跳到內容
+

深色模式

Material UI 提供兩種色盤模式:淺色(預設)和深色。

僅限深色模式

您可以將深色主題設為應用程式的預設主題 (無論使用者偏好為何),方法是在 createTheme() 輔助函式中加入 mode: 'dark'

import { ThemeProvider, createTheme } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';

const darkTheme = createTheme({
  palette: {
    mode: 'dark',
  },
});

export default function App() {
  return (
    <ThemeProvider theme={darkTheme}>
      <CssBaseline />
      <main>This app is using the dark mode</main>
    </ThemeProvider>
  );
}

createTheme() 輔助函式中加入 mode: 'dark' 會修改數個色盤值,如下列示範所示

排版

palette.text.primary

#fff

palette.text.secondary

rgba(255, 255, 255, 0.7)

palette.text.disabled

rgba(255, 255, 255, 0.5)

按鈕

palette.action.active

#fff

palette.action.hover

rgba(255, 255, 255, 0.08)

palette.action.selected

rgba(255, 255, 255, 0.16)

palette.action.disabled

rgba(255, 255, 255, 0.3)

palette.action.disabledBackground

rgba(255, 255, 255, 0.12)

背景

palette.background.default

#121212

palette.background.paper

#121212

分隔線

palette.divider

rgba(255, 255, 255, 0.12)

<ThemeProvider> 組件內加入 <CssBaseline /> 也會為應用程式的背景啟用深色模式。

覆寫深色色盤

若要覆寫預設色盤,請提供一個色盤物件,其中包含十六進位、RGB 或 HSL 格式的自訂色彩

const darkTheme = createTheme({
  palette: {
    mode: 'dark',
    primary: {
      main: '#ff5252',
    },
  },
});

深入瞭解色盤結構,請參閱色盤文件

系統偏好設定

有些使用者透過其作業系統 (無論是全系統範圍,還是針對個別使用者代理程式) 設定淺色或深色模式的偏好設定。以下章節說明如何將這些偏好設定套用至應用程式的主題。

內建支援

使用 colorSchemes 節點來建置具有多種色彩配置的應用程式。內建的色彩配置為 lightdark,可以透過將值設為 true 來啟用。

淺色色彩配置預設為啟用,因此您只需要設定深色色彩配置

import { ThemeProvider, createTheme } from '@mui/material/styles';

const theme = createTheme({
  colorSchemes: {
    dark: true,
  },
});

function App() {
  return <ThemeProvider theme={theme}>...</ThemeProvider>;
}

當提供 colorSchemes 時,會啟用下列功能

  • 根據使用者的偏好設定,在淺色和深色色彩配置之間自動切換
  • 視窗標籤頁之間的同步 - 在一個標籤頁中變更色彩配置會套用至所有其他標籤頁
  • 在色彩配置變更時停用轉場效果的選項

存取媒體 prefers-color-scheme

您可以搭配 useMediaQuery hook 和 prefers-color-scheme 媒體查詢來使用此偏好設定。

下列示範說明如何在作業系統或瀏覽器設定中檢查使用者的偏好設定

import * as React from 'react';
import useMediaQuery from '@mui/material/useMediaQuery';
import { createTheme, ThemeProvider } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';

function App() {
  const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
  return <div>prefersDarkMode: {prefersDarkMode.toString()}</div>;
}

切換色彩模式

若要讓使用者可以切換內建支援的模式,請使用 useColorScheme hook 來讀取和更新模式。

儲存管理器

預設情況下,色彩配置的內建支援使用瀏覽器的 localStorage API 來儲存使用者的模式和配置偏好設定。

若要使用不同的儲存管理器,請建立具有此簽章的自訂函式

type Unsubscribe = () => void;

function storageManager(params: { key: string }): {
  get: (defaultValue: any) => any;
  set: (value: any) => void;
  subscribe: (handler: (value: any) => void) => Unsubscribe;
};

然後將其傳遞至 ThemeProvider 組件的 storageManager prop

import { ThemeProvider, createTheme } from '@mui/material/styles';
import type { StorageManager } from '@mui/material/styles';

const theme = createTheme({
  colorSchemes: {
    dark: true,
  },
});

function storageManager(params): StorageManager {
  return {
    get: (defaultValue) => {
      // Your implementation
    },
    set: (value) => {
      // Your implementation
    },
    subscribe: (handler) => {
      // Your implementation
      return () => {
        // cleanup
      };
    },
  };
}

function App() {
  return (
    <ThemeProvider theme={theme} storageManager={storageManager}>
      ...
    </ThemeProvider>
  );
}

停用儲存

若要停用儲存管理器,請將 null 傳遞至 storageManager prop

<ThemeProvider theme={theme} storageManager={null}>
  ...
</ThemeProvider>

停用轉場效果

若要在色彩配置之間立即切換而沒有轉場效果,請將 disableTransitionOnChange prop 套用至 ThemeProvider 組件

<ThemeProvider theme={theme} disableTransitionOnChange>
  ...
</ThemeProvider>

停用雙重渲染

預設情況下,當主題包含淺色深色色彩配置時,ThemeProvider 會重新渲染,以防止 SSR hydration mismatch。

若要停用此行為,請使用 noSsr prop

<ThemeProvider theme={theme} noSsr>

