浏览器夜间模式适配

345 阅读2分钟

系统兼容

想要实现深色模式的效果,前提条件是要系统支持, 目前 常见系统支持情况如下:

H5 深色适配

随着深色模式的流行,越来越多的操作系统、浏览器开始支持深色模式,现在可以利用 CSS 的媒体查询方法(prefers-color-scheme)以及 CSS 变量(CSS variables、CSS custom properties)就可以实现页面主题跟随系统自动切换深浅模式 。

可以通过以下两种方式来实现 Web 端的深色适配:

一、CSS 的媒体查询

prefers-color-scheme 是一种用于检测用户是否有将系统的主题色设置为亮色或者暗色的 CSS 媒体特性。利用其设置不同主题模式下的 CSS 样式,浏览器会自动根据当前系统主题加载对应的 CSS 样式。light 适配浅色主题,dark 适配深色主题,no-preference 表示获取不到主题时的适配方案。

  • CSS
css
复制代码
@media (prefers-color-scheme: light) { 
  .article {  
    background:#fff; 
    color: #000;  
  } 
} 
@media (prefers-color-scheme: dark) { 
  .article {  
    background:#000;  
    color: white;  
  } 
} 
@media (prefers-color-scheme: no-preference) { 
  .article {  
    background:#fff; 
    color: #000;  
  } 
} 
  • link 标签
javascript
复制代码
<link href="./common.css" rel="stylesheet" type="text/css" /> 
<link href="./light-mode-theme.css" rel="stylesheet" type="text/css" /> 
<link href="./dark-mode-theme.css" rel="stylesheet" type="text/css" media="(prefers-color-scheme: dark)" /> 

二、CSS 变量 + 媒体查询

window.matchMedia 方法可以用来查询指定的媒体查询字符串解析后的结果。 结合 CSS 变量和 matchMedia 的查询结果,设置对应的 CSS 主题颜色。该方法更灵活,可以单独抽离主题色进行适配。

CSS 变量的作用域与 CSS 的"层叠"规则一致,优先级最高的声明生效。所以当 body 上存在 "dark" 类名时,:root .dark 会生效,否则 :root 生效。

css
复制代码
.article { 
  color: var(--text-color, #eee); 
  background: var(--text-background, #fff); 
} 
:root { 
  --text-color: #000; 
  --text-background: #fff; 
} 
:root .dark { 
  --text-color: #fff; 
  --text-background: #000; 
} 

使用 matchMedia 匹配主题媒体,深色模式匹配 (prefers-color-scheme: dark) ,浅色模式匹配 (prefers-color-scheme: light)

监听主题模式,深色模式时为根节点添加类名,根据 CSS 变量的响应式布局特点,自动生效 dark 类名下的 CSS。

let isDarkTheme = false

async function setTheme() {
    isDarkTheme = getDeviceTheme()
    
    if (isDarkTheme) {
        document.documentElement.classList.add('dark')
    } else {
        document.documentElement.classList.remove('dark')
    }
}

function getDeviceTheme() {
    const darkThemeMediaQuery = window.matchMedia('prefers-color-scheme: dark')
    if (darkThemeMediaQuery.matches) {
        return true
    }
}

export {
    setTheme
}