利用CSS变量进行黑暗模式切换
本文以react为例。
用css变量来切换黑暗模式,易于维护和扩展。
css变量的用法:
.selector {
--black-color: #282c34;
}
:root {
--black-color: #282c34;
}方案一
设置主题对应的CSS变量,切换主题只需切换css属性的变量值。例如:切换APP元素的主题只需切换App的color和background-color对应CSS变量的变量值即可。
:root {
/* 模式切换变量,默认light模式 */
--current-background-color: var(--light-background-color);
--current-primary-color: var(--light-primary-color);
/* 浅色主题 */
--light-primary-color: #666;
--light-background-color: #fff;
/* 深色主题 */
--dark-primary-color: #fff;
--dark-background-color: #282c34;
}
.App {
color: var(--current-primary-color);
background-color: var(--current-background-color);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
transition: background-color 0.3s;
}那么如何切换:root 下--current-background-color的值?
1. 查找它
2. 替换它查它
// 找到:root下所有定义以--current变量
const currentCssVar = Array.from(document.styleSheets).reduce(
(acc, sheet) =>
(acc = [
...acc,
...Array.from(sheet.cssRules).reduce(
(def, rule) =>
(def =
rule.selectorText === ":root"
? [
...def,
...Array.from(rule.style).filter((name) =>
name.startsWith("--current")
),
]
: def),
[]
),
]),
[]
);换它
currentCssVar.forEach((item) => {
document.documentElement.style.setProperty(
item,
`var(--${themeName}${item.substring(9)})`
);
});完整JS代码:
import React, { useEffect, useState } from "react";
import Project from "@bit/toringo.comp.product-list";
import Switch from "@bit/campgladiator.cgui.components.atoms.switch";
import "./App.css";
import setTheme from "./util";
// 默认主题可来源与server、storage等。
const defaultTheme = 'light';
function App() {
const [mode, setMode] = useState(defaultTheme);
useEffect(() => {
setTheme(mode);
}, [mode]);
return (
<div className="App">
<Switch
onClick={() => setMode(mode === "dark" ? "light" : "dark")}
/>
<Project list={["Hulk", "Stack", "Link"]} />
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</div>
);
}
export default App;方案二
设置CSS变量,定义theme对应的CSS class选择器,动态去改变className已达到主题切换。
.App {
/* color: var(--current-primary-color);
background-color: var(--current-background-color); */
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
transition: background-color 0.3s;
}
.App-light {
color: var(--light-primary-color);
background-color: var(--light-background-color);
}
.App-dark {
color: var(--dark-primary-color);
background-color: var(--dark-background-color);
}JS代码:
...
<div className={`App ${mode === "dark" ? "App-dark" : "App-light"}`}>
.... 方案三
利用css媒体查询动态改变网页主题样式,当浏览器的主体发生变化时, 媒体查询的prefers-color-scheme会动态执行匹配的规则,
@media (prefers-color-scheme: dark) {
:root {
/* 浅色主题 */
--current-background-color: #282c34;
--current-primary-color: #fff;
}
}
@media (prefers-color-scheme: light) {
:root {
/* 深色主题 */
--current-background-color: #fff;
--current-primary-color: #282c34;
}
}
.App {
color: var(--current-primary-color);
background-color: var(--current-background-color);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
transition: background-color 0.3s;
}方案四
利用JS事件监听媒体查询动态 改变网页主题样式,Web Animation API)还提供给了监听css媒体查询条件的匹配。
useEffect(() => {
const mediaQuery = window.matchMedia("(prefers-color-scheme: light)");
const setFn = () => {
setMode(mediaQuery.matches ? "light" : "dark");
};
mediaQuery.addEventListener("change", setFn);
return () => {
mediaQuery.removeListener("change", setFn);
};
}, []);本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!