如果您正在建置

  • 僅限用戶端應用程式,例如單頁應用程式 (SPA),則 noSsr 非常有用。此 prop 將最佳化效能,並防止使用者重新整理頁面時深色模式閃爍。
  • 具有Suspense的伺服器渲染應用程式。但是,您必須確保伺服器渲染輸出與用戶端上的初始渲染輸出相符。

設定預設模式

當提供 colorSchemes 時,預設模式為 system,這表示應用程式在使用者首次造訪網站時使用系統偏好設定。

若要設定不同的預設模式,請將 defaultMode prop 傳遞至 ThemeProvider 組件

<ThemeProvider theme={theme} defaultMode="dark">

InitColorSchemeScript 組件

如果您使用 InitColorSchemeScript 組件來防止 SSR 閃爍,則必須使用您傳遞至 ThemeProvider 組件的相同值來設定 defaultMode

<InitColorSchemeScript defaultMode="dark">

在深色模式中設定樣式

使用 theme.applyStyles() 公用程式來套用特定模式的樣式。

我們建議使用此函式來切換樣式,而不是檢查 theme.palette.mode,因為它具有更多優點

  • 它可以與 Pigment CSS (我們內部開發的零執行階段 CSS-in-JS 解決方案) 搭配使用。
  • 它通常更易於閱讀和維護。
  • 它的效能稍微好一些,因為它不需要重新計算樣式,但 SSR 產生樣式的套件大小較大。

用法

搭配 styled 函式

import { styled } from '@mui/material/styles';

const MyComponent = styled('div')(({ theme }) => [
  {
    color: '#fff',
    backgroundColor: theme.palette.primary.main,
    '&:hover': {
      boxShadow: theme.shadows[3],
      backgroundColor: theme.palette.primary.dark,
    },
  },
  theme.applyStyles('dark', {
    backgroundColor: theme.palette.secondary.main,
    '&:hover': {
      backgroundColor: theme.palette.secondary.dark,
    },
  }),
]);

搭配 sx prop

import Button from '@mui/material/Button';

<Button
  sx={[
    (theme) => ({
      color: '#fff',
      backgroundColor: theme.palette.primary.main,
      '&:hover': {
        boxShadow: theme.shadows[3],
        backgroundColor: theme.palette.primary.dark,
      },
    }),
    (theme) =>
      theme.applyStyles('dark', {
        backgroundColor: theme.palette.secondary.main,
        '&:hover': {
          backgroundColor: theme.palette.secondary.dark,
        },
      }),
  ]}
>
  Submit
</Button>;

API

theme.applyStyles(mode, styles) => CSSObject

套用特定模式的樣式。

引數

  • mode ('light' | 'dark') - 應套用樣式的模式。
  • styles (CSSObject) - 物件,其中包含要針對指定模式套用的樣式。

覆寫 applyStyles

您可以覆寫 theme.applyStyles() 與自訂函式,以完全控制其傳回的值。請檢閱原始碼,以瞭解預設實作的運作方式,然後再覆寫它。例如,如果您需要函式傳回字串而不是物件,使其可以在樣板字串內使用

const theme = createTheme({
  cssVariables: {
    colorSchemeSelector: '.mode-%s',
  },
  colorSchemes: {
    dark: {},
    light: {},
  },
  applyStyles: function (key: string, styles: any) {
    // return a string instead of an object
    return `*:where(.mode-${key}) & {${styles}}`;
  },
});

const StyledButton = styled('button')`
  ${theme.applyStyles(
    'dark', `
      background: white;
    `
  )}
`;

Codemod

我們提供 codemod,可將您的程式碼庫從使用 theme.palette.mode 遷移至使用 theme.applyStyles()。您可以執行下列每個 codemod 或一次全部執行。

npx @mui/codemod@latest v6.0.0/styled <path/to/folder-or-file>
npx @mui/codemod@latest v6.0.0/sx-prop <path/to/folder-or-file>
npx @mui/codemod@latest v6.0.0/theme-v6 <path/to/theme-file>

針對包含自訂 styleOverrides 的檔案執行 v6.0.0/theme-v6。如果您沒有自訂主題,請忽略此 codemod。

深色模式閃爍

問題

伺服器渲染的應用程式在到達使用者的裝置之前建置。這表示它們在首次載入時無法自動調整為使用者偏好的色彩配置。

以下是通常發生的情況

  1. 您載入應用程式並將其設定為深色模式。
  2. 您重新整理頁面。
  3. 應用程式短暫地以淺色模式 (預設) 顯示。
  4. 然後,一旦應用程式完全載入,它會切換回深色模式。

只要您的瀏覽器記住您的深色模式偏好設定,每次您開啟應用程式時,都會發生這種淺色模式的「閃爍」。

這種突然的變更可能會令人感到刺眼,尤其是在低光源環境中。它可能會使您的眼睛疲勞並中斷您的體驗,特別是如果您在此轉場期間與應用程式互動。

若要更瞭解此問題,請查看以下動畫影像

An example video that shows a page that initially loads correctly in dark mode but quickly flickers to light mode.

解決方案:CSS 變數

解決此問題需要一種新穎的樣式和主題方法。(請參閱此關於 CSS 變數支援的 RFC,以深入瞭解此功能的實作。)

對於需要使用 CSS 媒體 prefers-color-scheme 支援淺色和深色模式的應用程式,啟用CSS 變數功能即可修正此問題。

但是,如果您想要能夠手動切換模式,避免閃爍需要結合 CSS 變數和 InitColorSchemeScript 組件。請查看防止 SSR 閃爍章節以瞭解更多詳細資訊。