Skip to content

Commit

Permalink
[material-ui] Support CSS variables with shadow DOM (#43948)
Browse files Browse the repository at this point in the history
  • Loading branch information
siriwatknp authored Oct 1, 2024
1 parent bef8afa commit 94fc0ca
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 8 deletions.
29 changes: 29 additions & 0 deletions docs/data/material/customization/shadow-dom/shadow-dom.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,35 @@ const theme = createTheme({
</ThemeProvider>;
```

### 3. CSS theme variables (optional)

:::info
If you use **TypeScript**, you need to [extend the interface of the theme](/material-ui/customization/css-theme-variables/usage/#typescript) first.
:::

To use [CSS theme variables](/material-ui/customization/css-theme-variables/overview/) inside of the shadow DOM, you need to set the selectors for generating the CSS variables:

```diff
const theme = createTheme({
+ cssVariables: {
+ rootSelector: ':host',
+ colorSchemeSelector: 'class',
+ },
components: {
// ...same as above steps
}
})
```

Finally, set the `colorSchemeNode` prop using `shadowRootElement`, from step 1, as the value:

```diff
<ThemeProvider
theme={theme}
+ colorSchemeNode={shadowRootElement}
>
```

## Demo

In the example below you can see that the component outside of the shadow DOM is affected by global styles, while the component inside of the shadow DOM is not:
Expand Down
16 changes: 9 additions & 7 deletions packages/mui-material/src/styles/createGetSelector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import excludeVariablesFromRoot from './excludeVariablesFromRoot';

export default <
T extends {
rootSelector?: string;
colorSchemeSelector?: 'media' | 'class' | 'data' | string;
colorSchemes?: Record<string, any>;
defaultColorScheme?: string;
Expand All @@ -11,6 +12,7 @@ export default <
theme: T,
) =>
(colorScheme: keyof T['colorSchemes'] | undefined, css: Record<string, any>) => {
const root = theme.rootSelector || ':root';
const selector = theme.colorSchemeSelector;
let rule = selector;
if (selector === 'class') {
Expand All @@ -32,34 +34,34 @@ export default <
});
if (rule === 'media') {
return {
':root': css,
[root]: css,
[`@media (prefers-color-scheme: dark)`]: {
':root': excludedVariables,
[root]: excludedVariables,
},
};
}
if (rule) {
return {
[rule.replace('%s', colorScheme)]: excludedVariables,
[`:root, ${rule.replace('%s', colorScheme)}`]: css,
[`${root}, ${rule.replace('%s', colorScheme)}`]: css,
};
}
return { ':root': { ...css, ...excludedVariables } };
return { [root]: { ...css, ...excludedVariables } };
}
if (rule && rule !== 'media') {
return `:root, ${rule.replace('%s', String(colorScheme))}`;
return `${root}, ${rule.replace('%s', String(colorScheme))}`;
}
} else if (colorScheme) {
if (rule === 'media') {
return {
[`@media (prefers-color-scheme: ${String(colorScheme)})`]: {
':root': css,
[root]: css,
},
};
}
if (rule) {
return rule.replace('%s', String(colorScheme));
}
}
return ':root';
return root;
};
10 changes: 10 additions & 0 deletions packages/mui-material/src/styles/createTheme.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,3 +243,13 @@ const theme = createTheme();
},
});
}

// CSS variables for shadow DOM
{
createTheme({
cssVariables: {
rootSelector: ':host',
colorSchemeSelector: 'class',
},
});
}
1 change: 1 addition & 0 deletions packages/mui-material/src/styles/createTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export default function createTheme(
| Pick<
CssVarsThemeOptions,
| 'colorSchemeSelector'
| 'rootSelector'
| 'disableCssColorScheme'
| 'cssVarPrefix'
| 'shouldSkipGeneratingVar'
Expand Down
1 change: 1 addition & 0 deletions packages/mui-material/src/styles/createThemeNoVars.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ type CssVarsProperties = CssThemeVariables extends { enabled: true }
| 'applyStyles'
| 'colorSchemes'
| 'colorSchemeSelector'
| 'rootSelector'
| 'cssVarPrefix'
| 'defaultColorScheme'
| 'getCssVar'
Expand Down
8 changes: 8 additions & 0 deletions packages/mui-material/src/styles/createThemeWithVars.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,13 @@ export interface CssVarsThemeOptions extends Omit<ThemeOptions, 'palette' | 'com
* Generate CSS variables within a data attribute [data-mode-light], [data-mode-dark]
*/
colorSchemeSelector?: 'media' | 'class' | 'data' | string;
/**
* The selector to generate the global CSS variables (non-color-scheme related)
* @default ':root'
* @example ':host' // (for shadow DOM)
* @see https://mui.com/material-ui/customization/shadow-dom/#3-css-theme-variables-optional
*/
rootSelector?: string;
/**
* If `true`, the CSS color-scheme will not be set.
* https://developer.mozilla.org/en-US/docs/Web/CSS/color-scheme
Expand Down Expand Up @@ -430,6 +437,7 @@ export type ThemeCssVar = OverridableStringUnion<
*/
export interface CssVarsTheme extends ColorSystem {
colorSchemes: Partial<Record<SupportedColorScheme, ColorSystem>>;
rootSelector: string;
colorSchemeSelector: 'media' | 'class' | 'data' | string;
cssVarPrefix: string;
defaultColorScheme: SupportedColorScheme;
Expand Down
2 changes: 2 additions & 0 deletions packages/mui-material/src/styles/createThemeWithVars.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ export default function createThemeWithVars(options = {}, ...args) {
colorSchemeSelector: selector = colorSchemesInput.light && colorSchemesInput.dark
? 'media'
: undefined,
rootSelector = ':root',
...input
} = options;
const firstColorScheme = Object.keys(colorSchemesInput)[0];
Expand Down Expand Up @@ -179,6 +180,7 @@ export default function createThemeWithVars(options = {}, ...args) {
...muiTheme,
cssVarPrefix,
colorSchemeSelector: selector,
rootSelector,
getCssVar,
colorSchemes,
font: { ...prepareTypographyVars(muiTheme.typography), ...muiTheme.font },
Expand Down
13 changes: 13 additions & 0 deletions packages/mui-material/src/styles/extendTheme.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -851,5 +851,18 @@ describe('extendTheme', () => {
'.mode-light',
]);
});

it('should use a custom root selector', () => {
const theme = extendTheme({
colorSchemes: { light: true, dark: true },
colorSchemeSelector: 'class',
rootSelector: ':host',
});
expect(theme.generateStyleSheets().flatMap((sheet) => Object.keys(sheet))).to.deep.equal([
':host',
':host, .light',
'.dark',
]);
});
});
});
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export default function shouldSkipGeneratingVar(keys: string[]) {
return (
!!keys[0].match(
/(cssVarPrefix|colorSchemeSelector|typography|mixins|breakpoints|direction|transitions)/,
/(cssVarPrefix|colorSchemeSelector|rootSelector|typography|mixins|breakpoints|direction|transitions)/,
) ||
!!keys[0].match(/sxConfig$/) || // ends with sxConfig
(keys[0] === 'palette' && !!keys[1]?.match(/(mode|contrastThreshold|tonalOffset)/))
Expand Down

0 comments on commit 94fc0ca

Please sign in to comment.