You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Dark theme flash on SSR (server-side rendering)
related issues: #15588 #21371. One obvious example is material-ui documentation. If you toggle dark theme and press hard refresh, you will see a flash of light mode before it turns dark.
Bad debugging experience
the value in dev tool is the color code ex. #f5f5f5 which is hard to know which design token the component is using. This gives bad experience to both developer (working locally) and non-developer (checking the color in production website)
What is the variable that the component is using? no idea unless look at the code.
Performance
the current implementation consider light & dark as a separate theme which means by toggling to another mode will cause component in the tree to rerender. (However, the impact is not significant ~100-300ms depends on how large the page is)
Solution
CSS variables is the main ingredient to solve the above issues. Another important ingredient is to apply all the themes at once. How?
Case I: JS is disabled on the client
This could happen when user intentionally disable JS or internet connection is bad and could load only the HTML and CSS.
This is a very first crucial step to have perfect dark mode because if we can find a solution within this constraint, then we can use javascript to add more feature on top of it.
Since we cannot use JS to calculate stuff, everything should be ready on the server. The CSS should look like this.
// variable names and `:root` is for demonstration purpose
:root {
--bg:#fff;
--text:#000;
}
@media (prefers-color-scheme: dark) {
:root {
--bg:#000;
--text:#fff;
}
}
If user does not set prefers scheme, they will see the site on light mode. But with prefer scheme dark enabled, the @media (prefers-color-scheme: dark) will have more specificity and override the root.
Components implementation should use the variables that are agnostic of schemes, meaning the variables should be the same across light & dark mode.
User will be able to select the theme they like, ex. lightdark or system and the site will remember the user's preference.
To achieve this, an extra script need to be run on the client before the main react script to check selected theme in local storage. If theme (string) exists in local storage, it will apply the class, or attribute to the html or body. The snippet looks something like
The generated CSS at build time should contains .mui-dark specificity, otherwise it will not work.
// generated at build time
// variable names and `:root` is for demonstration purpose
:root {
--bg:#fff;
--text:#000;
}
@media (prefers-color-scheme: dark) {
:root {
--bg:#000;
--text:#fff;
}
}
body.mui-dark {
--bg:#fff;
--text:#000;
}
Benefits
CSS Variables open many doors for improving DX and design opportunities.
fix flash dark mode on SSR (with the correct implementation)
multiple themes not only light & dark (ex, trueBlackcomfort)
contextual styles without using javascript (<ThemeProvider theme={darkTheme}>
// the naming is for demonstration purpose
...
<divmui-mode="dark"><Button></Button><Card>...</Card></div>
the content inside <div mui-mode="dark"> will behave as dark mode regardless of user's preference.
better debugging experience (anyone in the teams including non-devs can see the design tokens directly in the website without looking at the code.
improve performance, the toggle between themes still require javascript but does not cause the whole application to rerender.
Implementation
The challenge of css variables support is the integration with both the 2nd design system we are working on and the existing material-design components (aka @material-ui/core at the time of writing). So to make it easier to maintain and able work along side with other product, the css variables support should be a dedicated module which has only a few functionalities such as
turn theme object in React context into css variables
provide the context and function that switch between theme
provide APIs that determine theme for SSR (the script that need to be placed at the top of <body>
As a result, each design system can use the module to implement on its own.
Plan
create an unstable_api in system (could be moved to its own package later).
2nd design system starts to consume the apis.
@material-ui/core (after the module is settled)
improve the colors palette without breaking change, hopefully (in order to remove runtime calculation such as alphalighten, ...)
expose another Provider that consume CSS variables module
convert components to use cssVars (names might be changed)
// ButtonconstButton=styled('button')(({theme: { cssVars, ...rest}})=>{consttheme=cssVars||rest;// the implementation would not change much, and allow developer to migrate smoothly.return{borderRadius: theme.shape.borderRadius,
...
}})
Problems
Dark theme flash on SSR (server-side rendering)
related issues: #15588 #21371. One obvious example is material-ui documentation. If you toggle dark theme and press hard refresh, you will see a flash of light mode before it turns dark.
Bad debugging experience
the value in dev tool is the color code ex.
#f5f5f5
which is hard to know which design token the component is using. This gives bad experience to both developer (working locally) and non-developer (checking the color in production website)Performance
the current implementation consider light & dark as a separate theme which means by toggling to another mode will cause component in the tree to rerender. (However, the impact is not significant ~100-300ms depends on how large the page is)
Solution
CSS variables is the main ingredient to solve the above issues. Another important ingredient is to apply all the themes at once. How?
Case I: JS is disabled on the client
Since we cannot use JS to calculate stuff, everything should be ready on the server. The CSS should look like this.
If user does not set prefers scheme, they will see the site on light mode. But with prefer scheme dark enabled, the
@media (prefers-color-scheme: dark)
will have more specificity and override the root.Case II: JS available on the client
User will be able to select the theme they like, ex.
light
dark
orsystem
and the site will remember the user's preference.To achieve this, an extra script need to be run on the client before the main react script to check selected theme in local storage. If theme (string) exists in local storage, it will apply the class, or attribute to the
html
orbody
. The snippet looks something likeThe generated CSS at build time should contains
.mui-dark
specificity, otherwise it will not work.Benefits
CSS Variables open many doors for improving DX and design opportunities.
trueBlack
comfort
)<ThemeProvider theme={darkTheme}>
<div mui-mode="dark">
will behave as dark mode regardless of user's preference.Implementation
The challenge of css variables support is the integration with both the 2nd design system we are working on and the existing material-design components (aka
@material-ui/core
at the time of writing). So to make it easier to maintain and able work along side with other product, the css variables support should be a dedicated module which has only a few functionalities such as<body>
As a result, each design system can use the module to implement on its own.
Plan
system
(could be moved to its own package later).@material-ui/core
(after the module is settled)alpha
lighten
, ...)cssVars
(names might be changed)Resources & Inspiration
The text was updated successfully, but these errors were encountered: