question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Migrate all website pages to use CSS theme variables

See original GitHub issue

We need your help!

Help us migrate the website to start using CSS theme variables!

Progress

If you are interested to contribute, check out the How-to and add your name at the end of the bullet to let us know that you are working on it.

Once you have done the migration, open a PR with the title [website] Migrate xxx page to use CSS theme variables

where “xxx” is the page that you pick.

How-to

  1. go to the page (in docs/pages/ folder) that you want to work on. Let’s take docs/pages/templates.tsx as an example.

  2. Replace BrandingProvider with BrandingCssVarsProvider

    -import BrandingProvider from 'docs/src/BrandingProvider';
    +import BrandingCssVarsProvider from 'docs/src/BrandingCssVarsProvider';
    
    -<BrandingProvider>
    +<BrandingCssVarsProvider>
    

    Start the development server via yarn docs:dev and goto /templates/, you are likely to encounter Warning: Prop "className" did not match. Server: or something similar. This is because some components in the page is relying on the runtime calculation to change the styles based on the color mode. Those conditions must be fixed as explained in the next step!

  3. Look at each component imported on the page, e.g. TemplateHero.tsx, and find the conditional expression like this:

    <Typography
      ...
      color={(theme) => (theme.palette.mode === 'dark' ? 'primary.400' : 'primary.600')}
    >
    

    Then, replace the condition with theme.applyDarkStyles():

    -color={(theme) => (theme.palette.mode === 'dark' ? 'primary.400' : 'primary.600')}
    +sx={theme => ({
    +  color: 'primary.600',
    +  ...theme.applyDarkStyles({
    +    color: 'primary.400',
    +  }),
    +})}
    
    • Check out migration scenarios to see other patterns. If you encounter a scenario that is not in the migration patterns, feel free to open a PR and ask for help.
    • Read the implementation details about the theme.applyDarkStyles.
  4. Refresh the page, you should not see a warning coming from the TemplateHero.tsx (you might still see the warning but coming from other components on the page).

  5. Once you have fixed all the components, open a PR with the title [website] Migrate xxx page to use CSS theme variables and tag @siriwatknp as a reviewer.

Migration patterns

Here are some use cases that you might encounter in the migration process.

1. Conditional expression in the prop that’s not sx

Example code:

<Typography color={theme => theme.palette.mode === 'dark' : 'grey.200' : 'grey.700'}>

Migrated code: Move the logic to sx prop:

<Typography
  sx={theme => ({
    color: 'grey.700',
    ...theme.applyDarkStyles({
       color: 'grey.200',
    })
  }))
>

2. Usage from theme.palette.*

Example code:

<Typography color={theme => theme.palette.mode === 'dark' : theme.palette.grey[200] : theme.palette.grey[700]}>

Migrated code: attach (theme.vars || theme).*:

<Typography
  sx={theme => ({
    color: (theme.vars || theme).palette.grey[700],
    ...theme.applyDarkStyles({
       color: (theme.vars || theme).palette.grey[200],
    })
  }))
>

3. Conditional expression in sx prop

Example code:

<Typography
  sx={{
    color: theme => theme.palette.mode === 'dark' : 'grey.200' : 'grey.700',
    bgcolor: theme => theme.palette.mode === 'dark' : 'grey.500' : 'grey.600',
  }}
>

Migrated code: Remove the condition and group into dark styles:

<Typography
  sx={theme => ({
    color: 'grey.700',
    bgcolor: 'grey.600',
    // `theme.applyDarkStyles()` must come later
    ...theme.applyDarkStyles({
      color: 'grey.200',
      bgcolor: 'grey.500',
    })
  })}
>

4. Conditional expression in sx prop (with nested selectors)

Example code:

const Example = styled('a')(({ theme }) => ({
  color: theme.palette.primary[700],
  '&::before': {
    width: 2,
    height: 2,
    backgroundColor: theme.palette.mode === 'dark' ? theme.palette.primary[600] : theme.palette.background.paper,
  },
  '& > span': {
    color theme.palette.mode === 'dark' ? theme.palette.primary[400] : theme.palette.primary[500],
  },
}))

Migrated code: use array:

const Example = styled('a')(
({ theme }) => ([{
  color: theme.palette.primary[700],
  '&::before': {
    width: 2,
    height: 2,
    backgroundColor: (theme.vars || theme).palette.background.paper,
  },
  '& > span': {
    color: (theme.vars || theme).palette.primary[500],
  },
},
theme => theme.applyDarkStyles({
  '&::before': {
    backgroundColor: (theme.vars || theme).palette.primary[600],
  },
  '& > span': {
    color: (theme.vars || theme).palette.primary[400],
  }
})]))

