在 React 中切换深色模式
当我重建我的作品集网站时,我就知道我想在设计上玩点花样,而深色和浅色模式正合我意。我非常享受这个项目过程中的诸多发现,但我最喜欢的还是可变色的SVG。本教程假设您已熟悉React,我使用的是v17.0.1版本和函数式组件。
首先,我创建了一个基础布局。接下来,我设计了深色和浅色配色方案。这需要一些尝试和调整,但在测试了所有组合以确保足够的对比度并尝试了不同的布局后,我发现需要 6 个 CSS 变量。可以说我采用了“深色优先”的开发方法,因为这些变量名在深色主题的上下文中是有意义的。浅色主题的变量较少,但需要用于与背景颜色相同的--button-border场景。--accent
.theme-dark {
--dark-text: #292929;
--light-text: #F9F8F8;
--dark-background: #2F4550;
--light-background: #586F7C;
--accent: #B8DBD9;
--button-border: #B8DBD9;
}
.theme-light {
--dark-text: #5E4B56;
--light-text: #5E4B56;
--dark-background: #DBE7E4;
--light-background: #EDDCD2;
--accent: #DBE7E4;
--button-border: #5E4B56;
}
然后,我开始为我的基础布局添加颜色:
html, #root {
background-color: var(--dark-background);
color: var(--dark-text);
}
nav {
background-color: var(--dark-background);
color: var(--light-text);
}
.main-container {
background-color: var(--light-background);
}
我还设置了我想突出显示的各内容部分的背景--accent。--dark-text虽然在深色主题下所有背景都可以,但我将章节标题设置为深色,--light-text使其更加突出。
我找到了 Musthaq Ahamad 的基础主题切换器教程,并着手将其应用到函数式 React 组件中。
我将用于更改主题和检查 localStorage 中主题偏好设置的函数放在一个名为 . 的文件中themes.js。
function setTheme(themeName) {
localStorage.setItem('theme', themeName);
document.documentElement.className = themeName;
}
function 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')
}
}
module.exports = {
setTheme,
keepTheme
}
在我的App.js文件中,我添加keepTheme()了我的useEffect()。
import { keepTheme } from './utils/themes';
function App() {
useEffect(() => {
keepTheme();
})
}
接下来,我将切换组件添加到导航栏组件中。我参考了Chris Bongers基于Katia De Juan Dribbble 作品的教程,为切换组件设置了样式。然后,我调整了切换组件的大小,并将其默认设置为深色模式。虽然这个切换组件可爱到让人心动,但本教程适用于任何<button>可点击的切换组件<input>。首先,我设置了基本的 JSX 代码、本地状态以及一个用于保存从 localStorage 获取的主题的变量:
import React, { useEffect, useState } from 'react';
import '../styles/toggle.css';
import { setTheme } from '../utils/themes';
function Toggle() {
const [togClass, setTogClass] = useState('dark');
let theme = localStorage.getItem('theme');
return (
<div className="container--toggle">
<input type="checkbox" id="toggle" className="toggle--checkbox" onClick={handleOnClick} />
<label htmlFor="toggle" className="toggle--label">
<span className="toggle--label-background"></span>
</label>
</div>
)
}
export default Toggle;
当用户点击切换按钮时,我希望页面主题和切换按钮都随之改变。我添加了导入的setTheme()函数,并将setTogClass()本地状态中的值传递给了 handleOnClick 函数。您可以在上面的 JSX 代码中看到它是如何传递给切换按钮的可点击部分的。
const handleOnClick = () => {
if (localStorage.getItem('theme') === 'theme-dark') {
setTheme('theme-light');
setTogClass('light')
} else {
setTheme('theme-dark');
setTogClass('dark')
}
}
我使用此组件useEffect()来确保本地状态 togClass 始终加载正确的主题。
useEffect(() => {
if (localStorage.getItem('theme') === 'theme-dark') {
setTogClass('dark')
} else if (localStorage.getItem('theme') === 'theme-light') {
setTogClass('light')
}
}, [theme])
因为我的切换按钮是一个复选框,所以深色主题应该显示未选中状态(月亮),浅色主题应该显示选中状态(太阳)。但我没能实现defaultChecked预期效果,所以我<input>用这个条件渲染三元运算符(条件运算符)替换了未选中状态:
{
togClass === "light" ?
<input type="checkbox" id="toggle" className="toggle--checkbox" onClick={handleOnClick} checked />
:
<input type="checkbox" id="toggle" className="toggle--checkbox" onClick={handleOnClick} />
}
如果你使用 `<div>` 标签<button>,你可以像这样轻松地使用条件渲染来更改标签内的 className 属性<button>,并获得相同的效果。
综合所有代码,切换组件的代码如下所示:
import React, { useEffect, useState } from 'react';
import '../styles/toggle.css';
import { setTheme } from '../utils/themes';
function Toggle() {
const [togClass, setTogClass] = useState('dark');
let theme = localStorage.getItem('theme');
const handleOnClick = () => {
if (localStorage.getItem('theme') === 'theme-dark') {
setTheme('theme-light');
setTogClass('light')
} else {
setTheme('theme-dark');
setTogClass('dark')
}
}
useEffect(() => {
if (localStorage.getItem('theme') === 'theme-dark') {
setTogClass('dark')
} else if (localStorage.getItem('theme') === 'theme-light') {
setTogClass('light')
}
}, [theme])
return (
<div className="container--toggle">
{
togClass === "light" ?
<input type="checkbox" id="toggle" className="toggle--checkbox" onClick={handleOnClick} checked />
:
<input type="checkbox" id="toggle" className="toggle--checkbox" onClick={handleOnClick} />
}
<label htmlFor="toggle" className="toggle--label">
<span className="toggle--label-background"></span>
</label>
</div>
)
}
更新
要了解我是如何重构此组件的逻辑并使其易于访问的,请阅读React 中的无障碍深色模式切换。
最后,我最喜欢的部分:SVG颜色切换!CSS变量在SVG代码中也能用!
我从DEVICON获取了 GitHub 和 Chrome 图标的 SVG 代码。对于 GitHub 图标,我只需要修改一个 `fill` 属性<g>:
<g fill="var(--dark-text)">
Chrome 图标在 a<circle>和 a中都有一个 fill 属性<path>:
<circle fill="var(--dark-text)" cx="63.624" cy="64.474" r="22.634"></circle><path fill="var(--dark-text)" ...>
结果如下:
结论
我尽量包含了所有相关的代码,但你也可以在我的GitHub 仓库中查看网站的完整代码。如果你喜欢这篇文章或者有任何疑问,请留言!我也很期待看到你根据本教程创建的作品。
文章来源:https://dev.to/abbeyperini/toggle-dark-mode-in-react-28c9