当你在 2025 做暗色方案

前言

兄弟们五一快乐呀,最近在做暗色模式的需求,正好调研了一下比较常见的切换主题色的方案,简单分享一下

方案

arco design 处理方案

可能有同学没用过,就是更漂亮一些的 ant design,是我们项目中的组件库。

先来看看官方文档是如何介绍暗色切换的

很简单,直接去操控 arco-theme 这个属性的值,如果为 dark 就为暗色,那么就上项目里看看这个值

果然,这里增加了一个属性,但为什么增加一个属性就可以起到切换主题色的作用呢?再把目光切到右边,亮色模式时:

而如果切换到暗色模式

这下就清晰了,body 相关的样式有二,一个是 body { *** },另一个是 body[arco-theme=dark] { *** },正常情况下只会生效 body,而当使用这段代码时:

javascript 复制代码
document.body.setAttribute('arco-theme', 'dark');

body 标签上会增加一段 arco-theme="dark",此时 body[arco-theme=dark] { *** } 将被匹配到,又因为多了一个筛选器,优先级会更高一级,这么一来就能覆盖掉最初的 body { *** },达成暗色模式。

话又说回来了,为什么切换一个 body 就可以进行全部样式的覆盖呢?

这就得说起 css 变量 这个东西了,它是在 2017 年基本全面支持的 css 特性,我们可以用 -- 语法来定义变量:

css 复制代码
--xxxx: xxxxxx

--color-bg-1: #17171a;
--color-bg-2: #232324;

随后可以使用 var() 语法来使用变量

css 复制代码
color: var(--color-bg-2);

如此一来,我们只需要在 body 中定义变量,那么 body 下的所有标签均可以使用该变量。当我替换了 body 中变量的定义时,下面所有标签的变量也会进行对应的替换。

直接听可能有点抽象,我们来写一个简单的 demo

html 复制代码
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>主题色</title>
    <style>
      /* 设置默认主题 */
      body {
        --color-bg: white;
        --color-text: black;
      }
      /* 设置暗色主题 */
      body[data-theme='dark'] {
        --color-bg: black;
        --color-text: white;
      }

      /* 设置主内容区域 */
      .main {
        background-color: var(--color-bg);
        color: var(--color-text);
      }
    </style>
  </head>
  <body>
    <div class="main">
      <h1>Hello World</h1>
      <button onclick="toggleTheme()">Toggle Theme</button>
    </div>
    <script>
      function toggleTheme() {
        document.body.dataset.theme =
          document.body.dataset.theme === 'dark' ? 'light' : 'dark';
      }
    </script>
  </body>
</html>

如图所示,还是比较简单好懂的。arco design 做的事情基本就是如此,它定义了相当丰富的变量,所有的颜色几乎都可以在变量中找到。在组件库中,只需要全部颜色相关的 css 都使用 body 中的变量,就可以达成一键切换的效果。

不过我们自己开发的组件往往都是随心所欲的,左一个 bg-black ,右一个 #ffffff,这样搞就会出现无法切换的情况,所以需要将这些组件的 color 统一改造为使用 body 中对应的变量。

ant design 处理方案

老规矩,先看它官方文档

看起来是把逻辑都收敛起来了,只暴露了方法,我们从控制台再找找线索

藏的比较深,不过还是找到了。简单测试一下,当点击切换主题时,对应的变量改变了,但是其 css 类名并没有发生改变,奇怪,这点跟 arco 不一样,它又是如何实现的呢。

没办法,翻一下源码吧,先看一下暗色的演示代码:

js 复制代码
const App: React.FC = () => (
  <ConfigProvider
    theme={{
      // 1. 单独使用暗色算法
      algorithm: theme.darkAlgorithm,

      // 2. 组合使用暗色算法与紧凑算法
      // algorithm: [theme.darkAlgorithm, theme.compactAlgorithm],
    }}
  >
    <Space>
      <Input placeholder="Please Input" />
      <Button type="primary">Submit</Button>
    </Space>
  </ConfigProvider>
);

所以突破口应该在 ConfigProvider 的 theme 上,沿着这段代码一直翻源码,可以翻出以下逻辑

  1. 读取写入 theme 中的 token,并找到默认 token 进行合并,变为完整的 Seed Token
  2. 读取算法,利用该算法,将完整的 Seed Token 转为对应主题的 Map Token
  3. 将 Map Token 转化为 css 变量,并进行应用

非常复杂啊朋友们,所以这里就不展开说了,大致就是通过 js 去改造 css 变量

light-dark

light-dark 是近几年新出的 css 函数,直接看 demo 吧

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <title>light-dark Demo</title>
  <style>
    body {
      background-color: light-dark(white, black);
      color: light-dark(black, white);
    }
  </style>
</head>
<body>
  <h1>Hello, world!</h1>
  <p>跟随系统浅色/深色模式</p>
</body>
</html>

这个函数比较简单,但是它不能用 js 去手动切换,只能跟随系统。那可玩性就不高了

民间一行代码

css 复制代码
body { filter: invert(1) hue-rotate(180deg) }
  • 单独使用 invert(1) 会让白变黑,但也会让蓝变橙、红变绿等颜色被颠倒;
  • 加上 hue-rotate(180deg) 能够部分校正颜色的色调错乱,看起来更自然一些。

对于部分网站可用,但是大部分的时候效果并不好

over,这就是常见的几种方案了,祝大家五一快乐~

相关推荐
夕水11 分钟前
自动化按需导入组件库的工具rust版本完成开源了
前端·rust·trae
JarvanMo1 小时前
借助FlutterFire CLI实现Flutter与Firebase的多环境配置
前端·flutter
Jedi Hongbin1 小时前
echarts自定义图表--仪表盘
前端·javascript·echarts
凯哥19701 小时前
Sciter.js指南 - 桌面GUI开发时使用第三方模块
前端
边洛洛1 小时前
对Electron打包的exe文件进行反解析
前端·javascript·electron
财神爷亲闺女1 小时前
js 实现pc端鼠标横向拖动滚动
前端
用户2031196600961 小时前
sheet在SwiftUI中的基本用法
前端
晴殇i1 小时前
一行代码搞定防抖节流:JavaScript新特性解析
前端·javascript