前言
暗黑模式这个概念最早起源于MacOS系统的Mojave,提供浅色主题和深色主题两种皮肤供用户选择,深色主题就是我们常说的暗黑模式。为了眼睛健康,笔者在手机、平板和电脑上都选择了暗黑模式。
主题皮肤
随着苹果官方逐渐要求各大系统平台都得适配暗黑模式,所以笔者也探索出一种应该是市面上最低成本的网站暗黑模式适配方案。
很多同学可能觉得这个方案只能使用JS实现,其实可使用纯CSS实现。
思路
思路很简单,使用一个按钮来回切换主题样式。按钮未选中则切换到浅色主题,按钮选中则切换到深色主题。可用:checked和+/~打辅助完成这个任务。
- :checked:选项选中的表单元素
- +:元素相邻的同胞元素
- ~:元素后面的同胞元素
使用模拟按钮,当按钮处于选中状态时触发:checked,通过+/~带动后面相邻的网站主体
<body>
<input class="ios-switch" type="checkbox">
<div class="main">网站主体</div>
</body>
更多选择器的功能和分类请回看笔者这篇文章《可能是最全最易记的CSS选择器分类大法》。
切换按钮
打算设计一个美观的按钮,可是没有特别思路,就打开iPhone,把设置里的切换按钮用纯CSS实现一番。
尺寸和颜色都是与iPhone切换按钮一致。思路是使用模拟按钮,声明appearance:none将其默认外观抹去,使用::before模拟背景区域,使用::after模拟点击区域,在触发:checked后添加一些小动画进行修饰,近乎完美地实现了iPhone切换按钮。
切换按钮
<input class="ios-switch" type="checkbox">
.btn {
border-radius: 31px;
width: 102px;
height: 62px;
background-color: #e9e9eb;
}
.ios-switch {
position: relative;
appearance: none;
cursor: pointer;
transition: all 100ms;
@extend .btn;
&::before {
position: absolute;
content: "";
transition: all 300ms cubic-bezier(.45, 1, .4, 1);
@extend .btn;
}
&::after {
position: absolute;
left: 4px;
top: 4px;
border-radius: 27px;
width: 54px;
height: 54px;
background-color: #fff;
box-shadow: 1px 1px 5px rgba(#000, .3);
content: "";
transition: all 300ms cubic-bezier(.4, .4, .25, 1.35);
}
&:checked {
background-color: #5eb662;
&::before {
transform: scale(0);
}
&::after {
transform: translateX(40px);
}
}
}
请戳这里👇查看在线演示与源码。(https://codepen.io/JowayYoung/pen/KKzgWaz)
暗黑模式
还记得4月4日那次全网开启悼念模式吗?笔者发表了一篇文章《一行代码全站进入悼念模式》,巧妙地使用filter这个强大的CSS属性。
html {
filter:grayscale(1);
}
真的是一行代码,本次也不例外,一行代码全站进入暗黑模式。
html {
filter: invert(1) hue-rotate(180deg);
}
filter的兼容性不差,各位同学可放心使用,还有一些细节地方需注意,本文就不重复讲解了,详情可回看《一行代码全站进入悼念模式》。
filter是一个非常神奇的属性,能将Photoshop一些基础的滤镜效果应用到网站上。笔者平时非常喜欢使用filter,在笔者的CodePen上有许多纯CSS特效都使用了filter,细心的同学可能会发现笔者特别喜欢使用hue-rotate()这个函数结合CSS变量动态生成过渡颜色,详情请回看《妙用CSS变量,让你的CSS变得更心动》。
本次的暗黑模式使用到两个滤镜函数:invert()、hue-rotate()。
- invert():反相,反向输出图像着色,值为0%则无变化,值为0~100%则是线性乘子效果,值为100%则完全反转
- hue-rotate():色相旋转,减弱图像着色,处理非黑白的颜色,值为0deg则无变化,值为0~360deg则逐渐减弱,值超过360deg则相当绕N圈再计算剩余的值
invert()简单理解就是黑变白,白变黑,黑白颠倒。hue-rotate()简单理解就是冲淡颜色。为了确保主题色调不会改变,将色相旋转声明为180deg比较合理。
依据上述分析的思路,当按钮处于选中状态时使用+/~连带后面的同胞元素也进入选中状态。若同胞元素无背景色需声明background-color:#fff,否则无法让滤镜效果起效,为了让这个同胞元素在使用滤镜效果时过渡得更自然,声明transition:all 300ms。
.ios-switch {
...
&:checked {
...
& + .main {
filter: invert(1) hue-rotate(180deg);
}
}
}
.main {
background-color: #fff;
transition: all 300ms;
}
在CodePen上为了更好地展示效果,就使用
优化
细心的同学可能会发现,怎么图片都变成照B超的感觉了。
缺陷
按照设计原则来说,换肤只针对组件,像一些媒体类型的元素,例如背景、图片、视频等,都是不能直接处理的,需保持其原样。既然暗黑模式是使用了滤镜的反相和色相旋转实现,那么对这些媒体元素再次使用滤镜的反相和色相旋转就能复原了。使用过Photoshop滤镜的同学应该会更清楚。
img,
video {
filter: invert(1) hue-rotate(180deg);
}
还有一个问题,背景怎样处理?众所周知,背景是使用background系列属性进行声明的,因此无法通过特定的选择器进行标注。但是,可换种思路处理,就是给有背景的元素加上一个特定类名,将其包含到上述规则里即可。
通过Chrome DevTools查看搜狐门户的网站源码,发现这些头像、缩略图和展示图都有一些特定类名,将其特定类名添加到规则里。
img,
video,
.logo,
.icon,
.jubao-con {
filter: invert(1) hue-rotate(180deg);
}
在通用网站里,这个类名可自行定义,最可行的方法就是定义一个特定类名.exclude。不使用滤镜效果的元素统统加上.exclude。
.exclude {
filter: invert(1) hue-rotate(180deg);
}
改造
上述为了方便演示代码,在CodePen Demo里使用
<body>
<header class="sohu-head"></header>
<div class="sohu-ph" id="sohuTopc" style="display:none;"></div>
<div class="top-box"></div>
<div class="wrapper-box"></div>
...
</body>
往
里插入切换按钮。<body>
<input class="ios-switch" type="checkbox">
<header class="sohu-head"></header>
<div class="sohu-ph" id="sohuTopc" style="display:none;"></div>
<div class="top-box"></div>
<div class="wrapper-box"></div>
...
</body>
把以下SCSS代码转换成CSS代码插入到
新建的