banner
innei

innei

写代码是因为爱,写到世界充满爱!
github
telegram
twitter

让 Tailwind 内置颜色支持暗黑模式

最近给 xLog 增加了黑暗模式的支持,但由于 xLog 在开发时候就没对黑暗模式留个口子,比如颜色值不固定写死,或者是使用 CSS 变量的颜色值。而 xLog 真巧使用了 Tailwind,基本上所有的颜色应用场景都用了 Tailwind 自带的色值,但由于 Tailwind 本身自带的色值都是一个固定的值,并不支持根据 Dark Mode 切换色值。

于是我萌生了一个想法,让自带的颜色能根据是否是暗黑模式去切换就行了。

首先第一是,要重新配置 Tailwind,覆写原来内置的所有的颜色,把固定的值全部改写成 CSS 变量。变量的前缀可以自定义,不要冲突就行了,这边就暂定为 tw-colors-i。比如我们需要类似这样的色值配置:

// tailwind.config.ts
module.exports = {
  theme: {
    colors: {
      slate: {
        50: 'rgb(var(--tw-colors-i-slate-50))',
        100: 'rgb(var(--tw-colors-i-slate-100))',
        // ...
      },
    },
  },
}

但是以上的写法并不支持 Tailwind 的让颜色获得 Opacity 的能力,所以还需要修改一下。

// tailwind.config.ts
module.exports = {
  theme: {
    colors: {
      slate: {
        50: 'rgba(var(--tw-colors-i-slate-50), <alpha-value>)',
        100: 'rgba(var(--tw-colors-i-slate-100), <alpha-value>)',
        // ...
      },
    },
  },
}

加上 <alpha-value> 用于被 Tailwind 替换相应的 Opacity。注意这里我们使用了 rgba 所以后续在定义颜色变量的时候要注意,不能使用 Hex 类型的颜色值,而要使用类似这样的格式 --tw-colors-i-slate-50: 248, 250, 252;

接下来就是如何取反色的问题,在 Tailwind 3.3 以上的版本,内置颜色都是从 50 开始到 950 结束(3.2 版本没有 950)。一个亮色对应一个暗色,并且相加等于 1000。比如,slate-50 对应的暗色就是 slate-95090+950=1000。按照这个思路,就可以写一个脚本去生成一个暗色的所有的色值。

代码这里就不贴了,可以去 Crossbell-Box/xLog 这里看,如果需要使用的话只需要修改下,生成的 CSS 路径和 Tailwind 色值配置的路径即可(Tailwind >= 3.3)。

生成的 CSS 颜色变量定义大概长这样(截取部分,完整可以通过 Crossbell-Box/xLog 查看):

:root {
  --tw-colors-i-slate-50: 248, 250, 252;
  --tw-colors-i-slate-100: 241, 245, 249;
  --tw-colors-i-slate-200: 226, 232, 240;
  --tw-colors-i-slate-300: 203, 213, 225;
  --tw-colors-i-slate-400: 148, 163, 184;
  --tw-colors-i-slate-500: 100, 116, 139;
}

html.dark {
  --tw-colors-i-slate-50: 2, 6, 23;
  --tw-colors-i-slate-100: 15, 23, 42;
  --tw-colors-i-slate-200: 30, 41, 59;
  --tw-colors-i-slate-300: 51, 65, 85;
  --tw-colors-i-slate-400: 71, 85, 105;
  --tw-colors-i-slate-500: 100, 116, 139;
}

html.light {
  --tw-colors-i-slate-50: 248, 250, 252;
  --tw-colors-i-slate-100: 241, 245, 249;
  --tw-colors-i-slate-200: 226, 232, 240;
  --tw-colors-i-slate-300: 203, 213, 225;
  --tw-colors-i-slate-400: 148, 163, 184;
  --tw-colors-i-slate-500: 100, 116, 139;
}

@media (prefers-color-scheme: dark) {
  html:not(.light) {
    --tw-colors-i-slate-50: 2, 6, 23;
    --tw-colors-i-slate-100: 15, 23, 42;
    --tw-colors-i-slate-200: 30, 41, 59;
    --tw-colors-i-slate-300: 51, 65, 85;
    --tw-colors-i-slate-400: 71, 85, 105;
    --tw-colors-i-slate-500: 100, 116, 139;
  }
}

@media (prefers-color-scheme: light) {
  html:not(.dark) {
    --tw-colors-i-slate-50: 248, 250, 252;
    --tw-colors-i-slate-100: 241, 245, 249;
    --tw-colors-i-slate-200: 226, 232, 240;
    --tw-colors-i-slate-300: 203, 213, 225;
    --tw-colors-i-slate-400: 148, 163, 184;
    --tw-colors-i-slate-500: 100, 116, 139;
  }
}

这个 CSS 中定义了在系统在暗色还是亮色以及根据 html.light html.dark 确定亮暗色。

同时生成 Tailwind 颜色配置:

// tw-colors.js
export default {
  slate: {
    50: "rgba(var(--tw-colors-i-slate-50), <alpha-value>)",
    100: "rgba(var(--tw-colors-i-slate-100), <alpha-value>)",
    200: "rgba(var(--tw-colors-i-slate-200), <alpha-value>)",
    300: "rgba(var(--tw-colors-i-slate-300), <alpha-value>)",
    400: "rgba(var(--tw-colors-i-slate-400), <alpha-value>)",
    500: "rgba(var(--tw-colors-i-slate-500), <alpha-value>)",
    600: "rgba(var(--tw-colors-i-slate-600), <alpha-value>)",
    700: "rgba(var(--tw-colors-i-slate-700), <alpha-value>)",
    800: "rgba(var(--tw-colors-i-slate-800), <alpha-value>)",
    900: "rgba(var(--tw-colors-i-slate-900), <alpha-value>)",
    950: "rgba(var(--tw-colors-i-slate-950), <alpha-value>)",
  },
  // ...
  inherit: "inherit",
  current: "currentColor",
  transparent: "transparent",
  black: "rgba(var(--tw-colors-i-black), <alpha-value>)",
  white: "rgba(var(--tw-colors-i-white), <alpha-value>)",
}

在项目中,引入生成的 CSS,并在 Tailwind 配置覆写颜色。

// tailwind.config.js
const twColors = require("./tw-colors")
const alwaysColor = require("tailwindcss/colors")

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ["./src/**/*.tsx"],
  darkMode: ['class', 'html.dark'],
  theme: {
    colors: twColors,
    extend: {
    	colors: {
    	  always: alwaysColor
  		}
  	}
	}
}

这里可选配置定义下 always,always 的颜色仍是自带的固定颜色值。如有需要可以取用。

至于 darkMode 是否需要定义,现在变成了可选项,如果需要用 Tailwind 提供的 dark-mode: 那是需要开启的。不过既然都是动态色值了,开不开就按使用需求了。


说说缺点:

  • 引入了大量 CSS 变量,并且无用变量无法在编译时消除。

更好的解决方式?

给 Tailwind 提 PR。支持颜色多重定义,如下所示:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ['./src/**/*.tsx'],
  theme: {
    colors: {
      blue: {
        50: {
          light: '#123',
          dark: '#321',
        },
      },
    },
  },
}

希望有 Tailwind 的小伙伴可以看到这个提案。

最后安利一下 xLog,这么好用的博客平台,还不赶紧试试?

对了,我自创的 Mix Space 现也支持同步到 xLog 了,赶紧来试试吧。

此文由 Mix Space 同步更新至 xLog
原始链接为 https://innei.ren/posts/programming/tailwind-built-in-colors-dark-mode

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。