Skip to content
+

CSS theme variables

An overview of adopting CSS theme variables in Material UI.

CSS variables are a modern cross-browser feature that let you declare variables in CSS and reuse them in other properties. You can implement them to improve Material UI's theming and customization experience.

Introduction

CSS theme variables replace raw values in Material UI components for a better developer experience because, in the browser dev tool, you will see which theme token is used as a value.

In addition with these variables, you can inject a theme into your app's stylesheet at build time to apply the user's selected settings before the whole app is rendered.

Advantages

  • It lets you prevent dark-mode SSR flickering.
  • You can create unlimited color schemes beyond light and dark.
  • It offers a better debugging experience not only for developers but also designers on your team.
  • The color scheme of your website is automatically synced between browser tabs.
  • It simplifies integration with third-party tools because CSS theme variables are available globally.
  • It reduces the need for a nested theme when you want to apply dark styles to a specific part of your application.

Trade-offs

For server-side applications, there are some trade-offs to consider:

Compare to the default method Reason
HTML size Bigger CSS variables are generated for both light and dark mode at build time.
First Contentful Paint (FCP) Longer Since the HTML size is bigger, the time to download the HTML before showing the content is a bit longer.
Time to Interactive (TTI) Shorter (for dark mode) Stylesheets are not regenerated between light and dark mode, a lot less time is spent running JavaScript code.

Mental model

Adopting CSS variables requires some shifts in your mental model of theming and customizing user-selected modes.

Colors

Default approach: Light and dark colors are created separately.

import { createTheme } from '@mui/material/styles';

const lightTheme = createTheme();

const darkTheme = createTheme({
  palette: {
    mode: 'dark',
  },
});

CSS theme variables: Light and dark colors are consolidated into a theme.

import { extendTheme } from '@mui/material/styles';

// `extendTheme` is a new API
const theme = extendTheme({
  colorSchemes: {
    light: { // palette for light mode
      palette: {...}
    },
    dark: { // palette for dark mode
      palette: {...}
    }
  }
})

Styling

Default approach: Usually relies on JavaScript to switch the value between modes:

createTheme({
  components: {
    MuiButton: {
      styleOverrides: {
        root: ({ theme }) => ({
          // use JavaScript conditional expression
          color: theme.palette.mode === 'dark' ? '#fff' : theme.palette.primary.main,
        }),
      },
    },
  },
});

CSS theme variables: Styling leans toward cascading and specificity by using the appropriate selector which lets you prevent dark-mode SSR flickering:

extendTheme({
  components: {
    MuiButton: {
      styleOverrides: {
        root: ({ theme }) => ({
          color: theme.vars.palette.primary.main,
          // When the mode switches to dark, the attribute selector is attached to
          // the <html> tag by default.
          '*:where([data-mui-color-scheme="dark"]) &': {
            color: '#fff',
          },
        }),
      },
    },
  },
});

What's next