🌈 为你的网站增加点灵性:随系统变色

1,874 阅读7分钟

本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究!

🌻 前言

网站切换主题色已经是非常常见的功能了,提供浅色和暗色两种色调可以满足用户的使用习惯,帮助这些用户获得更好的访问体验。但是只能用户手动切换主题。

那如果用户已经将系统切换到了深色模式,当他们打开我们网站的时候,网站自动切换到深色模式,第一感觉就不会那么扎眼,是不是会有更好的第一印象,提高留存。

好了,铺垫到位了哈哈,引出本文将要讲的内容:实现网站跟随系统主题变色。

🎨 实现

实现的基础知识

css中有一个属性color-scheme,可以设置元素支持的系统配色方案,有以下四个值:

  • normal:使用浏览器的默认配色方案进行渲染;
  • light:使某元素支持使用操作系统浅色方案来渲染元素;
  • dark:使某元素支持使用操作系统深色配色方案进行渲染;
  • only:禁止用户代理覆盖元素的配色方案,必须搭配light或dark使用,例如 only dark 关键词表示禁用浅色模式,只应用深色模式;

简单来说,这个属性就是设置元素支持的系统配色方案,例如仅支持系统深色模式,注意是设置支持的值,而不是直接设置配色方案,他是会影响表单控件和滚动条等的颜色的,你可以简单在下面码上掘金上体验下效果:

与此相关的,有个媒体查询@media (prefers-color-scheme: light|dark)。它用于检测操作系统是浅色还是深色主题,从而设置针对不同的配色方案设置不同的样式。

很容易理解,例如下面代码:

:root{
  --bg-color: #f2f2f2;
}
div{
  background: var(--bg-color);
}
@media (prefers-color-scheme: light) {
    :root{
      --bg-color: #f2f2f2;
    }
}

@media (prefers-color-scheme: dark) {
    :root{
      --bg-color: #000;
    }
}

一个div的背景色由css变量--bg-color设定,那么我们就可以通过在@media prefers-color-scheme媒体查询中修改这个变量的值来修改页面样式。

由此,想必你应该想到了,那利用这个媒体查询不就能实现根据系统主题切换网页配色了吗?确实可以,但,未免有些粗糙!

问题

1.这样做切换网页配色,那网页只能跟随系统主题来变化了,但是用户对于系统主题色,一般都是设置一种之后基本就不会再变了,所以这样来实现切换网站主题色岂不是相当于多写了一套“闲置无用”的主题方案。

2.常规实现网站手动切换主题色有很多种方案,例如在根元素设置[theme="dark"]属性,通过切换其属性值来切换网站主题,而网站中的各种配色都以CSS变量形式存储在theme的属性选择器下。

这样的话属性选择器中的css变量和@media prefers-color-scheme中的css变量就会重合,就会根据书写顺序进行覆盖,那只能有一种切换网站主题的方案生效了。

而网站随系统变色,本身就属于附加功能,它和用户自由切换主题色相比,如果为了网站随系统变色而使手动切换主题色失效,那岂不是本末倒置了。

所以怎样才能使手动切换网站配色和随系统变色公共生效呢?
答案很简单,在网站配置了手动切换配色的功能后,只需要在网站加载时,通过 JS 查询系统主题,然后在代码中切换到对应的配色不就好了吗😎

关键代码

JS 中有一个 API:window.matchMedia(),它可以检测是否匹配到了某个媒体查询。

问题简单起来了,只需要利用window.matchMedia()检测@media prefers-color-scheme媒体查询,即可根据系统主题切换不同的网站配色了。代码大致如下:

// html
<div id="app" theme="light"></div>

// css
[theme="light"]{
  --bg: #f2f2f2;
}
[theme="dark"]{
  --bg: #000;
}
#app{
  background: var(--bg);
}

// js
const app = document.getElementById('app')
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
    // 切换到深色主题
    app.setAttribute('theme','dark')
} else {
    // 切换到浅色主题
    app.setAttribute('theme','light')
}

效果如下:

甚至你还可以监听颜色方案的变化,以便在用户改变系统颜色主题时跟随切换网站配色:

window.matchMedia('(prefers-color-scheme: dark)').addListener((e) => {
    if (e.matches) {
        app.setAttribute('theme','dark')
    } else {
       app.setAttribute('theme','light')
    }
});

tutieshi_640x287_4s.gif

又一些问题

在上一章讲color-scheme属性时,想必就有人想到了,是不是可以通过切换color-scheme: lightcolor-scheme: dark,配合@media prefers-color-scheme媒体查询写两套样式(浅色和深色的各写一套),然后也能实现手动切换网站主题?

但事实上并不能,因为color-scheme本质上只是设置支持的系统主题,而不是设置系统主题的,所以并不会影响@media prefers-color-scheme的匹配。说人话就是,即使你设置了color-scheme: only dark,但你的系统是浅色模式,@media (prefers-color-scheme:dark)里的代码也不会生效。

color-scheme有什么用呢?其实吧,这玩意确实没什么用,但是如果你的网站使用了很多系统控件,就可以用color-scheme来控制它们在系统深色模式下的颜色。

系统控件在浅色和深色模式下是有两套默认的配色的。你可以使用light-dark()函数分别设置它们在浅色和深色模式下的样式。

light-dark()函数用于检测是否设置了浅色或深色配色方案,使用方式很简单,用逗号分隔浅色和深色模式的值即可,如下:
background: light-dark(blue,red); //blue就是浅色模式下的值,red是深色模式的值

无论是手动切换系统主题,还是设置 color-scheme: only dark ,其实都可以影响到 light-dark()。但往往我们都做法是,全部设置color-scheme: light dark,然后利用light-dark()函数设置系统系统控件的样式。

注意: 我一直在说系统控件,不代表 light-dark() 只能作用于系统原生控件,只是它用来设置普通网页元素的配色方案的场景不多。

关于深色模式

深色模式可不只是切换一些配色这么简单,里面的学问也很多,例如为了确保可访问性和可读性,要注意选择颜色和调整一些板式:

深色模式的颜色不是简单的反转,例如白色背景变为黑色,黑色文本变为白色。显然,全黑背景上的纯白色文本使得阅读长段文本会导致眼睛疲劳。深色模式一般最好使用低对比度的颜色,例如使用稍微灰白色的文本和/或深灰色背景。降低对比度可以提高可读性,Lea Verou 的对比度计算器 这个工具可以计算颜色对比度,优化配色方案。

深色模式设计不应仅限于选择深色。还应该考虑改变排版样式,以保持深色模式可读性。例如深色模式下的浅色文本对比度会相对较高,可以考虑提高文本行高或字距离。

兼容性

兼容性方面,其实主要只需要考虑 prefers-color-scheme 的兼容性就好,还凑合,color-scheme也差不多,light-dark()相对兼容性较差,但是一般使用场景比较少。

image.png

🎁 最后

学如逆水行舟,不进则退~👊👊👊

先看后赞,养成习惯👍
收藏吃灰,不如学会🍗
点个关注,不要迷路🪤