该 demo 基于:
react:17.0.2
less:4.1.1(如果使用了 less)
1. 动态增加标签自定义属性(html,body......)
app.tsx
import "./styles.less";
export default function App() {
// 切换主题
const changeTheme = (type: string) => {
const prefersDarkMode = window.matchMedia("(prefers-color-scheme: dark)")
.matches; // 获取系统是否是暗黑模式
if (type === "auto") {
document.documentElement.setAttribute(
"data-prefers-color",
prefersDarkMode ? "dark" : "light"
);
} else {
document.documentElement.setAttribute("data-prefers-color", type);
}
};
return (
<div className="App">
<h1>Dark Light Demo</h1>
<div>
<button
className="btn"
onClick={() => {
changeTheme("dark");
}}
>
change Dark
</button>
</div>
<div>
<button
className="btn"
onClick={() => {
changeTheme("light");
}}
>
change Light
</button>
</div>
<div>
<button
className="btn"
onClick={() => {
changeTheme("auto");
}}
>
change Auto
</button>
</div>
</div>
);
}
var.less
@c-bg: #fff;
@c-bg-dark: #141414;
@c-text: #141414;
@c-text-dark: rgba(255, 255, 255, 0.65);
@c-border: #d9d9d9;
@c-border-dark: #434343;
styles.less
根据属性修改对应样式 html[data-prefers-color="dark"]
@import "./var.less";
body {
background: @c-bg;
[data-prefers-color="dark"] & {
background: @c-bg-dark;
}
}
.App {
font-family: sans-serif;
text-align: center;
color: @c-text;
[data-prefers-color="dark"] & {
color: @c-text-dark;
}
}
.btn {
margin-top: 10px;
padding: 4px 15px;
font-size: 14px;
border-radius: 2px;
color: rgba(0, 0, 0, 0.85);
color: @c-text;
border: 1px solid @c-border;
user-select: none;
cursor: pointer;
background: @c-bg;
[data-prefers-color="dark"] & {
color: @c-text-dark;
border: 1px solid @c-border-dark;
background: @c-bg-dark;
}
}
2. 使用CSS自定义属性(变量)
css 自定义属性介绍:developer.mozilla.org/zh-CN/docs/…
app.tsx
// 将 less 替换为 css,其余代码保持一致。
- import "./styles.less";
+ import "./styles.css";
...
styles.css
html {
--c-bg: #fff;
--c-text: #141414;
--c-border: #d9d9d9;
}
html[data-prefers-color="dark"] {
--c-bg: #141414;
--c-text: rgba(255, 255, 255, 0.65);
--c-border: #434343;
}
body {
background: var(--c-bg);
}
.App {
font-family: sans-serif;
text-align: center;
color: var(--c-text);
}
.btn {
margin-top: 10px;
padding: 4px 15px;
font-size: 14px;
border-radius: 2px;
color: rgba(0, 0, 0, 0.85);
color: var(--c-text);
border: 1px solid var(--c-border);
user-select: none;
cursor: pointer;
background: var(--c-bg);
}
还可以通过 js 来修改: document.documentElement.style.setProperty(‘--themeColor‘, '#333');
3. 动态修改 <link>
假设有存在以下主题目录:
theme
├── dark.css
├── light.css
├── blue.css
├── red.css
└── yellow.css
onClick = (theme) => {
let styleLink = document.getElementById('theme-style');
if (styleLink) {
// 假如存在id为theme-style 的link标签,直接修改其href
styleLink.href = `/theme/${theme}.css`; // 切换主题
}
} else {
// 不存在的话,则新建一个
styleLink = document.createElement('link');
styleLink.type = 'text/css';
styleLink.rel = 'stylesheet';
styleLink.id = 'theme-style';
styleLink.href = '/theme/light.css';
}
document.body.append(styleLink);
}
};
4. 使用 less.modifyVars()
color.less
@c-bg: #fff;
@c-text: #141414;
@c-border: #d9d9d9;
index.html
<!DOCTYPE html>
<html lang="en">
...
<head>
<link rel="stylesheet/less" type="text/css" href="/color.less" />
</head>
...
</html>
app.tsx
import "./styles.less";
const loadScript = (src: string) => {
return new Promise((resolve, reject) => {
const script = document.createElement("script");
script.type = "text/javascript";
script.src = src;
script.onload = resolve;
script.onerror = reject;
document.head!.appendChild(script);
});
};
let lessLoaded = false;
export default function App() {
// 切换主题
const changeTheme = (type: string) => {
// 这里就没有去完善根据类型自动配置对应主题了,可自行添加
// const prefersDarkMode = window.matchMedia("(prefers-color-scheme: dark)")
// .matches;
const changeColor = () => {
(window as any).less
.modifyVars({
"@c-bg": "#141414",
"@c-text": "rgba(255, 255, 255, 0.65)",
"@c-border": "#434343"
})
.then((res) => {
alert("修改主题成功!");
});
};
const lessUrl =
"https://gw.alipayobjects.com/os/lib/less/3.10.3/dist/less.min.js";
if (lessLoaded) {
changeColor();
} else {
(window as any).less = {
async: true,
javascriptEnabled: true
};
loadScript(lessUrl).then(() => {
lessLoaded = true;
changeColor();
});
}
};
return (
// ... 同上 ...
);
}
- 以上方式都没有进行初始化,如需初始化,请自行调用切换主题函数。
- 上述方式各有利弊,酌情选择,若有更优解求分享。🤣🤣🤣