利用CSS變數進行黑暗模式切換
阿新 • • 發佈:2020-10-14
本文以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;
}
電腦刺繡繡花廠 http://www.szhdn.com 廣州品牌設計公司https://www.houdianzi.com
方案四
利用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);
};
}, []);