设计一个主题切换架构

各大应用网站开始支持深色模式。那么对于前端来说,如何高效的支持深色模式呢?这里的高效指的是工程化、自动化。

PostCss原理和相关插件能力

简单来说,PostCss是一款编译Css的工具。PostCss具有良好的插件性,非常利于开发者进行扩展。PostCss工作原理:PostCss接收一个CSS文件,并提供插件机制,提供给开发者分析、修改Css规则的能力,具体实现方式也是基于AST技术实现的

架构思路

对于主题切换,社区介绍的往往是通过CSS变量来实现的。那么如果是作为架构的话,其实这只是其中的一个环节,我们还应该思考以下几个问题:

  • 如何维护不同主题色值?
  • 谁来维护不同主题色值?
  • 在研发和设计之间,如何保持不同主题色值的同步沟通?
  • 如何最小化前端工程师的开发量,让他们不必硬编码两份色值?
  • 如何使一键切换时的性能最优?
  • 如何配合JavaScript状态管理,同步主题切换的信号?

我们以一个超链接样式为例,希望在开发时编写以下代码:

1
2
3
a {
color: cc(GBK05A)
}

这样就能一劳永逸直接支持两种主题模式,也就是说在应用编译时,上述代码将会被编译为下面这样:

1
2
3
4
5
6
a {
color: #646464
}
html[data-theme='dark'] a {
color: #808080
}

上述代码在构建阶段完成了以下操作:

  • cc(GBK05A)这样的声明被编译为#646464。CC是一个函数,而GBK054是一组色值包含了浅色和深色两种主题模式中的眼颜色。
  • 在HTML根节点上,添加属性选择器data-theme=’dark’,并添加a标签,color色值样式为#808080。

当构建完成后,用户点击切换主题按钮的时候,首先通过js脚本向HTML根节点标签内添加data-theme=’dark’的属性值。这时CSS选择器html[data-theme=’dark’]a将发挥作用,实现样式切换。

上面的逻辑很简单,那么我们回到架构设计中来,如何在构建阶段实现CSS样式编译转换呢?那就要依托于PostCss啦~

  • 编写一个名字为postcss-theme-colors的PostCss插件,实现上述编译过程。
  • 维护一个色值,这里用YML格式为例子,配置如下:
    1
    2
    3
    4
    GBK05A: [BK05, BK06]

    BK05: '#808080'
    BK06: '#999999'

postcss-theme-colors需要完成以下操作:

  • 识别cc函数。
  • 读取色组配置。
  • 通过色值对cc函数求值,得到两种颜色,分别对应浅色和深色两种主题模式。
  • 原地编译CSS中的颜色为浅色主题模式色值。
  • 将深色主题模式色值写到HTML根节点上。

为了将深色主题模式色值写到HTML根节点上,我们又用到了两个PostCss插件,它们分别是postcss-nestedpostcss-nesting