CSS | 使用 PostCSS 插件让 Web 应用支持暗黑模式
1 概述
通過使用 CSS 變量,可以實現暗黑模式。比如使用媒體查詢器:
:root {color-scheme: light dark;background: white;color: black; }@media (prefers-color-scheme: dark) {:root {background: black;color: white;} }如果有很多顏色需要處理,此時就要用到 CSS 變量。
:root {color-scheme: light dark;--nav-bg-color: #F7F7F7;--content-bg-color: #FFFFFF;--font-color: rgba(0,0,0,.9); }@media (prefers-color-scheme: dark) {:root {--nav-bg-color: #2F2F2F;--content-bg-color: #2C2C2C;--font-color: rgba(255, 255, 255, .8);} }:root {color: var(--font-color) }.header {background-color: var(--nav-bg-color); }.content {background-color: var(--content-bg-color); }可以看到這種辦法代碼簡單易懂,問題是var關鍵字不兼容 IE 瀏覽器。
還有一個辦法,就是使用 less.js 實時編譯 CSS 代碼,比如:
使用 less 的缺點是,當點擊切換按鈕時,會導致卡頓。
當然,也可以通過DOM動態更新 CSS 代碼。
function changeTheme(theme) {const styleCss = document.querySelector("#styleCss");if (styleCss) {styleCss.href = `/assets/css/${theme}.css`;} else {const head = document.getElementsByTagName("head")[0];const link = document.createElement("link");link.id = "styleCss";link.type = "text/css";link.rel = "stylesheet";link.dataset.type = "theme";link.href = `/assets/css/${theme}.css`;head.appendChild(link); }localStorage.setItem("theme", theme); }使用 DOM 動態更改的辦法,需要把顏色單獨作成樣式文件,導致配置繁瑣。
2 什么是 PostCSS ?
PostCSS 核心包含一個生成 CSS 抽象語法樹的AST 解析器,能將 CSS 代碼抽象成一個節點樹。如果代碼發生更改,節點樹也會更新。
核心過程是 解析 => 轉換 => 生成,類似 Babel。形式如下圖所示:
3 舉個例子
比如下面的 .less 代碼:
我們要將其轉換為:
Wepack 的配置如下:
3.1 編寫 PostCSS 插件
我們可以使用 postcss-plugin-boilerplate腳手架,這個腳手架可以創建一個 postcss-plugin,并且還可以配置jest單元測試。流程圖如下:
當然,也可以在項目根目錄下手動創建一個 PostCSS 插件。
// test-plugin.js var postcss = require("postcss");module.exports = postcss.plugin("pluginname", function (opts) {opts = opts || {}; // plugin 參數return function (root, result) {// Transform the CSS AST}; });然后,將其放入 postcss.config.js 配置文件中。
module.exports = {plugins: [require('./test-plugin'),require('autoprefixer')] };編寫代碼,完成插件功能。
var postcss = require("postcss");module.exports = postcss.plugin("postcss-backwards", function (opts) {opts = opts || {};return function (root, result) {// Iterate over all style nodesroot.walkDecls((declaration) => {declaration.value = declaration.value.split("").reverse().join("");});}; });這是一個沒有實際意義的插件,僅作教學使用。
3.2 如何使用 PostCSS 實現暗黑模式支持 ?
將less文件導入項目。
import "./default-theme.less"; import "./dark-theme.less";component.less
.box{width: 100px;height: 100px;border: 1px solid @border;background-color: @bg;color: @color; }default-theme.less
@import "./component";@border: #333; @color: #000; @bg: #fff;dark-theme.less
@import "./component";@border: #999; @color: #fff; @bg: #000;轉換模塊
function isEmpty(arr) {return Array.isArray(arr) && arr.length === 0; }const hasColorProp = (colorProps, declProp) =>colorProps.some((prop) => declProp.includes(prop));module.exports = (opts = {}) => {if (!opts.colorProps) {opts.colorProps = ["color", "background", "border", "box-shadow", "stroke"];}return (root) => {let theme;const file = root.source.input.file || "";const matched = file.match(/(?<theme>[a-zA-Z0-9]+)-theme.(less|css|scss|sass)/);if (matched && matched.groups.theme !== "default") {theme = matched.groups.theme;} else {if (process.env.NODE_ENV == "test") {theme = "test";}}if (theme) {root.walkRules((rule) => {rule.walkDecls((decl) => {if (!hasColorProp(opts.colorProps, decl.prop)) {decl.remove();}});if (isEmpty(rule.nodes)) {rule.remove();} else {rule.selector = rule.selector.replace(/\n/g, "").split(",").map((s) => `.${theme} ${s}`).join(",\n");}});}}; };4 實施步驟
4.1 通過文件名來確定是否需要生成皮膚樣式
const file = root.source.input.file || "";const matched = file.match(/(?<theme>[a-zA-Z0-9]+)-theme.(less|css|scss|sass)/ );4.2 刪除不包含顏色的樣式
只保留邊框顏色、背景顏色和包含顏色的 CSS 屬性。并且,刪除不包含 CSS 屬性的選擇器。
["color", "background","border","box-shadow","stroke",]4.3 配置規則
module.exports = [{prop: ["background-color", "background"],from: ["#fff", "#ffffff", "@white"],to: "@component-background",},{prop: ["border", "border-color"],from: ["#D3D9E4", "#D3D9E2"],to: "@border-color",},{prop: ["color"],from: ["#666E79", "#5C6268"],to: "@text-color",} ];4.4 執行轉化
const syntax = require("postcss-less"); var fs = require("fs"); const path = require("path"); const rules = require("./rule.js");var glob = require("glob");function log(file, node, to) {console.log("\x1b[32m",`convert ${file} ${node.source.start.line}:${node.source.start.column} ${node.parent.selector} ${node.prop} from ${node.value} to ${to}`); }let codes = {};// options is optional glob("./src/**/*.less", function (er, files) { files.forEach((file) => {var ast = syntax.parse(file);// traverse AST and modify itast.walkDecls(function (node) {rules.forEach((item) => {if (item.prop.includes(node.prop) && item.from.includes(node.value)) {node.value = item.to;log(file, node, item.to);}});});fs.writeFileSync(path.resolve(file), syntax.nodeToString(ast));}); });總結
以上是生活随笔為你收集整理的CSS | 使用 PostCSS 插件让 Web 应用支持暗黑模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python3.8使用aiml总结
- 下一篇: 密码生成 算法编程题