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

SX and Styled can't override applyStyles css #44488

Open
RickyRoller opened this issue Nov 20, 2024 · 4 comments
Open

SX and Styled can't override applyStyles css #44488

RickyRoller opened this issue Nov 20, 2024 · 4 comments
Assignees
Labels
customization: theme Centered around the theming features docs Improvements or additions to the documentation enhancement This is not a bug, nor a new feature

Comments

@RickyRoller
Copy link

RickyRoller commented Nov 20, 2024

Steps to reproduce

Repro link

Steps:

  1. Setup theme to use css variables
  2. Use theme.applyStyles to apply dark theme styles to component
  3. Sx and Styled components can't overwrite the dark theme styles

Current behavior

Sx and Styled theme behavior breaks with this new paradigm. They can't override the styles if the base component is using the new applyStyles helper since it creates a css selector with higher specificity

Expected behavior

Sx and Styled should be able to override styles like they used to with theme.palette.mode conditional logic

Context

There are multiple issues with the new css variables system.

  1. Sx and Styled can't override styles that use theme.applyStyles due to specificty
  2. theme.applyStyles doesn't support template literal syntax so the css selector has to either be extracted to a var or hardcoded
  3. Docs are inconsistent on recommendations. Docs say to use spread syntax on theme.applyStyles, then the actual code files say to not use spread syntax. ie. Docs, Code
  4. applyStyles isn't a valid replacement for controlling the theme of specific components. If you only specify dark theme styles with applyStyles, then adding .mode-light to a component won't apply those styles since .mode-dark has higher specificity. Refer to link for example

The biggest issue is the breaking change with sx/styled behavior

/**
 * All 3 buttons should be green (#bada55)
 */

/**
 * Light theme should be red
 * Dark theme should be blue
 */

const BaseButton = styled(Button)(({ theme }) => ({
  background: 'red',
  color: '#fff',
  ...theme.applyStyles('dark', {
    background: 'steelblue',
    color: '#fff',
  }),
}));

/**
 * Styled can't override dark theme either
 */

const StyledButton = styled(BaseButton)`
  background: #bada55;
`;

/**
 * No support for applyStyles with template literals
 */

const TemplateButton = styled(Button)`
  background: red;
  color: #fff;
  *:where(.mode-dark) & {
    background: aliceblue;
    color: #555;
  }
`;

/**
 * applyStyles isn't a valid alternative to controlling theme for a specific component.
 * If you don't split all styles into applyStyles('light') and applyStyles('dark') then it won't apply the alternative
 */

const Themed = () => (
  <div className=".mode-light">
    <BaseButton variant="contained">Light Themed</BaseButton>
  </div>
);

export default function BasicButtons() {
  return (
    <Stack spacing={2} direction="row">
      <BaseButton
        variant="contained"
        sx={{
          background: '#bada55',
        }}
      >
        Dark
      </BaseButton>
      <StyledButton variant="contained">Styled</StyledButton>
      <TemplateButton
        variant="contained"
        sx={{
          background: '#bada55',
        }}
      >
        Template
      </TemplateButton>
      <Themed />
    </Stack>
  );
}

Screenshot 2024-11-20 at 11 44 00 AM

Your environment

npx @mui/envinfo
  System:
    OS: macOS 14.1.1
  Binaries:
    Node: 20.12.2 - ~/.nvm/versions/node/v20.12.2/bin/node
    npm: 10.8.3 - ~/.nvm/versions/node/v20.12.2/bin/npm
    pnpm: 9.13.0 - ~/.nvm/versions/node/v20.12.2/bin/pnpm
  Browsers:
    Chrome: 131.0.6778.71
    Edge: Not Found
    Safari: 17.1

Search keywords: css variables, css vars, applyStyles, sx, styled

@RickyRoller RickyRoller added the status: waiting for maintainer These issues haven't been looked at yet by a maintainer label Nov 20, 2024
@DiegoAndai DiegoAndai moved this to Selected in Material UI Nov 21, 2024
@DiegoAndai DiegoAndai added the customization: theme Centered around the theming features label Nov 21, 2024
@siriwatknp
Copy link
Member

siriwatknp commented Nov 22, 2024

Sx and Styled theme behavior breaks with this new paradigm. They can't override the styles if the base component is using the new applyStyles helper since it creates a css selector with higher specificity

This is expected as a default behavior. However, you can customize applyStyles to match your need, here is an example:

const theme = createTheme({
  cssVariables: {
    colorSchemeSelector: '.mode-%s',
  },
  colorSchemes: {
    dark: {},
    light: {},
  },
  applyStyles: function (key: string, styles: any) {
    if (typeof styles === 'string') {
      return `*:where(.mode-${key}:not(:has(.mode-light))) & {${styles}}`;
    }
    return {
      [`*:where(.mode-${key}:not(:has(.mode-light &))) & `]: styles,
    };
  },
});

@siriwatknp siriwatknp added bug 🐛 Something doesn't work enhancement This is not a bug, nor a new feature and removed status: waiting for maintainer These issues haven't been looked at yet by a maintainer bug 🐛 Something doesn't work labels Nov 22, 2024
@siriwatknp
Copy link
Member

I mark this as an enhancement to make the applyStyles support literal styles.

And to add a docs about customizing theme.applyStyles.

@siriwatknp siriwatknp added the docs Improvements or additions to the documentation label Nov 22, 2024
@RickyRoller
Copy link
Author

RickyRoller commented Nov 22, 2024

It's good to know that you can override the applyStyles function, but that's not the crux of this issue.

The issue is that there is a breaking change when switching to the new system of using applyStyles and there is seemingly no workaround for it.

This code creates green text in both themes

const Text = styled('div')`
  color: ${({ theme }) => theme.palette.mode === 'dark' ? 'red' : 'blue'};
`;

<Text sx={{ color: 'green' }}>Hello</Text>

But that's not possible with the new system. This results in green text in light mode and red text in dark mode.

const Text = styled('div')(({ theme }) => ({
  color: 'blue',
  ...theme.applyStyles('dark', {
    color: 'red',
  }),
}));

<Text sx={{ color: 'green' }}>Hello</Text>

It's a problem with sx and styled modifying the base styles of a component and so the dark theme styles always overwrite those. Honestly I'm not sure how this is even solvable with out a major change like moving styles into layers so that sx/styled always override.
If it's not solvable, there should be a massive warning on the docs that say this approach has a very real limitation that will be very hard to work around and is a breaking change from old behavior

@siriwatknp
Copy link
Member

The issue is that there is a breaking change when switching to the new system of using applyStyles and there is seemingly no workaround for it.

That's true. I will add docs for this but I don't see a perfect transition, the @layer will make the experience the same but you will to adjust the external CSS.

Anyway, the next step is to add option to enable CSS @layer as it's become a standard.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
customization: theme Centered around the theming features docs Improvements or additions to the documentation enhancement This is not a bug, nor a new feature
Projects
Status: Selected
Development

No branches or pull requests

3 participants