web深色模式(Dark Mode)

深色模式应该是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选项卡里面是可以进行模拟切换的

image-20240115180330762

如果你打开调试窗口没找到这个选项卡的话可以在调试窗口的更多工具里面看一下,还不行的话就升级一下chrome的版本吧!

image-20240115180522745

注意

一些手机浏览器的深色模式及其霸道,比如说小米自带的浏览器,当你开启了浏览器的深色模式之后,即使你在页面上选择了浅色模式,但是整个页面背景还是会呈现出黑色,文字呈现白色,这应该是浏览器级别的魔改了,暂时不知道怎么解决,他这个深色模式我倒是觉得称之为夜间模式更贴切!如果哪位大佬知道如何处理的话欢迎留言!

参考

css函数

自定义属性

prefers-color-scheme

matchMedia

  • 18
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值