Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate Material UI components to support theme CSS variables #32049

Closed
65 tasks done
mnajdova opened this issue Mar 30, 2022 · 41 comments
Closed
65 tasks done

Migrate Material UI components to support theme CSS variables #32049

mnajdova opened this issue Mar 30, 2022 · 41 comments
Labels
package: material-ui Specific to @mui/material umbrella For grouping multiple issues to provide a holistic view

Comments

@mnajdova
Copy link
Member

mnajdova commented Mar 30, 2022

We need your help!

Starting from 5.6.0 we are adding a new experimental API for supporting CSS variables in the Material UI components. You can see more details for this new feature on #27651 or our docs page.

The changes are not breaking so we can safely apply them in the components during the lifecycle of v5.

Initially, the idea was implemented in one component, the Button, but we would need this to be applied to all components. This issue will help track the progress of the migration.

Contribution guides

  1. Pick a component in the “Ready to be picked up” section
  2. Read the “Patterns” section
  3. Replace the use of these theme tokens to (theme.vars || theme).*.
    • theme.palette.*
    • theme.shape.borderRadius
    • theme.shadows
    • theme.zIndex
  4. Create a new page using the template at docs/pages/experiments/material-ui/css-vars-template.tsx. Ex. if you pick Accordion component, create the page (docs/pages/experiments/material-ui/accordion.tsx) and copy the template. Then, import the Accordion and render it on the page.
  5. Verify that it works
    1. Run yarn docs:dev
    2. go to URL /experiments/material-ui/{component}/ and check the component (it should look the same)
    3. Open the DevTool and toggle the dark mode button at the top of the page, the classNames of the component should not change.
  6. Open a PR and put the URL to the the end of the component below (so that everyone knows it has been taken)
  7. Tag @mui/core to review

If you come across any issue or are unsure of the changes, feel free to open a PR and describe the problem + tag @mui/core for help.

Patterns

Straightforward

- borderRadius: theme.shape.borderRadius
+ borderRadius: (theme.vars || theme).shape.borderRadius

- border: `1px solid ${theme.palette.action.disabledBackground}`,
+ border: `1px solid ${(theme.vars || theme).palette.action.disabledBackground}`,

color manipulation

Some components are using a conditional statement to switch values between modes which causes the flicker issue for SSR application. To fix this, we have provided channel colors to be combined with opacity (thanks to CSS variables, this seems to be the only solution so far).

The channel colors in the theme palettes are mainChannel, lightChannel, darkChannel and contrastTextChannel.

- backgroundColor: alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity),
+ backgroundColor: theme.vars ?
+   `rgba(${theme.vars.palette.primary.mainChannel} / ${theme.vars.palette.action.selectedOpacity})` :
+   alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity),

- backgroundColor: alpha(
-       theme.palette.primary.main,
-       theme.palette.action.selectedOpacity + theme.palette.action.focusOpacity,
-     ),
+ backgroundColor: theme.vars ?
+       `rgba(${theme.vars.palette.primary.mainChannel} / calc(${theme.vars.palette.action.selectedOpacity} + ${theme.vars.palette.action.focusOpacity}))` :
+       alpha(...)

List of components

Ready to be picked up

✅ First set is done!

Need clarification

These components contain hardcoded values between modes. We will put the guidelines for each of these components and moved them to "Ready to be picked up".

Proposal: #32049 (comment)

@mnajdova mnajdova added the status: waiting for maintainer These issues haven't been looked at yet by a maintainer label Mar 30, 2022
@danilo-leal danilo-leal added package: material-ui Specific to @mui/material and removed status: waiting for maintainer These issues haven't been looked at yet by a maintainer labels Mar 30, 2022
@siriwatknp siriwatknp changed the title Migrate all components to CSS variables Migrate all components to support theme CSS variables Apr 2, 2022
@siriwatknp siriwatknp changed the title Migrate all components to support theme CSS variables Migrate Material UI components to support theme CSS variables Apr 2, 2022
@danilo-leal danilo-leal added the umbrella For grouping multiple issues to provide a holistic view label Apr 4, 2022
@vicasas
Copy link
Member

