随着IOS深色模式的普及,众多App支持了白天/深色主题。当然Web页面也可以做到。
方案
变色的核心,是色值和背景图的改变,以下是一些实现方案:
CSS滤镜 filter: brightness()
brightness() - CSS(层叠样式表) | MDN
懒人做法,可以通过改变<html>
标签即整个页面的输出亮度实现夜晚变暗的效果,值为0-1,0表示全黑。效果上其实就是加了个黑色带透明度的蒙层。
简单粗暴、效果单一,兼容性尚可。但是有个坑,即元素background
不能为none
或透明,否则brightness()
无效果。
效果:
JS变量
配置js全局变量存储颜色,写内联样式
1 | const theme = { |
1 | <p :style="{ color: theme.color }">text</p> |
优点: 无兼容性
缺点:除了写起来很麻烦,在处理图片时,为了兼容明暗两种图片,需要单独处理一次,工作量大且不易维护。不建议使用。
Sass/Less/Stylus等预编译器函数
通过预编译器变量和函数生成多套主题CSS,这里以Scss为例
定义Scss变量
1 | // variable.scss |
定义主题map
1 | // theme.scss |
mixin 方法生成多种主题样式
处理颜色
参数:
$type
颜色相关属性,默认为background-color
,可以是color
,border-color
$typeColor
色值,为主题模式色值map中的key,默认bg-default
$alpha
透明度,值为 0-1
1 | @mixin setThemes($type: background-color, $typeColor: 'bg-default', $alpha: 1) { |
使用:
1 | .header { |
最终生成:
1 | .theme-light .header { |
处理图片
图片也存在深色模式时,通过图片命名来区分:
icon-success-light.png
icon-success-dark.png
参数:
$imgUrl
图片地址前缀$imgType
图片格式后缀,默认png
1 | // 设置背景 |
使用:
1 | .header { |
最终生成:
1 | .theme-light .header { |
同理也可以处理box-shadow
。
特殊情况
当遇到无法处理的情况,如白天有box-shadow
,夜晚没有。这时候需要手动处理:
1 | .header { |
改变主题
通过改变根DOM的class来应用主题
1 | <div class="theme-light"> |
优缺点
优点: scss最终输出成css,通过该方法实现变色无兼容性问题,且切换主题方便,处理图片也毫不费力。
缺点: 但当主题非常多时,会生成很多主题样式代码,全部打到包里,造成样式代码冗余,即使用户并不用这些主题。
CSS变量
简单使用
1 | :root { |
CSS变量存储在类名下
1 | $theme-light: ( |
最终生成:
1 | .theme-light { |
支持可变背景图片
由于CSS变量不支持写路径,所以背景图片可变时,需要结合上面的Scss方案,使用上面提到的Scss的 setThemesBgImg
mixin方法
改变主题
所有变量都控制在了不同类名下,因此仅需更改类名即可:
1 | type ThemeName = 'light' | 'dark' |
优点:
通过js动态改变css变量的好处之一,就是可以通过服务端接口获取主题变量色值,通过后台可配置无限种主题,且不会产生冗余的CSS代码。
缺点:
- 不能支持可变背景图片,仍需scss方法支持图片
- 兼容性堪忧,尤其是国内的环境……
但是Apple官网用了。
结论
在主题不多时,如仅有白天&深色模式,适合用预编译器方案
;
在主题很多,且不考虑完美兼容性时(如国际C端),用 css变量写颜色 + Scss方法写背景图;
在主题很多且考虑兼容性时(如国内C端),使用预编译器方案
写样式。再开发一个webpack打包插件把单个主题样式代码抽离出来打包成单文件,切换主题时动态加载样式文件。