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

[material-ui] Support CSS variables with shadow DOM #43948

Merged
merged 5 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading