深色模式应该是ios13
出来之后逐渐推广开来的,作为个小小果粉出来以后也是一直在用,以至于现在每次用百度搜索都会觉得眼睛不是很舒服,它为什么不学学谷歌跟进一下呢!
最近我们的应用也要做深色模式了,当然这是一个大工程,需要先预研了一下技术方案,做个Demo
出来,于是乎就有了这篇文章。
需求
首先应用适配深色模式,另外支持用户自定义设置深色、浅色模式和自动模式(跟随系统设置,如果系统是深色设置那么应用也设置为深色模式,浅色也同步设置为浅色模式)。
技术选型
核心技术是首先使用CSS自定义属性(--*)
来声明属性变量,然后通过CSS函数var()
在合适的元素选择器重使用前面声明的属性变量
- CSS自定义属性:属性名必须以
--
开头,属性值无特殊要求,自定义属性一样有优先级 - CSS函数:使用
var(变量名)
获取自定义属性中定义的属性值
比如下面的例子,.box2
中的标题会渲染红色,而.box1
中的标题会渲染蓝色,这是因为css自定义属性的声明也是有优先级的,优先级更高的属性值会生效。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>demo</title>
<style>
:root{
--primary-text-color: red;
}
.box1 {
--primary-text-color: blue;
}
h1 {
color: var(--primary-text-color);
}
</style>
</head>
<body>
<div class="box1">
<h1>box1中的标题</h1>
</div>
<div class="box2">
<h1>box2中的标题</h1>
</div>
</body>
</html>
兼容性
我们的是移动端业务只考虑大多数移动端设备的兼容性,chrome>=49,safari>=9.3
,基本满足绝大多数设备的需求,当然针对那一小部分不兼容的设备也要考虑使用css降级去兼容他们,后面会提到。
实现内容
-
在页面根元素中定义深色模式和浅色模式的属性变量
-
利用css会覆盖来实现css降级
:root { --color-text: #333333; --color-background: #ffffff; --color-background-light: #ccc; } :root[data-prefers-color-scheme="dark"] { --color-text: #e6e6e6; --color-background: #000000; --color-background-light: #202124; } html { background-color: var(--color-background); } h1 { color: #333333; background-color: #ffffff; color: var(--color-text); background-color: var(--color-background-light); }
-
使用媒体查询检测用户是否有将系统的主题色设置为亮色或者暗色
matchDarkMedia = window.matchMedia("(prefers-color-scheme:dark)"); setThemeColor(matchDarkMedia.matches ? THEME.DARK : THEME.LIGHT)
-
使用localstorage缓存用户选择的设置,下次打开页面优先使用用户自定义设置
完整demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>theme</title>
<style>
:root {
--color-text: #333333;
--color-background: #ffffff;
--color-background-light: #ccc;
}
:root[data-prefers-color-scheme="dark"] {
--color-text: #e6e6e6;
--color-background: #000000;
--color-background-light: #202124;
}
html {
background-color: var(--color-background);
}
h1 {
color: #333333;
background-color: #ffffff;
color: var(--color-text);
background-color: var(--color-background-light);
}
</style>
</head>
<body>
<h1>试试切换颜色主题吧!</h1>
<select name="theme" id="selector" onchange="handleSelectChange()">
<option value="auto">auto</option>
<option value="dark">dark</option>
<option value="light">light</option>
</select>
<script>
const THEME = {
AUTO: 'auto',
DARK: 'dark',
LIGHT: 'light',
}
const LS_KEY_FOR_THEME_SETTING = 'theme-setting'
const THEME_ATTRIBUTE_NAME = 'data-prefers-color-scheme'
let matchDarkMedia
let darkMediaListener
window.onload = mounted
function mounted() {
const cachedTheme = getCachedTheme() || THEME.AUTO
const selector = document.getElementById('selector')
selector.value = THEME[cachedTheme.toUpperCase()] || THEME.AUTO
setThemeSetting(selector.value)
}
function getCachedTheme() {
return localStorage.getItem(LS_KEY_FOR_THEME_SETTING)
}
function cacheColorTheme(theme) {
localStorage.setItem(LS_KEY_FOR_THEME_SETTING, theme)
}
function setThemeColor(themeColor) {
document.documentElement.setAttribute(
THEME_ATTRIBUTE_NAME,
themeColor
);
}
function setOsDefault() {
matchDarkMedia = window.matchMedia("(prefers-color-scheme:dark)");
setThemeColor(matchDarkMedia.matches ? THEME.DARK : THEME.LIGHT)
darkMediaListener = function(e) {
console.log(e.currentTarget)
setThemeColor(e.currentTarget.matches ? THEME.DARK : THEME.LIGHT)
}
matchDarkMedia.addEventListener('change', darkMediaListener)
}
function handleSelectChange() {
const selector = document.getElementById('selector')
cacheColorTheme(selector.value)
setThemeSetting(selector.value)
}
function setThemeSetting(colorTheme) {
switch (colorTheme) {
case THEME.DARK:
case THEME.LIGHT:
if (matchDarkMedia && darkMediaListener) {
matchDarkMedia.removeEventListener('change', darkMediaListener)
matchDarkMedia = null
darkMediaListener = null
}
setThemeColor(colorTheme)
break;
case THEME.AUTO:
default:
setOsDefault()
break;
}
}
</script>
</body>
</html>
到这里我们就基本实现用户自定义设置深色、浅色模式和自动模式的需求了。
调试
在页面上选择深色或者浅色模式我们选择之后都会立马看到效果,自动模式我们就真的需要每次去设置里面修改主题颜色吗?当然我们没必要这么做,在chrome
的调试模式的Rendering
选项卡里面是可以进行模拟切换的
如果你打开调试窗口没找到这个选项卡的话可以在调试窗口的更多工具里面看一下,还不行的话就升级一下chrome
的版本吧!
注意
一些手机浏览器的深色模式及其霸道,比如说小米自带的浏览器,当你开启了浏览器的深色模式之后,即使你在页面上选择了浅色模式,但是整个页面背景还是会呈现出黑色,文字呈现白色,这应该是浏览器级别的魔改了,暂时不知道怎么解决,他这个深色模式我倒是觉得称之为夜间模式更贴切!如果哪位大佬知道如何处理的话欢迎留言!