vicasas commented Apr 28, 2022

Could we suppose that for components that don't use theme we could mark them as don't take? For example List component.

@siriwatknp
Copy link
Member

Could we suppose that for components that don't use theme we could mark them as don't take? For example List component.

I have updated the list.

@ZeeshanTamboli
Copy link
Member

Replace the use of these theme tokens to (theme.vars || theme)..
theme.palette.

theme.shape.borderRadius
theme.typography.fontFamily
theme.typography.fontSize
theme.typography.fontWeightLight
theme.typography.fontWeightRegular
theme.typography.fontWeightMedium
theme.typography.fontWeightBold
theme.typography.htmlFontSize
theme.shadows
theme.zIndex

Correct me if I am wrong, but I think theme.typography should not be updated as per the following theme structure for CSS
variables:

import { Theme as MuiTheme } from '@mui/material/styles';

declare module '@mui/material/styles' {
  interface Theme {
    vars: Omit<
      MuiTheme,
      'typography' | 'mixins' | 'breakpoints' | 'direction' | 'transitions'
    >;
  }
}

@siriwatknp
Copy link
Member

siriwatknp commented Apr 29, 2022

@ZeeshanTamboli

You are right. Even though, I think some part of the typography is nice to have to be generated as CSS variables but let's do thing one step at a time and neglect the typography for now. I have updated the issue decription.

@haneenmahd
Copy link
Contributor

I don't think AlertTitle component has anything to change as of now. It only uses typography from the theme palette.
But It is neglected as of now. What should I do?

@alippai
Copy link

alippai commented May 25, 2022

It might be early to ask, but do you plan to keep both solutions, add this as an alternative only or will you replace the current provider with the new one eventually?

@siriwatknp
Copy link
Member

do you plan to keep both solutions

Absolutely, yes. The CSS variables support is considered a new feature that developers can opt-in. We don't plan to replace the current one at this point.

@siriwatknp
Copy link
Member

siriwatknp commented Jun 17, 2022

👋 Progress Updates

Migration

The migration to support CSS variables in all components is at the finishing line. All issues have been resolved and we can fix all the problems that cause dark mode flicker when you adopt the new provider.

Typescript

We will provide a module augmentation file similar to the lab to augment the theme to have access to the CSS variables. You just need one line in your project.

import type {} from '@mui/material/themeCssVarsAugmentation';

// You will be able to see `theme.vars` in all of the APIs eg. styled, sx, useTheme

This should work as well if you have custom module augmentation.

Docs

After the migration and the types are done, we will start working on the docs about how to work with CSS variables. There are mental model changes that are different but we believe that it is the right way to go

For example:

// current
color: theme.palette.mode === 'dark' ? theme.palette.primary.main : theme.palette.text.primary,

// With CSS variables
color: theme.palette.primary.main,
[theme.getColorSchemeSelector('dark')]: {
  color: theme.palette.text.primary,
}

Using JS condition to switch between colors at runtime will not scale:

  • causes dark mode flickers (or flashing)
  • will be complicated if you have more than light and dark color schemes.
  • for a very big application, it is not performant because all the styles are recalculated when the mode changes.

Blog post

We will definitely share insights and overviews about what it means for Material UI to adopt CSS variables!

@ceopaludetto
Copy link

I've been testing this new provider, works great, but I have a doubt. I wanna use prefers-color-scheme media query instead of script-managed value injected in html, how can I do that?

