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

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。