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

从操作系统中获取首选颜色方案
用户在操作系统中的首选颜色方案被保存在 prefers-color-schemeCSS媒体特征中。我们可以使用 matchMedia方法来获得这个值,Window 对象:
const darkOS = window.matchMedia(
"(prefers-color-scheme: dark)"
).matches;
为了在Chrome浏览器中测试这一点,我们可以在DevTools中设置prefers-color-scheme 。我们在 "设置">"偏好"部分找到这个。
在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。