用CSS属性在React应用中实现暗色模式的详细指南

374 阅读3分钟

暗色模式是一项允许用户选择暗色方案的功能,在macOS、iOS、Android和Windows 10中都得到支持。在这篇文章中,我们将介绍如何在CSS属性的帮助下将暗色模式支持添加到React应用中。

Dark mode

从操作系统中获取首选颜色方案

用户在操作系统中的首选颜色方案被保存在 prefers-color-schemeCSS媒体特征中。我们可以使用 matchMedia方法来获得这个值,Window 对象:

const darkOS = window.matchMedia(
  "(prefers-color-scheme: dark)"
).matches;

为了在Chrome浏览器中测试这一点,我们可以在DevTools中设置prefers-color-scheme 。我们在 "设置">"偏好"部分找到这个。

Dark mode in chrome 在React上下文中保持主题

我们将使用React上下文来保持当前的主题(浅色或深色),并允许任何组件使用它。主题上下文也将使主题被设置为与操作系统颜色方案不同的值。

让我们从创建上下文开始:

type ThemeName = "light" | "dark";
type ThemeContextType = {
  theme: ThemeName;
  setTheme: (name: ThemeName) => void;
};
const ThemeContext = React.createContext<
  ThemeContextType
>(undefined!);

我们使用一个TypeScript联合类型来强制规定主题名称只能是"light""dark"

然后我们可以为这个上下文创建一个组件可以消费的提供者组件:

type Props = {
  children: React.ReactNode,
};
export const ThemeProvider = ({
  children,
}: Props) => {
  const [themeName, setThemeName] = 
    React.useState<ThemeName>("light");

  React.useEffect(() => {
    const darkOS = window.matchMedia(
      "(prefers-color-scheme: dark)"
    ).matches;
    setTheme(darkOS ? "dark" : "light");
  }, []);

  const setTheme = (name: ThemeName) => {
    setThemeName(name);
  };
  return (
    <ThemeContext.Provider
      value={{ theme: themeName, setTheme }}
    >
      {children}
    </ThemeContext.Provider>
  );
};

我们在状态中存储主题名称。我们在初始渲染后使用一个useEffect 钩子,将初始主题设置为操作系统的颜色方案。

我们还可以创建一个自定义钩子来获取和设置主题名称:

export const useTheme = () =>
  React.useContext(ThemeContext);

ThemeProvider 这样就可以把需要访问主题的组件包裹起来:

export default function App() {
  return (
    <ThemeProvider>
      <Page />
    </ThemeProvider>
  );
}

useTheme 钩子可以在一个组件中使用,以访问和设置主题名称:

const Page = () => {
  const { theme, setTheme } = useTheme();
  return (
    <div>
      <h1>{theme}</h1>
      <button
        onClick={() =>
          setTheme(
            theme === "dark" ? "light" : "dark"
          )
        }
      >
        {theme === "dark"
          ? "Switch to light mode"
          : "switch to dark mode"}
      </button>
    </div>
  );
};

为颜色使用CSS属性

CSS属性允许CSS基于动态变量,这些变量可以在运行时改变。它们有时被称为CSS变量。

我们将CSS属性定义在 :root伪类。这是一个代表文档的树状元素的根:

:root {
  --background-color: #fefefe;
  --color: #343434;
}

我们已经将这些值初始化为浅色模式的颜色。

然后我们可以使用var 函数在其他CSS类定义中引用这些CSS属性:

body {
  background-color: var(--background-color);
  color: var(--color);
}

在React组件中设置一个CSS属性

我们可以在一个对象字面中保存主题的颜色:

const themeColours = {
  light: {
    color: "#343434",
    backgroundColor: "#fefefe",
  },
  dark: {
    color: "#fff",
    backgroundColor: "#3f3f3f",
  },
};

在主题上下文提供者中,我们可以使用样式上的setProperty 方法设置CSS属性值,如下所示:

const setTheme = (name: ThemeName) => {
  document.body.style.setProperty(    "--color",    themeColours[name].color  );  document.body.style.setProperty(    "--background-color",    themeColours[name].backgroundColor  );  setThemeName(name);
};

这就完成了在React应用中实现黑暗模式的所有关键部分。

在CodeSandbox中可以找到本帖代码的一个工作实例,网址是:https://codesandbox.io/s/dark-mode-on6gi。