黑暗模式切换和偏好配色方案
2021 年,当我在 React 中编写一个可访问的暗黑模式切换功能时, @grahamthedev建议我在主题设置器中实现一个prefers-color-scheme检查。我终于抽出时间实现了。
什么是prefers-color-scheme
?
prefers-color-scheme
是一种媒体功能。媒体功能提供有关用户设备或用户代理的信息。用户代理是代表用户的程序,在本例中指的是网络浏览器或操作系统 (OS)。
您可能最熟悉媒体查询中使用的媒体功能,例如响应式 CSS。
@media (max-width: 800px) {
.container {
width: 60px;
}
}
默认值为prefers-color-scheme
“亮”。如果用户在其设备或浏览器中明确选择了暗黑模式,prefers-color-scheme
则设置为“暗黑”。您可以在媒体查询中使用它来相应地更新样式。
@media (prefers-color-scheme: dark) {
.theme {
color: #FFFFFF,
background-color: #000000
}
}
模拟用户偏好进行测试
在 Chrome DevTools 中,您可以在渲染选项卡prefers-color-scheme
中模拟和其他媒体功能。
如果您更喜欢 Firefox DevTools,它prefers-color-scheme
在 CSS 检查器中就有按钮。
prefers-color-scheme
使用 JavaScript进行检测
很遗憾,我没有在 CSS 中更改主题。我结合使用了localStorage和替换组件的类名。幸运的是,一如既往,Web API 随时可用。
window.matchMedia
将返回一个MediaQueryList
具有布尔属性的对象matches
。这将适用于任何典型的媒体查询,如下所示prefers-color-scheme
。
window.matchMedia('(prefers-color-scheme: dark)');
我的 Toggle 解决方案
您可以在我的投资组合仓库中查看此应用程序的所有代码。
首先,我需要检查用户是否访问过我的网站,并且localStorage
是否已设置“主题”项。接下来,我需要通过 检查用户的偏好设置是否不是暗黑模式prefers-color-scheme
。然后,我希望将主题默认设置为暗黑模式。我还需要确保在用户设置初始偏好设置后,切换按钮能够更新主题。
我的主题实用程序文件最终看起来像这样:
function setTheme(themeName, setClassName) {
localStorage.setItem('theme', themeName);
setClassName(themeName);
}
function keepTheme(setClassName) {
const theme = localStorage.getItem('theme');
if (theme) {
setTheme(theme, setClassName);
return;
}
const prefersLightTheme = window.matchMedia('(prefers-color-scheme: light)');
if (prefersLightTheme.matches) {
setTheme('theme-light', setClassName);
return;
}
setTheme('theme-dark', setClassName);
}
module.exports = {
setTheme,
keepTheme
}
我的主要组件调用keepTheme()
了它的useEffect
,并setClassName
来自它的状态。我使用在设置项目useState
之前默认使用暗黑模式。localStorage
const [className, setClassName] = useState("theme-dark");
该切换按钮用于setTheme()
更新主题。
重构
之前,setTheme()
没有使用setClassName
。
function setTheme(themeName) {
document.documentElement.className = themeName;
localStorage.setItem('theme', themeName);
}
由于我使用的是 React,所以我想避免直接操作 DOM。现在,我的主组件在其最外层元素上使用了一个动态类名。
<div className={`App ${className}`}>
setClassName
我想在未来的某个时候重构我的组件架构,这可能有助于我减少作为回调传递的次数。
keepTheme()
曾经有很多嵌套的条件。
if (localStorage.getItem('theme')) {
if (localStorage.getItem('theme') === 'theme-dark') {
setTheme('theme-dark');
} else if (localStorage.getItem('theme') === 'theme-light') {
setTheme('theme-light');
}
} else {
setTheme('theme-dark');
}
我的本能总是明确地说明else
,所以我的下一个解决方案仍然检查了太多东西。我至少开始使用保护条款了。
const theme = localStorage.getItem('theme');
if (theme) {
if (theme === 'theme-dark') {
setTheme('theme-dark');
}
if (theme === 'theme-light') {
setTheme('theme-light');
}
return;
}
const prefersDarkTheme = window.matchMedia('(prefers-color-scheme: dark)');
if (prefersDarkTheme.matches) {
setTheme('theme-dark');
return;
}
const prefersLightTheme = window.matchMedia('(prefers-color-scheme: light)');
if (prefersLightTheme.matches) {
setTheme('theme-light');
return;
}
setTheme('theme-dark');
此时,我意识到如果我已经默认使用暗黑模式,就不需要检查了(prefers-color-scheme: dark)
。然后我了解到localStorage
项目与窗口的原点绑定。由于我不需要检查值,所以我只需检查theme
是否存在,然后将其传递给即可setTheme()
。
结论
回到这个切换开关,我有点怀旧。差不多两年前,它帮助我找到了第一份开发者工作。有时候,回顾自己在知识匮乏的时候写的代码,会让人感到很不自在。这次,我已经在做两年后必须做的那种更新了,很高兴看到自己学到了这么多。这让我想要重构应用程序的其余部分,并期待着在接下来的两年里看到自己能学到什么。
文章来源:https://dev.to/abbeyperini/dark-mode-toggle-and-prefers-color-scheme-4f3m