使用 PostCSS 插件让你的网站支持暗黑模式

2022-03-29 19:19:00 浏览数 (1)

最近公司需要给多个 webapp(大概 20 )加上多皮肤的功能,原先默认是白色皮肤,我们先从暗黑模式入手,从而逐渐实现多皮肤功能。本篇记录下实现思路。

换肤方案

css variables

css variables 是 Web 标准实现了对深色模式的支持, 以下代码通过 CSS 媒体查询,最简单的实现。

代码语言:javascript复制
:root {
  color-scheme: light dark;
  background: white;
  color: black;
}

@media (prefers-color-scheme: dark) {
  :root {
    background: black;
    color: white;
  }
}

颜色较多的情况下,使用 css variables

代码语言:javascript复制
:root {
  color-scheme: light dark;
  --nav-bg-color: #f7f7f7;
  --content-bg-color: #ffffff;
  --font-color: rgba(0, 0, 0, 0.9);
}

@media (prefers-color-scheme: dark) {
  :root {
    --nav-bg-color: #2f2f2f;
    --content-bg-color: #2c2c2c;
    --font-color: rgba(255, 255, 255, 0.8);
  }
}

:root {
  color: var(--font-color);
}

.header {
  background-color: var(--nav-bg-color);
}

.content {
  background-color: var(--content-bg-color);
}

优点:代码量最少,实现起来方便;

缺点:存在浏览器兼容性,需要 edge16 才支持,老项目实现起来, 需要重构 css, 所以对我司来说就不适用了,如果是新的 webapp,我会毫不犹豫的选择这种方式。

less 在线编译

这种方案最典型的例子是 https://antdtheme.com/ ,通过less modifyVars方法 启用对较少变量的运行时修改。使用新值调用时,将重新编译较少的文件,而无需重新加载。

代码语言:javascript复制
<script src="less.js"></script>
<script>
  less.modifyVars({ '@text-color': '#fff', '@bg-color': '#000' })
</script>

那如果要修改的颜色变量过多,或者样式文件过多,就会造成切换的时候卡顿。

打包多份 css

当然也可以手动打包 2 份 css 样式

代码语言:javascript复制
var less = require("less");
var fs = require("fs");

fs.readFile("./index.less", "utf-8", (err, str) => {
  less.render(
    str,
    {
      paths: [".", "./componnents"], //  为 @import指令指定搜索路径
      compress: true, // 压缩
      modifyVars: {
        "@text-color": "#fff",
        "@bg-color": "#000",
      },
    },
    function (e, output) {
      console.log(output.css);
    }
  );
});

然后就可以通过动态插入 css 的方式进行换肤了

代码语言:javascript复制
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)
}

这种方式存在一个问题,当点击切换的时候会引起整个页面重排,因此我们需要单独打包出只包含颜色的样式文件。从这个思路出发,我们就接触到了 postcss.

PostCSS

PostCSS 核心包含一个解析器,该解析器生成一个 CSS AST (抽象语法树) ,这是一个解析 CSS 字符串的节点树的表示。当我们在 CSS 抽象语法树中修改一些内容后,PostCSS 将语法树(AST)生成回 CSS 字符串。

核心就是 编译->转换-->生成 是不是跟 babel 相似呢?

大家都知道 https://astexplorer.net/ 这个网站,可以用来写 babel 插件,不知道是否使用过其他解析器?这边选择 CSS 和 postcss 这样就可以将 css 解析成 CSS AST (抽象语法树)了。

目的

当前我有一份 less 样式和 2 份颜色变量,我需要生成如下样式:

这样我就可以在 html 跟节点 添加和删除 dark 这个样式来实现换肤了。

或许有同学会问,这里怎么突然变成 less 了?PostCSS 能解析 Less 吗? 答案是不能。 当前假设我们的 webapp 是基于 webpack 构建的。

代码语言:javascript复制
module: {
  rules: [
    //...
    {
      test: /.less$/i,
      use: ['style-loader', 'css-loader', 'postcss-loader', 'less-loader'],
    },
    //...
  ]
}

上面的 loader 的执行顺序是 自右向左

0 人点赞