5. img switch between color modes

From StoreTemplatesBanner.tsx.

Example code:

const globalTheme = useTheme();
  const mode = globalTheme.palette.mode;
  return (
    <Image
      ref={ref}
      src={`/static/branding/store-templates/template-${mode}${Object.keys(linkMapping).indexOf(brand) + 1}.jpeg`}
      alt=""
      {...props}
    />
  );

Migrated code: use CSS content: url(...):

<Image
  ref={ref}
  src={`/static/branding/store-templates/template-light${Object.keys(linkMapping).indexOf(brand) + 1}.jpeg`}
  alt=""
  sx={(theme) =>
    theme.applyDarkStyles({
      content: `url(/static/branding/store-templates/template-dark${Object.keys(linkMapping).indexOf(brand) + 1}.jpeg)`,
    })
  }
  {...props}
/>

6. Section wrapped with <ThemeProvider theme={darkTheme}>

Remove the ThemeProvider and add data-mui-color-scheme="dark" to the next div instead.

diff --git a/docs/pages/careers.tsx b/docs/pages/careers.tsx
index b06294da66..1ae0709c22 100644
--- a/docs/pages/careers.tsx
+++ b/docs/pages/careers.tsx
@@ -476,8 +476,7 @@ function CareersContent() {
       </Container>
       {/* Next roles */}
       {nextRolesData.length > 0 ? (
-        <ThemeProvider theme={brandingDarkTheme}>
-          <Box sx={{ bgcolor: 'primaryDark.700' }}>
+          <Box data-mui-color-scheme="dark" sx={{ bgcolor: 'primaryDark.700' }}>
             <Container sx={{ py: { xs: 4, md: 8 } }}>
               <Box
                 sx={{
@@ -531,7 +530,6 @@ function CareersContent() {
               </Stack>
             </Container>
           </Box>
-        </ThemeProvider>
       ) : null}
       {/* Frequently asked questions */}
       <Container sx={{ py: { xs: 4, sm: 6, md: 8 } }}>

Implementation details

  • We want to migrate the website page by page, so we create a new provider BrandingCssVarsProvider.tsx as a replacement for the existing BrandingProvider. It is built on top of the public CssVarsProvider API specific for mui.com which contains custom theme tokens. Once a page is wrapped with the new provider, the components under it will start using CSS theme variables immediately which causes server-client inconsistency due to conditional expressions like this theme.palette.mode === 'dark' ? theme.palette.grey[300] : theme.palette.grey[700]

  • The applyDarkStyles() util is added to the theme (this is specific to mui.com, not Material UI) to ease the migration because some components need to be backward compatible for some pages that haven’t been migrated. This util is straightforward as the name implies. It will decide the approach to use based on the theme, so you don’t have to know if the components are rendered in which provider. All you need to do is put the styles for dark mode to the util after the default styles. if the component is under the new BrandingCssVarsProvider:

    theme.applyDarkStyles({
      color: (theme.vars || theme).palette.primary.main,
    })
    
    // it returns
    :where([data-mui-color-scheme="dark"]) & {
      color: var(--muidocs-palette-primary-main);
    }
    

    if the component is under the old BrandingProvider:

     theme.applyDarkStyles({
       color: (theme.vars || theme).palette.primary.main,
     })
     
     // it returns
     color: #ff0dc;
    
  • We have added a workaround to make the :where() selector works.

Issue Analytics

  • State:closed
  • Created a year ago
  • Reactions:2
  • Comments:16 (16 by maintainers)

github_iconTop GitHub Comments

2reactions
siriwatknpcommented, Dec 19, 2022

Thanks, everyone for the help!

1reaction
brianlu2610commented, Oct 26, 2022

@siriwatknp I would like to contribute too. Can you assign me the “About” page?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Developing Websites with Multiple Themes using CSS Variables
CSS variables is the easiest way to create a multi-theme app. ... Assuming you have already built your webpage without using CSS variables....
Read more >
Dynamic theme with CSS Variables - Medium
Customize the theme with CSS variables​​ Enable theming with CSS variables is extremely simple and straightforward, with the following steps: ...
Read more >
How to create better themes with CSS variables
Here we're defining a variable we'll call --primaryColor in three different places.
Read more >
Migrating to CSS theme variables - Material UI - MUI
If you have a custom theme, you must replace createTheme() with the extendTheme() API. This moves palette customization to within the colorSchemes node....
Read more >
Using CSS custom properties (variables) - MDN Web Docs
Complex websites have very large amounts of CSS, often with a lot of repeated values. For example, the same color might be used...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found