After reading createCssVarsProvider code, I think we can leave a option like tailwind does(https://tailwindcss.com/docs/dark-mode#toggling-dark-mode-manually), I propose something like attributeManagedStyles: boolean or mediaManagedStyles: boolean. If user toggle on, we add the global style like this:

:root {
    --md-some-color: ;
}

@media (prefers-color-scheme: dark) {
    :root {
        --md-some-color: ;
    }
}

And we change getColorSchemeSelector util to:

(target: 'dark' | 'light') => `(prefers-color-scheme: ${target})`

What do you think?

@siriwatknp
Copy link
Member

@ceopaludetto Thanks for the feedback. Can you explain a bit more about your use case? Providing a minimal CodeSandbox would be great!

@ceopaludetto
Copy link

ceopaludetto commented Jun 21, 2022

@ceopaludetto Thanks for the feedback. Can you explain a bit more about your use case? Providing a minimal CodeSandbox would be great!

@siriwatknp
So, my idea is to provide a prop in CssVarsProvider which changes the css var injection in :root. If user chooses "data-attribute", the injection will be:

:root {
  --md-some-color: 255 255 255;
}

[data-theme="dark"] {
    :root {
        --md-some-color: 0 0 0;
    }
}

But if he chooses "media", the injection will be:

:root {
  --md-some-color: 255 255 255;
}

@media(prefers-color-scheme: dark) {
    :root {
        --md-some-color: 0 0 0;
    }
}

In second option, user loss the ability to dynamic change the theme based on preference inside the application, but he can use without calling getInitColorSchemeScript and website color will always match system preference

I've created two sandboxes, one with data-attribute behavior and another with media behavior, I've used emotion for example purposes, but I tried to replicate CssVarsProvider actual implementation.

data-attribute: https://codesandbox.io/s/emotion-data-attribute-rw288w
media: https://codesandbox.io/s/emotion-data-media-qw25y3

@siriwatknp
Copy link
Member

siriwatknp commented Jun 22, 2022

But if he chooses "media", the injection will be:

@ceopaludetto Got it, yeah it is not possible at this point. Would you mind creating a new issue to discuss further?

@ceopaludetto
Copy link

But if he chooses "media", the injection will be:

@ceopaludetto Got it, yeah it is not possible at this point. Would you mind creating a new issue to discuss further?

Of course not, thanks for listening, i'll open later

@TimoWilhelm
Copy link
Contributor

TimoWilhelm commented Jun 24, 2022

@siriwatknp This look great! I think there might be an issue with the SnackbarContent.

https://github.com/mui/material-ui/pull/32835/files#diff-c69c0067f63247670034da3fc1b223b7603fc9cee0545d1ce2cf1521e58fbebbR33-R35

The content color will always be primary text color, which will have poor contrast with the snackbar background color.

I think it should be something like color: theme.vars.palette[theme.vars.palette.SnackbarContent.bg].contrastTextChannel, but I don't think there is an contrastTextChannel for --mui-palette-SnackbarContent-bg yet.

@Rafcin
Copy link

Rafcin commented Jun 25, 2022

Even though Experimental_CssVarsProvider says experimental, is it stable enough to use? I've been playing around with it in sandboxes and it's hands down my new favorite feature.

Also a question regarding the vars provider. Suppose we have a theme object, until this point had my own version of useColorScheme and my theme object had my palettes split into dark and light. With the experimental vars, should the theme now be changed to use colorSchemes: { light: { palette {...} }, dark: { palette: {...} } } or do we still need to keep palette in the root of the theme?

@siriwatknp
Copy link
Member

siriwatknp commented Jul 4, 2022

Even though Experimental_CssVarsProvider says experimental, is it stable enough to use? I've been playing around with it in sandboxes and it's hands down my new favorite feature.

Good to hear that! I believe that the CssVarsProvider is quite stable and there is no major change that I can think of.

With the experimental vars, should the theme now be changed to use colorSchemes

Yes, colorSchemes must be used with CssVarsProvider. (A pile of documentation will be added soon)

Before:

const theme = createTheme({ palette: { mode: userSelectedMode } });

<ThemeProvider theme={theme}>

After:

const theme = extendTheme({
  colorSchemes: {
    light: { palette: { ... } },
    dark: { palette: { ... } },
  }
});

<CssVarsProvider theme={theme}>

Note: CssVarsProvider is built on top of ThemeProvider with the feature that generates CSS variables from the theme.

I think this diagram describes it really well (more details in #27651).
Screen Shot 2565-07-04 at 08 13 39

@maks-yaremishyn
Copy link

Hey, @TimoWilhelm and mui team! We consume mui library and have rather straightforward use of snackbar component

      <Snackbar
        open={!!successMessage}
        onClose={...}
        autoHideDuration={5000}
        anchorOrigin={{
          vertical: 'bottom',
          horizontal: 'left',
        }}
      >
        <Alert severity="success">
          {successMessage}
        </Alert>
      </Snackbar>

after dependencies update unexpectedly text or success messages changed from white to black. is such side effect possible as result of this PR ?

@TimoWilhelm
Copy link
Contributor

@maks-yaremishyn The Alert component color should not be affected by the Snackbar variables. For me, it's using the correct color (rgb(30, 70, 32) in light mode) for the standard variant inside a Snackbar. Do you have images for before and after?

image
image

@maks-yaremishyn
Copy link

image

image

@TimoWilhelm
Copy link
Contributor

This looks like you have custom styling applied to your Alert.

To answer your question: No, the PR should not affect the Alert component since it has it's own styling properties by default. If you override the styles to inherit the color property from the Snackbar, then I guess it could happen.

@mwskwong
Copy link
Contributor

mwskwong commented Jul 22, 2022

A few questions, if anyone is kind enough to answer these

  1. Is this new CSS variable API intended to replace the existing theme API eventually?
  2. Let's say I just need light mode. By omitting the dark key in experimental_extendTheme, will that prevent the default dark mode styles from being bundled into the PROD build?

@siriwatknp
Copy link
Member

siriwatknp commented Jul 25, 2022

@mwskwong Great questions!

  1. Is this new CSS variable API intended to replace the existing theme API eventually?

For Material UI, NO. We consider CSS variables support as an opt-in feature on top of the default API.

  1. Let's say I just need light mode. By omitting the dark key in experimental_extendTheme, will that prevent the default dark mode styles from being bundled into the PROD build?

No, omitting the key does not remove the default dark mode styles (that's why it is the default styles). The workaround is to remove the dark key from the theme object directly before passing it to CssVarsProvider.

const theme = extendTheme();

delete theme.colorSchemes.dark;

// CssVarsProvider will generate CSS variables based on the object in `theme.colorSchemes`.
<CssVarsProvider theme={theme}>

Proof that it works: https://codesandbox.io/s/cssvariablescustomization-demo-material-ui-forked-qfy955?file=/demo.js

I will put this in the docs. #33417

@siriwatknp siriwatknp unpinned this issue Aug 1, 2022
@mwskwong
Copy link
Contributor

mwskwong commented Aug 17, 2022

I noticed the migration task doesn't include lab components. Is that intended to be included some time in the future?

@siriwatknp
Copy link
Member

siriwatknp commented Aug 17, 2022

I noticed the migration task doesn't include lab components. Is that intended to be included some time in the future?

Sure! I have created an umbrella issue to keep track of it. The contribution from the community is welcome as well.

@ZeeshanTamboli
Copy link
Member

I noticed the migration task doesn't include lab components. Is that intended to be included some time in the future?

@mwskwong We have added support for CSS variables in lab components. It should be available in the next release.

@elyobo
Copy link

elyobo commented Aug 15, 2024

Maybe a dumb question, but is there some reason not to just mutate the theme such that theme.vars always exists with the regular theme values so that theme consumers don't have to always and everywhere do (theme.vars || them).whatever? Seems tedious and easy to miss.

e.g. something slightly more smart but along the lines of

theme.vars = theme.vars || theme // ideally pick out only the right stuff here, but this is essentially what the || does elsewhere

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
package: material-ui Specific to @mui/material umbrella For grouping multiple issues to provide a holistic view
Projects
None yet
Development

No branches or pull requests