UNPKG

@mui/material

Version:

Material UI is an open-source React component library that implements Google's Material Design. It's comprehensive and can be used in production out of the box.

395 lines (314 loc) • 10 kB
# CSS theme variables - Configuration A guide for configuring CSS theme variables in Material UI. ## Customizing variable prefix To change the default variable prefix (`--mui`), provide a string to `cssVarPrefix` property, as shown below: ```js createTheme({ cssVariables: { cssVarPrefix: 'any' } }); // generated stylesheet: // --any-palette-primary-main: ...; ``` To remove the prefix, use an empty string as a value: ```js createTheme({ cssVariables: { cssVarPrefix: '' } }); // generated stylesheet: // --palette-primary-main: ...; ``` ## Toggling dark mode manually To toggle between modes manually, set the `colorSchemeSelector` with one of the following selectors: - `class`: adds a class to the `<html>` element. ```js class createTheme({ colorSchemes: { light: true, dark: true }, cssVariables: { colorSchemeSelector: 'class' } }); // CSS Result .light { ... } .dark { ... } ``` - `data`: adds a data attribute to the `<html>` element. ```js data createTheme({ colorSchemes: { light: true, dark: true }, cssVariables: { colorSchemeSelector: 'data' } }); // CSS Result [data-light] { ... } [data-dark] { ... } ``` - `string`: adds a custom selector to the `<html>` element. ```js string // The value must start with dot (.) for class or square brackets ([]) for data createTheme({ colorSchemes: { light: true, dark: true }, cssVariables: { colorSchemeSelector: '.theme-%s' } }); // CSS Result .theme-light { ... } .theme-dark { ... } ``` Then, use `useColorScheme` hook to switch between modes: ```jsx import { useColorScheme } from '@mui/material/styles'; function ModeSwitcher() { const { mode, setMode } = useColorScheme(); if (!mode) { return null; } return ( <select value={mode} onChange={(event) => { setMode(event.target.value); // For TypeScript, cast `event.target.value as 'light' | 'dark' | 'system'`: }} > <option value="system">System</option> <option value="light">Light</option> <option value="dark">Dark</option> </select> ); } ``` :::success After React hydrates the tree, the mode is set to `system` to follow the user's preference. ::: ### Determining the system mode To determine if the system mode is `light` or `dark`, use the `systemMode` property: ```js const { mode, systemMode } = useColorScheme(); console.log(mode); // 'system' console.log(systemMode); // 'light' | 'dark' ``` However, if the mode is **not** `system`, the `systemMode` will be `undefined`. ```js const { mode, systemMode } = useColorScheme(); console.log(mode); // 'light' | 'dark' console.log(systemMode); // undefined ``` ### Preventing SSR flickering For SSR (server-side rendering) applications, Material UI cannot detect the user-selected mode on the server, causing the screen to flicker from light to dark during the hydration phase on the client. To prevent the issue, you need to ensure that there is no usage of `theme.palette.mode === 'dark'` in your code base. If you have such a condition, replace it with the [`theme.applyStyles()` function](/material-ui/customization/dark-mode/#styling-in-dark-mode): ```diff import Card from '@mui/material/Card'; function App() { return ( <Card - sx={(theme) => ({ - backgroundColor: theme.palette.mode === 'dark' ? '#000' : '#fff', - '&:hover': { - backgroundColor: theme.palette.mode === 'dark' ? '#333' : '#f5f5f5', - }, - })} + sx={[ + { + backgroundColor: '#fff', + '&:hover': { + backgroundColor: '#f5f5f5', + }, + }, + (theme) => + theme.applyStyles('dark', { + backgroundColor: '#000', + '&:hover': { + backgroundColor: '#333', + }, + }), + ]} /> ); } ``` Next, if you have a custom selector that is **not** `media`, add the [`InitColorSchemeScript`](/material-ui/react-init-color-scheme-script/) component based on the framework that you are using: :::success The `attribute` has to be the same as the one you set in the `colorSchemeSelector` property: ```js createTheme({ cssVariables: { colorSchemeSelector: 'class' } }) <InitColorSchemeScript attribute="class" /> ``` ::: ### Next.js App Router Add the following code to the [root layout](https://nextjs.org/docs/app/api-reference/file-conventions/layout#root-layouts) file: ```jsx title="app/layout.js" import InitColorSchemeScript from '@mui/material/InitColorSchemeScript'; export default function RootLayout(props) { return ( <html lang="en" suppressHydrationWarning> <body> {/* must come before the <main> element */} <InitColorSchemeScript attribute="class" /> <main>{children}</main> </body> </html> ); } ``` :::warning If you don't add `suppressHydrationWarning` to your `<html>` tag, you will see warnings about `"Extra attributes from the server"` because `InitColorSchemeScript` updates that element. ::: ### Next.js Pages Router Add the following code to the custom [`pages/_document.js`](https://nextjs.org/docs/pages/building-your-application/routing/custom-document) file: ```jsx title="pages/_document.js" import Document, { Html, Head, Main, NextScript } from 'next/document'; import InitColorSchemeScript from '@mui/material/InitColorSchemeScript'; export default class MyDocument extends Document { render() { return ( <Html> <Head>...</Head> <body> {/* must come before the <Main> element */} <InitColorSchemeScript attribute="class" /> <Main /> <NextScript /> </body> </Html> ); } } ``` ### Gatsby Place the script in your [`gatsby-ssr.js`](https://www.gatsbyjs.com/docs/reference/config-files/gatsby-ssr/) file: ```jsx import * as React from 'react'; import InitColorSchemeScript from '@mui/material/InitColorSchemeScript'; export function onRenderBody({ setPreBodyComponents }) { setPreBodyComponents([<InitColorSchemeScript attribute="class" />]); } ``` ## Forcing a specific color scheme To force a specific color scheme for some part of your application, set the selector to the component or HTML element directly. In the example below, all the components inside the `div` will always be dark: <codeblock> ```js class // if the selector is '.mode-%s' <div className=".mode-dark"> <Paper sx={{ p: 2 }}> <TextField label="Email" type="email" margin="normal" /> <TextField label="Password" type="password" margin="normal" /> <Button>Sign in</Button> </Paper> {/* other components */} </div> ``` ```js data-attribute // if the selector is '[data-mode-%s]' <div data-mode-dark> <Paper sx={{ p: 2 }}> <TextField label="Email" type="email" margin="normal" /> <TextField label="Password" type="password" margin="normal" /> <Button>Sign in</Button> </Paper> {/* other components */} </div> ``` </codeblock> ## Disabling CSS color scheme By default, `createTheme()` attaches a [CSS `color-scheme` property](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/color-scheme) based on the palette mode. You can disable this by setting `disableCssColorScheme` to `true`: ```js createTheme({ cssVariables: { disableCssColorScheme: true }, }); ``` The generated CSS will not include the `color-scheme` property: ```diff @media (prefers-color-scheme: dark) { :root { - color-scheme: dark; --mui-palette-primary-main: #90caf9; ... } } ``` ## Instant transition between color schemes To disable CSS transitions when switching between modes, apply the `disableTransitionOnChange` prop: ```js <ThemeProvider disableTransitionOnChange /> ``` ```tsx import * as React from 'react'; import { createTheme, ThemeProvider, useColorScheme } from '@mui/material/styles'; import Stack from '@mui/material/Stack'; import MenuItem from '@mui/material/MenuItem'; import Switch from '@mui/material/Switch'; import Select from '@mui/material/Select'; import FormControlLabel from '@mui/material/FormControlLabel'; const theme = createTheme({ cssVariables: { colorSchemeSelector: '.demo-disable-transition-%s', }, colorSchemes: { dark: true }, }); function ModeSwitcher() { const { mode, setMode } = useColorScheme(); if (!mode) { return null; } return ( <Select value={mode} onChange={(event) => setMode(event.target.value as 'system' | 'light' | 'dark') } sx={{ minWidth: 120 }} > <MenuItem value="system">System</MenuItem> <MenuItem value="light">Light</MenuItem> <MenuItem value="dark">Dark</MenuItem> </Select> ); } export default function DisableTransitionOnChange() { const [disableTransition, setDisableTransition] = React.useState(false); return ( <ThemeProvider theme={theme} disableNestedContext disableTransitionOnChange={disableTransition} > <Stack sx={{ width: '100%', borderRadius: '4px', p: 2, gap: 2, display: 'flex', justifyContent: 'center', alignItems: 'center', bgcolor: 'background.default', color: 'text.primary', transition: '1s', }} > <ModeSwitcher /> <FormControlLabel control={ <Switch checked={disableTransition} onChange={(event) => setDisableTransition(event.target.checked)} /> } label="Disable transition" /> </Stack> </ThemeProvider> ); } ``` ## Force theme recalculation between modes By default, the `ThemeProvider` does not re-render when switching between light and dark modes when `cssVariables: true` is set in the theme. If you want to opt-out from this behavior, use the `forceThemeRerender` prop in the ThemeProvider: ```js <ThemeProvider forceThemeRerender /> ```