跳到内容
+

深色模式

Material UI 带有两种调色板模式:浅色(默认)和深色。

仅深色模式

您可以通过将 mode: 'dark' 添加到 createTheme() 辅助函数中,使您的应用程序默认使用深色主题——无论用户偏好如何

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>
  );
}

mode: 'dark' 添加到 createTheme() 辅助函数会修改多个调色板值,如下面的演示所示

排版

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 钩子和 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 钩子来读取和更新模式。

存储管理器

默认情况下,配色方案的内置支持使用浏览器的 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 属性

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 属性

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

禁用过渡

要立即在配色方案之间切换而无需过渡,请将 disableTransitionOnChange 属性应用于 ThemeProvider 组件

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

禁用双重渲染

默认情况下,当主题包含浅色深色配色方案时,ThemeProvider 会重新渲染,以防止 SSR 水合不匹配。

要禁用此行为,请使用 noSsr 属性

<ThemeProvider theme={theme} noSsr>

如果您正在构建以下类型的应用程序,则 noSsr 非常有用

  • 仅客户端应用程序,例如单页应用程序 (SPA)。此属性将优化性能并防止用户刷新页面时出现深色模式闪烁。
  • 带有 Suspense 的服务器渲染应用程序。但是,您必须确保服务器渲染输出与客户端上的初始渲染输出匹配。

设置默认模式

当提供 colorSchemes 时,默认模式为 system,这意味着应用程序在用户首次访问该站点时使用系统偏好。

要设置不同的默认模式,请将 defaultMode 属性传递给 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 属性

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 或一次运行所有 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 闪烁部分以获取更多详细信息。