Skip to content

[material-nextjs] Back port CSS layers for pages router #46283

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

Draft
wants to merge 2 commits into
base: v6.x
Choose a base branch
from
Draft
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
36 changes: 35 additions & 1 deletion docs/data/material/integrations/nextjs/nextjs.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ Finally, in `src/app/layout.tsx`, pass the theme to the `ThemeProvider`:

To learn more about theming, check out the [theming guide](/material-ui/customization/theming/) page.

#### CSS theme variables
### CSS theme variables

To use [CSS theme variables](/material-ui/customization/css-theme-variables/overview/), enable the `cssVariables` flag:

Expand Down Expand Up @@ -243,6 +243,40 @@ To use a custom [Emotion cache](https://emotion.sh/docs/@emotion/cache), pass it
};
```

#### Cascade layers (optional)

To enable [cascade layers](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Cascade_layers) (`@layer`), create a new cache with `enableCssLayer: true` and pass it to the `emotionCache` property in both `_document.tsx` and `_app.tsx`:

```diff title="pages/_document.tsx"
+import { createEmotionCache } from '@mui/material-nextjs/v15-pagesRouter';
...

MyDocument.getInitialProps = async (ctx) => {
const finalProps = await documentGetInitialProps(ctx, {
+ emotionCache: createEmotionCache({ enableCssLayer: true }),
});
return finalProps;
};
```

```diff title="pages/_app.tsx"
+import { createEmotionCache } from '@mui/material-nextjs/v15-pagesRouter';
...

const clientCache = createEmotionCache({ enableCssLayer: true });

+ export default function MyApp({ emotionCache = clientCache }) {
return (
+ <AppCacheProvider emotionCache={emotionCache}>
<Head>
...
</Head>
...
</AppCacheProvider>
);
}
```

#### App enhancement (optional)

Pass an array to the `plugins` property to enhance the app with additional features, like server-side-rendered styles if you're using JSS and styled-components.
Expand Down
19 changes: 17 additions & 2 deletions packages/mui-material-nextjs/src/v13-pagesRouter/createCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ const isBrowser = typeof document !== 'undefined';
// On the client side, Create a meta tag at the top of the <head> and set it as insertionPoint.
// This assures that MUI styles are loaded first.
// It allows developers to easily override MUI styles with other styling solutions, like CSS modules.
export default function createEmotionCache() {
export default function createEmotionCache(
options?: { enableCssLayer?: boolean } & Parameters<typeof createCache>[0],
) {
let insertionPoint;

if (isBrowser) {
Expand All @@ -15,5 +17,18 @@ export default function createEmotionCache() {
insertionPoint = emotionInsertionPoint ?? undefined;
}

return createCache({ key: 'mui', insertionPoint });
const { enableCssLayer, ...rest } = options ?? {};

const emotionCache = createCache({ key: 'mui', insertionPoint, ...rest });
if (enableCssLayer) {
const prevInsert = emotionCache.insert;
emotionCache.insert = (...args) => {
if (!args[1].styles.startsWith('@layer')) {
// avoid nested @layer
args[1].styles = `@layer mui {${args[1].styles}}`;
}
return prevInsert(...args);
};
}
return emotionCache;
}
1 change: 1 addition & 0 deletions packages/mui-material-nextjs/src/v13-pagesRouter/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './pagesRouterV13Document';
export * from './pagesRouterV13App';
export { default as createEmotionCache } from './createCache';
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,23 @@ export async function documentGetInitialProps(
const { styles } = extractCriticalToChunks(initialProps.html);
return {
...initialProps,
emotionStyleTags: styles.map((style) => (
<style
data-emotion={`${style.key} ${style.ids.join(' ')}`}
key={style.key}
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: style.css }}
/>
)),
emotionStyleTags: styles.map((style) => {
if (!style.css.trim()) {
return null;
}
const isLayerOrderRule = style.css.startsWith('@layer') && !style.css.match(/\{.*\}/);
return (
<style
// If the style is a layer order rule, prefix with the cache key to let Emotion hydrate this node.
// Otherwise, Emotion will hydrate only the non-global styles and they will override the layer order rule.
data-emotion={`${isLayerOrderRule ? `${cache.key} ` : ''}${style.key} ${style.ids.join(' ')}`}
key={style.key}
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: style.css }}
nonce={cache.nonce}
/>
);
}),
};
},
},
Expand Down
Loading