banner
innei

innei

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

Enable built-in color support for dark mode in Tailwind.

Recently, I added support for dark mode to xLog. However, when developing xLog, I didn't leave any room for dark mode, such as using CSS variables for color values. xLog happens to use Tailwind, and almost all color scenarios use the built-in color values of Tailwind. However, the built-in color values of Tailwind are fixed values and do not support switching color values based on dark mode.

So I came up with an idea to make the built-in colors switch based on whether it is dark mode or not.

First of all, the first step is to reconfigure Tailwind and override all the built-in colors, changing all the fixed values to CSS variables. The prefix of the variables can be customized as long as there is no conflict. Here, let's temporarily set it as tw-colors-i. For example, we need a color value configuration like this:

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

But this writing method does not support Tailwind's ability to obtain opacity for colors, so we need to modify it a bit.

// 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>)',
        // ...
      },
    },
  },
}

Add <alpha-value> to be replaced by Tailwind with the corresponding opacity. Note that we use rgba, so when defining color variables, be careful not to use Hex color values, but use formats like --tw-colors-i-slate-50: 248, 250, 252;.

Next is the problem of how to get the inverse color. In Tailwind versions 3.3 and above, the built-in colors range from 50 to 950 (version 3.2 does not have 950). One light color corresponds to one dark color, and their sum is 1000. For example, the dark color corresponding to slate-50 is slate-950, 90+950=1000. Following this idea, you can write a script to generate all the dark color values.

I won't paste the code here, you can go to Crossbell-Box/xLog to see it. If you need to use it, just modify the generated CSS path and the path of the Tailwind color value configuration (Tailwind >= 3.3).

The generated CSS color variable definition looks like this (part of it is intercepted, you can view the complete version through 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;
  }
}

This CSS defines the light and dark colors based on whether the system is in dark or light mode, and is determined by html.light and html.dark.

At the same time, generate the Tailwind color configuration:

// 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>)",
}

In the project, import the generated CSS and override the colors in the Tailwind configuration.

// 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
  		}
  	}
	}
}

Here, the always option is defined as an optional configuration, and the colors of always are still the fixed built-in color values. You can use them if needed.

As for whether to define darkMode, it has now become an optional item. If you need to use dark-mode: provided by Tailwind, you need to enable it. But since it is dynamic color values, whether to enable it or not depends on the usage requirements.


Let's talk about the disadvantages:

  • A large number of CSS variables are introduced, and unused variables cannot be eliminated during compilation.

A better solution?

Contribute to Tailwind. Support multiple definitions for colors, as shown below:

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

I hope that Tailwind enthusiasts can see this proposal.

Finally, I recommend xLog, such a useful blogging platform, why not give it a try?

By the way, my self-created Mix Space now also supports synchronization to xLog. Come and try it.

This article is synchronized and updated to xLog by Mix Space
The original link is https://innei.ren/posts/programming/tailwind-built-in-colors-dark-mode

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.