From 99959e3b218dbdbd92e703d0326fc36bd33d2ae2 Mon Sep 17 00:00:00 2001 From: Siriwat K Date: Fri, 14 Jun 2024 11:31:36 +0700 Subject: [PATCH 1/6] Add `InitColorSchemeScript` for Next.js App Router (#42247) --- .../joy/customization/dark-mode/dark-mode.md | 38 +++++++++++++--- .../dark-mode-optimization.md | 6 +-- .../css-theme-variables/customization.md | 6 +-- .../css-theme-variables/migration.md | 12 +++--- .../css-theme-variables/usage/usage.md | 34 +++++++++++---- docs/pages/_document.js | 8 ++-- docs/pages/blog/first-look-at-joy.md | 6 +-- .../joyUi/projectSettings.ts | 5 ++- .../materialUi/projectSettings.ts | 2 +- .../muiSystem/projectSettings.ts | 4 +- .../ApiBuilders/ComponentApiBuilder.ts | 2 +- .../InitColorSchemeScript.spec.tsx | 4 ++ .../InitColorSchemeScript.test.tsx | 13 ++++++ .../InitColorSchemeScript.tsx | 14 ++++++ .../src/InitColorSchemeScript/index.ts | 1 + .../mui-joy/src/styles/CssVarsProvider.tsx | 38 ++++++++++++---- .../InitColorSchemeScript.spec.tsx | 4 ++ .../InitColorSchemeScript.test.tsx | 13 ++++++ .../InitColorSchemeScript.tsx | 14 ++++++ .../src/InitColorSchemeScript/index.ts | 1 + .../src/styles/CssVarsProvider.tsx | 20 +++++---- .../InitColorSchemeScript.test.js} | 43 ++++++++----------- .../InitColorSchemeScript.tsx} | 14 +++++- .../src/InitColorSchemeScript/index.ts | 2 + .../src/cssVars/createCssVarsProvider.d.ts | 4 +- .../src/cssVars/createCssVarsProvider.js | 6 +-- .../src/cssVars/createCssVarsProvider.test.js | 5 ++- .../src/cssVars/useCurrentColorScheme.test.js | 2 +- .../src/cssVars/useCurrentColorScheme.ts | 2 +- packages/rsc-builder/buildRsc.ts | 14 +++++- 30 files changed, 242 insertions(+), 95 deletions(-) create mode 100644 packages/mui-joy/src/InitColorSchemeScript/InitColorSchemeScript.spec.tsx create mode 100644 packages/mui-joy/src/InitColorSchemeScript/InitColorSchemeScript.test.tsx create mode 100644 packages/mui-joy/src/InitColorSchemeScript/InitColorSchemeScript.tsx create mode 100644 packages/mui-joy/src/InitColorSchemeScript/index.ts create mode 100644 packages/mui-material/src/InitColorSchemeScript/InitColorSchemeScript.spec.tsx create mode 100644 packages/mui-material/src/InitColorSchemeScript/InitColorSchemeScript.test.tsx create mode 100644 packages/mui-material/src/InitColorSchemeScript/InitColorSchemeScript.tsx create mode 100644 packages/mui-material/src/InitColorSchemeScript/index.ts rename packages/mui-system/src/{cssVars/getInitColorSchemeScript.test.js => InitColorSchemeScript/InitColorSchemeScript.test.js} (78%) rename packages/mui-system/src/{cssVars/getInitColorSchemeScript.tsx => InitColorSchemeScript/InitColorSchemeScript.tsx} (86%) create mode 100644 packages/mui-system/src/InitColorSchemeScript/index.ts diff --git a/docs/data/joy/customization/dark-mode/dark-mode.md b/docs/data/joy/customization/dark-mode/dark-mode.md index 45ed52be23f0a2..f3cb415cd8e6f0 100644 --- a/docs/data/joy/customization/dark-mode/dark-mode.md +++ b/docs/data/joy/customization/dark-mode/dark-mode.md @@ -12,10 +12,10 @@ When you change the `defaultMode` to another value, you must clear the local sto {{"demo": "DarkModeByDefault.js"}} -For server-side applications, check out the framework setup in [the section below](#server-side-rendering) and provide the same value to the `getInitColorSchemeScript` function: +For server-side applications, check out the framework setup in [the section below](#server-side-rendering) and provide the same value to the `InitColorSchemeScript` component: ```js -getInitColorSchemeScript({ defaultMode: 'dark' }); + ``` ## Matching device's preference @@ -28,10 +28,10 @@ import { CssVarsProvider } from '@mui/joy/styles'; ...; ``` -For server-side applications, check out the framework setup in [the section below](#server-side-rendering) and provide the same value to the `getInitColorSchemeScript` function: +For server-side applications, check out the framework setup in [the section below](#server-side-rendering) and provide the same value to the `InitColorSchemeScript` component: ```js -getInitColorSchemeScript({ defaultMode: 'system' }); + ``` ### Identify the system mode @@ -121,7 +121,31 @@ If you try to render your UI based on the server, before mounting on the client, ### Avoiding screen flickering -To [prevent the UI from flickering](/joy-ui/main-features/dark-mode-optimization/#the-problem-flickering-on-first-load), apply `getInitColorSchemeScript()` before the main application script-it varies across frameworks: +To [prevent the UI from flickering](/joy-ui/main-features/dark-mode-optimization/#the-problem-flickering-on-first-load), apply `` before the main application script-it varies across frameworks: + +### Next.js App Router + +To use the Joy UI API with a Next.js project with the App Router, add the following code to the [`app/layout.js`](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#layouts) file in order to prevent flickering: + +```jsx title="layout.js" +import InitColorSchemeScript from '@mui/joy/InitColorSchemeScript'; +import { CssVarsProvider } from '@mui/joy/styles'; +import CssBaseline from '@mui/joy/CssBaseline'; + +export default function RootLayout({ children }) { + return ( + + + + + + {children} + + + + ); +} +``` ### Next.js Pages Router @@ -129,7 +153,7 @@ To use the Joy UI API with a Next.js project, add the following code to the cus ```jsx import Document, { Html, Head, Main, NextScript } from 'next/document'; -import { getInitColorSchemeScript } from '@mui/joy/styles'; +import InitColorSchemeScript from '@mui/joy/InitColorSchemeScript'; export default class MyDocument extends Document { render() { @@ -137,7 +161,7 @@ export default class MyDocument extends Document { ... - {getInitColorSchemeScript()} +
diff --git a/docs/data/joy/main-features/dark-mode-optimization/dark-mode-optimization.md b/docs/data/joy/main-features/dark-mode-optimization/dark-mode-optimization.md index 4b8a62fdf249e5..1afca0965a328c 100644 --- a/docs/data/joy/main-features/dark-mode-optimization/dark-mode-optimization.md +++ b/docs/data/joy/main-features/dark-mode-optimization/dark-mode-optimization.md @@ -27,14 +27,14 @@ Solving this problem required us to take a novel approach to styling and theming Thanks to Joy UI's built-in support for CSS variables, your app can render all of its color schemes at build time, so that the user's preference can be injected _before_ the DOM is rendered in the browser. -Joy UI provides the `getInitColorSchemeScript()` function to make this flash-free dark mode possible with React frameworks like Next.js or Remix. +Joy UI provides the `InitColorSchemeScript` component to make this flash-free dark mode possible with React frameworks like Next.js or Remix. This function must be placed before the main script so it can apply the correct stylesheet before your components are rendered. The code snippet below shows how this works with the Next.js Pages Router: ```jsx import Document, { Html, Head, Main, NextScript } from 'next/document'; -import { getInitColorSchemeScript } from '@mui/joy/styles'; +import InitColorSchemeScript from '@mui/joy/InitColorSchemeScript'; export default class MyDocument extends Document { render() { @@ -42,7 +42,7 @@ export default class MyDocument extends Document { ... - {getInitColorSchemeScript()} +
diff --git a/docs/data/material/experimental-api/css-theme-variables/customization.md b/docs/data/material/experimental-api/css-theme-variables/customization.md index 6c8776bf98005e..f788e64fb848d7 100644 --- a/docs/data/material/experimental-api/css-theme-variables/customization.md +++ b/docs/data/material/experimental-api/css-theme-variables/customization.md @@ -289,12 +289,10 @@ function App() { } ``` -For a server-side application, provide the same value to [`getInitColorSchemeScript()`](/material-ui/experimental-api/css-theme-variables/usage/#server-side-rendering): +For a server-side application, provide the same value to [`InitColorSchemeScript`](/material-ui/customization/css-theme-variables/usage/#server-side-rendering): ```js -getInitColorSchemeScript({ - defaultMode: 'dark', -}); + ``` :::warning diff --git a/docs/data/material/experimental-api/css-theme-variables/migration.md b/docs/data/material/experimental-api/css-theme-variables/migration.md index 02cf6c5a4bddc2..0ea3e3707ac76f 100644 --- a/docs/data/material/experimental-api/css-theme-variables/migration.md +++ b/docs/data/material/experimental-api/css-theme-variables/migration.md @@ -180,15 +180,15 @@ The `mode` is stored inside `CssVarsProvider` which handles local storage synchr ## 3. Prevent dark-mode flickering in server-side applications -The `getInitColorSchemeScript()` API prevents dark-mode flickering by returning a script that must be run before React. +The `InitColorSchemeScript` component prevents dark-mode flickering by returning a script that must be run before React. ### Next.js Pages Router -Place the script before `
` in your [`pages/_document.js`](https://nextjs.org/docs/pages/building-your-application/routing/custom-document): +Place the component before `
` in your [`pages/_document.js`](https://nextjs.org/docs/pages/building-your-application/routing/custom-document): ```jsx import Document, { Html, Head, Main, NextScript } from 'next/document'; -import { getInitColorSchemeScript } from '@mui/material/styles'; +import InitColorSchemeScript from '@mui/material/InitColorSchemeScript'; export default class MyDocument extends Document { render() { @@ -196,7 +196,7 @@ export default class MyDocument extends Document { ... - {getInitColorSchemeScript()} +
@@ -212,10 +212,10 @@ Place the script in your [`gatsby-ssr.js`](https://www.gatsbyjs.com/docs/referen ```jsx import * as React from 'react'; -import { getInitColorSchemeScript } from '@mui/material/styles'; +import InitColorSchemeScript from '@mui/material/InitColorSchemeScript'; export function onRenderBody({ setPreBodyComponents }) { - setPreBodyComponents([getInitColorSchemeScript()]); + setPreBodyComponents([]); } ``` diff --git a/docs/data/material/experimental-api/css-theme-variables/usage/usage.md b/docs/data/material/experimental-api/css-theme-variables/usage/usage.md index 08089eb2def2a3..ad3bf34327fff2 100644 --- a/docs/data/material/experimental-api/css-theme-variables/usage/usage.md +++ b/docs/data/material/experimental-api/css-theme-variables/usage/usage.md @@ -116,15 +116,34 @@ The structure of this object is nearly identical to the theme structure, the onl ## Server-side rendering -Place `getInitColorSchemeScript()` before the `
` tag to prevent the dark-mode SSR flickering during the hydration phase. +Place `` before the `
` tag to prevent the dark-mode SSR flickering during the hydration phase. + +### Next.js App Router + +Add the following code to the [root layout](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required) file: + +```jsx title="app/layout.js" +import InitColorSchemeScript from '@mui/material/InitColorSchemeScript'; + +export default function RootLayout({ children }) { + return ( + + + {/* must come before the
element */} +
{children}
+ + + ); +} +``` ### Next.js Pages Router Add the following code to the custom [`pages/_document.js`](https://nextjs.org/docs/pages/building-your-application/routing/custom-document) file: -```jsx +```jsx title="pages/_document.js" import Document, { Html, Head, Main, NextScript } from 'next/document'; -import { getInitColorSchemeScript } from '@mui/material/styles'; +import InitColorSchemeScript from '@mui/material/InitColorSchemeScript'; export default class MyDocument extends Document { render() { @@ -132,7 +151,7 @@ export default class MyDocument extends Document { ... - {getInitColorSchemeScript()} + {/* must come before the
element */}
@@ -159,7 +178,7 @@ const StyledComponent = styled('button')(({ theme }) => ({ ## API -### `` props +### ``  props - `defaultMode?: 'light' | 'dark' | 'system'` - Application's default mode (`light` by default) - `disableTransitionOnChange : boolean` - Disable CSS transitions when switching between modes @@ -172,10 +191,9 @@ const StyledComponent = styled('button')(({ theme }) => ({ - `mode: string` - The user's selected mode - `setMode: mode => {…}` - Function for setting the `mode`. The `mode` is saved to internal state and local storage; if `mode` is null, it will be reset to the default mode -### `getInitColorSchemeScript: (options) => React.ReactElement` - -**options** +### ``  props - `defaultMode?: 'light' | 'dark' | 'system'`: - Application's default mode before React renders the tree (`light` by default) - `modeStorageKey?: string`: - localStorage key used to store application `mode` - `attribute?: string` - DOM attribute for applying color scheme +- `nonce?: string` - Optional nonce passed to the injected script tag, used to allow-list the next-themes script in your CSP diff --git a/docs/pages/_document.js b/docs/pages/_document.js index a800959e8aa63b..c2ffa343a34cf4 100644 --- a/docs/pages/_document.js +++ b/docs/pages/_document.js @@ -5,8 +5,8 @@ import { ServerStyleSheets as JSSServerStyleSheets } from '@mui/styles'; import { ServerStyleSheet } from 'styled-components'; import Document, { Html, Head, Main, NextScript } from 'next/document'; import GlobalStyles from '@mui/material/GlobalStyles'; -import { getInitColorSchemeScript as getMuiInitColorSchemeScript } from '@mui/material/styles'; -import { getInitColorSchemeScript as getJoyInitColorSchemeScript } from '@mui/joy/styles'; +import MuiInitColorSchemeScript from '@mui/material/InitColorSchemeScript'; +import JoyInitColorSchemeScript from '@mui/joy/InitColorSchemeScript'; import { pathnameToLanguage } from 'docs/src/modules/utils/helpers'; import createEmotionCache from 'docs/src/createEmotionCache'; import { getMetaThemeColor } from '@mui/docs/branding'; @@ -173,8 +173,8 @@ export default class MyDocument extends Document { /> - {getMuiInitColorSchemeScript({ defaultMode: 'system' })} - {getJoyInitColorSchemeScript({ defaultMode: 'system' })} + +