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

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。