我的网站早在 2018 年就实现了浅色和深色主题的切换,但是一直没有写过文章来阐述一下主流的实现方法,今天来就介绍一下各种深色主题实现的方法。
1.深色样式
想要实现网站主题的切换,一般有以下几种思路:
1.1 方法一:使用两个 css 文件
这种方法适用于网站主题样式变化较大的情况,为了便于维护样式文件分开成如 app.css 和 app-dark.css 这样的独立样式文件,然后通过 js 进行样式切换。这种方法的缺点也很明显,多加载了一个文件且切换时会有明显的样式变化,需要用样式过渡进行化解,例如 Mazer 项目就是这样实现的。
1.2 方法二:使用 Dark 属性覆盖
可以将浅色模式样式作为网站的默认样式,所有的深色样式都用 dark 属性:
.box { background-color: #fff; color: #000; } .box.dark { background-color: #000; color: #fff; }
只需在样式后面注入 dark 属性就行了:
<div class="box">浅色主题</div> <div class="box dark">深色主题</div>
这种切换方式不会出现某时刻丢失样式文件的问题,但是你需要指定哪些元素标签需要插入 dark 属性,然后遍历插入。
1.3 方法三:使用颜色注入
还可在 css 样式文件中不要指定颜色,用变量 var(--color)
等进行代替,然后再 :root 中单独指定变量的值,通过 js 注入对应的 root 到页面中:
/* 浅色 */ :root { --color-primary: #4154f1; --form-color: #fff; --color-nav-text-active: rgba(0, 98, 182, 1); --color-nav-text: rgba(0, 98, 182, 0.6); --icon-color: rgba(1, 41, 112, 0.5); --main-theme-color: #012970; --main-text-color: #444444; --box-shadow: 0 16px 56px #e7ebf6AA; } /* 深色 */ :root { --color-primary: #0062b6; --form-color: #fffefe; --color-nav-text-active: rgba(255, 255, 255, 1); --color-nav-text: rgba(255, 255, 255, 0.6); --icon-color: rgba(166, 180, 204, 0.9); --main-theme-color: #b5def9; --main-text-color: #efefef; --box-shadow: 0 16px 56px #000000AA; }
我的网站就是使用这种方法,因为这样可以让 :root 最先加载,不会出现 loading 加载样式慢的问题。
1.4 方法四:color-scheme
您可以更改在 :root 上设置的 color-scheme 的值,并查看网页如何响应:
- 设置为 light dark 时,表示相应元素同时支持浅色模式和深色模式。具体选择使用哪种值,取决于 prefers-color-scheme 媒体条件的值
- 设置为 light 时,表示该元素支持浅色配色方案
- 设置为 dark 时,表示此元素支持深色配色方案
See the Pen color-scheme: light dark; and System Colors by web.dev (@web-dot-dev) on CodePen.
2.样式切换判断
已经实现了深色主题的样式,那么什么事情情况下进行切换呢,一般有如下几种常用方案:
- 读取用户当地时间,每天设置固定时间例如:19:00 至次日 6:00 自动开启主题切换
- 读取用户当地日落日出时间,自动进行设置
- 读取用户系统主题颜色,根据系统颜色进行切换
2.1 通过 css 媒体查询
在 CSS 中可以使用利用媒体属性 preferred-color-scheme 查询:
@media (prefers-color-scheme: light) { .article { background:#fff; color: #000; } } @media (prefers-color-scheme: dark) { .article { background:#000; color: #fff; } } @media (prefers-color-scheme: no-preference) { .article { background:#fff; color: #000; } }
- no-preference 表示系统未得知用户在这方面的选项,在布尔值上下文中,其执行结果为 false
- light 表示用户已告知系统他们选择使用浅色主题的界面
- dark 表示用户已告知系统他们选择使用暗色主题的界面
2.2 通过 js 监听
上述方法仅能在网页加载时获取一次主题,若用户切换主题时,页面是无法实时更换主题的。 但是上述媒体查询结果(MediaQueryLis)对象支持 addListener 方法监听主题变化。 通过监听就可以解决主题切换实时更新的额问题了。
const themeMedia = window.matchMedia("(prefers-color-scheme: light)"); themeMedia.addListener(e => { if (e.matches) { } else { console.log('dark') } });
3.主题设置储存
当用户设定了主题之后,需要将选择主题信息进行保存,如果你有账户系统就可以将用户配置存储到用户数据表中。如果你的网站可以匿名访问,可以选择将主题样式存储到 localStorage 中:
const toggleButton = document.getElementById('toggle-theme'); const box = document.querySelector('.box'); toggleButton.addEventListener('click', () => { box.classList.toggle('dark'); // 将用户选择保存到 localStorage const isDarkMode = box.classList.contains('dark'); localStorage.setItem('darkMode', isDarkMode); }); // 页面加载时检查并应用用户上次选择 if (localStorage.getItem('darkMode') === 'true') { box.classList.add('dark'); }
一般情况下,用户不主动清除浏览器缓存,则 localStorage 会一直生效。你也可以用 js 进行清除:
// 清除某个特定的键 localStorage.removeItem('darkMode'); // 清除所有的 localStorage 数据 localStorage.clear();