CSS theme variables - Customization
A guide for customizing CSS theme variables in Material UI.
Theming
experimental_extendTheme
is an API that extends the default theme. It returns a theme that can only be used by the Experimental_CssVarsProvider
.
import {
Experimental_CssVarsProvider as CssVarsProvider,
experimental_extendTheme as extendTheme,
} from '@mui/material/styles';
const theme = extendTheme();
// ...custom theme
function App() {
return <CssVarsProvider theme={theme}>...</CssVarsProvider>;
}
Color schemes
The major difference from the default approach is in palette customization.
With the extendTheme
API, you can specify the palette for all color schemes at once (light
and dark
are built in) under the colorSchemes
node.
Here's an example of how to customize the primary
palette:
import { pink } from '@mui/material/colors';
const theme = extendTheme({
colorSchemes: {
light: {
palette: {
primary: {
main: pink[600],
},
},
},
dark: {
palette: {
primary: {
main: pink[400],
},
},
},
},
});
Components
Component customization remains the same as the default approach.
We recommend using the value from theme.vars.*
whenever possible for a better debugging experience:
const theme = extendTheme({
components: {
MuiChip: {
styleOverrides: {
root: ({ theme, ownerState }) => ({
...(ownerState.variant === 'outlined' &&
ownerState.color === 'primary' && {
// this is the same as writing:
// backgroundColor: 'var(--mui-palette-background-paper)',
backgroundColor: theme.vars.palette.background.paper,
}),
}),
},
},
},
});
Channel tokens
A channel token is a variable that consists of color space channels but without the alpha component. The value of a channel token is separated by a space, e.g. 12 223 31
, which can be combined with the color functions to create a translucent color.
The extendTheme()
automatically generates channel tokens that are likely to be used frequently from the theme palette. Those colors are suffixed with Channel
, for example:
const theme = extendTheme();
const light = theme.colorSchemes.light;
console.log(light.palette.primary.mainChannel); // '25 118 210'
// This token is generated from `theme.colorSchemes.light.palette.primary.main`.
You can use the channel tokens to create a translucent color like this:
const theme = extendTheme({
components: {
MuiChip: {
styleOverrides: {
root: ({ theme, ownerState }) => ({
...(ownerState.variant === 'outlined' &&
ownerState.color === 'primary' && {
backgroundColor: `rgba(${theme.vars.palette.primary.mainChannel} / 0.12)`,
}),
}),
},
},
},
});
Adding new theme tokens
You can add other key-value pairs to the theme input which will be generated as a part of the CSS theme variables:
const theme = extendTheme({
colorSchemes: {
light: {
palette: {
// The best part is that you can refer to the variables wherever you like 🤩
gradient:
'linear-gradient(to left, var(--mui-palette-primary-main), var(--mui-palette-primary-dark))',
border: {
subtle: 'var(--mui-palette-neutral-200)',
},
},
},
dark: {
palette: {
gradient:
'linear-gradient(to left, var(--mui-palette-primary-light), var(--mui-palette-primary-main))',
border: {
subtle: 'var(--mui-palette-neutral-600)',
},
},
},
},
});
function App() {
return <CssVarsProvider theme={theme}>...</CssVarsProvider>;
}
Then, you can access those variables from the theme.vars
object:
const Divider = styled('hr')(({ theme }) => ({
height: 1,
border: '1px solid',
borderColor: theme.vars.palette.border.subtile,
backgroundColor: theme.vars.palette.gradient,
}));
Or use var()
to refer to the CSS variable directly:
/* global.css */
.external-section {
background-color: var(--mui-palette-gradient);
}
TypeScript
You must augment the theme palette to avoid type errors:
declare module '@mui/material/styles' {
interface PaletteOptions {
gradient: string;
border: {
subtle: string;
};
}
interface Palette {
gradient: string;
border: {
subtle: string;
};
}
}
Changing variable prefixes
To change the default variable prefix (--mui
), provide a string to cssVarPrefix
property, as shown below:
const theme = extendTheme({ cssVarPrefix: 'any' });
// the stylesheet will be like this:
// --any-palette-primary-main: ...;
To remove the prefix, use an empty string as a value:
const theme = extendTheme({ cssVarPrefix: '' });
// the stylesheet will be like this:
// --palette-primary-main: ...;
Custom styles per mode
To customize the style without creating new tokens, you can use the theme.getColorSchemeSelector
utility:
const Button = styled('button')(({ theme }) => ({
// in default mode.
backgroundColor: theme.vars.palette.primary.main,
color: '#fff',
'&:hover': {
backgroundColor: theme.vars.palette.primary.dark,
},
// in dark mode.
[theme.getColorSchemeSelector('dark')]: {
backgroundColor: theme.vars.palette.primary.dark,
color: theme.vars.palette.primary.main,
'&:hover': {
color: '#fff',
backgroundColor: theme.vars.palette.primary.dark,
},
},
}));
Force a specific color scheme
Specify data-mui-color-scheme="dark"
to any DOM node to force the children components to appear as if they are in dark mode.
<div data-mui-color-scheme="dark">
<Paper sx={{ p: 2 }}>
<TextField label="Email" type="email" margin="normal" />
<TextField label="Password" type="password" margin="normal" />
<Button>Sign in</Button>
</Paper>
</div>
Dark color scheme application
For an application that only has a dark mode, set the default mode to dark
:
const theme = extendTheme({
// ...
});
// remove the `light` color scheme to optimize the HTML size for server-side application
delete theme.colorSchemes.light;
function App() {
return (
<CssVarsProvider theme={theme} defaultMode="dark">
...
</CssVarsProvider>
);
}
For a server-side application, provide the same value to getInitColorSchemeScript()
:
getInitColorSchemeScript({
defaultMode: 'dark',
});