likes
comments
collection
share

如何用 Tailwindcss 实现暗黑主题

作者站长头像
站长
· 阅读数 52

给 Grow in 掘金做深色主题适配时,调研了 Tailwindcss 实现暗黑模式的几种方案,在此简单分享一下它们的用法和适用场景。

官方方案:Dark Mode

tailwindcss 内置的 Dark Mode 由两部分组成:

  • dark 修饰符,用于指定暗黑模式下的样式
  • darkMode 配置,指定暗黑模式的应用标识

使用 dark 修饰符指定样式

<div class="bg-gray-50 dark:bg-gray-800"></div>

生成的 CSS

.bg-gray-50 {
  --tw-bg-opacity: 1;
  background-color: rgb(249 250 251 / var(--tw-bg-opacity));
}

@media (prefers-color-scheme: dark) {
  .dark\:bg-gray-800 {
    --tw-bg-opacity: 1;
    background-color: rgb(31 41 55 / var(--tw-bg-opacity));
  }
}

tailwindcss 通过 extractor 提取 html 和 js 中的 class,变体 class .dark\:bg-gray-800 作为独立项被提出。

默认情况下, tailwindcss 通过媒体查询自动跟随系统切换暗黑模式。也可以设置 darkMode 改用类策略,手动切换暗黑模式。

/** @type {import('tailwindcss').Config} */
module.exports = {
  darkMode: ['class'],
  theme: {
    extend: {
      // ...
    },
  },
  plugins: [],
}

生成的 CSS

.bg-gray-50 {
  --tw-bg-opacity: 1;
  background-color: rgb(249 250 251 / var(--tw-bg-opacity));
}

:is(.dark .dark\:bg-gray-800) {
  --tw-bg-opacity: 1;
  background-color: rgb(31 41 55 / var(--tw-bg-opacity));
}

关于内置暗黑模式的具体介绍,指路 官方文档

dark 修饰符是符合 tailwindcss 原子化设计的,但在实际应用中并不方便。dark 修饰符的不便之处在于粒度较细,需要在 html 或模板中逐点添加,这样容易产生疏漏,会增加后续开发和测试的成本。

理想的维护状态是:在 html 中只需指定一次,不同主题的颜色、阴影、透明度在其他地方集中定义。

灵活定义:CSS 变量

虽然 tailwindcss 的 theme config 只支持定义一套设计素材 (Design Tokens),但 tailwindcss 对 CSS 变量有很好的支持,可以用 CSS 变量代替固定值。

:root {
	opacity: {
        mask: "var(--gij-opacity-mask)",
        layer: "var(--gij-opacity-layer)",
        widget: "var(--gij-opacity-widget)",
    },
    boxShadow: {
        card: "var(--gij-color-card-shadow)",
        inner: "var(--gij-color-inner-shadow)",
    }
}

需要注意的是 tailwindcss 支持颜色透明度修改器,也就是用 bg-gray-700/50 这样的写法给背景加透明度 background-color: rgb(55 65 81 / 0.5) 。因此色值定义中需要保留 <alpha-value> 占位符供 tailwindcss 实现透明度修改器,同时把 CSS 变量赋值为 rgb 或 hsl 的组成值即可。

CSS 变量赋值为 rgb 或 hsl 的组成值

:root {
    /* For rgb(255 115 179 / <alpha-value>) */
    --color-primary: 255 115 179;

    /* For hsl(198deg 93% 60% / <alpha-value>) */
    --color-primary: 198deg 93% 60%;

    /* For rgba(255, 115, 179, <alpha-value>) */
    --color-primary: 255, 115, 179;
}

保留 <alpha-value> 占位符

module.exports = {
  theme: {
    colors: {
      // Using modern `rgb`
      primary: 'rgb(var(--color-primary) / <alpha-value>)',
      secondary: 'rgb(var(--color-secondary) / <alpha-value>)',

      // Using modern `hsl`
      primary: 'hsl(var(--color-primary) / <alpha-value>)',
      secondary: 'hsl(var(--color-secondary) / <alpha-value>)',

      // Using legacy `rgba`
      primary: 'rgba(var(--color-primary), <alpha-value>)',
      secondary: 'rgba(var(--color-secondary), <alpha-value>)',
    }
  }
}

CSS 变量融合进 tailwindcss 后,就能自由地标识暗色模式了

: root {
	--color-bg: 255 255 255;
}

[dark-theme='dark'] :root {
	--color-bg: 0 0 0;
}

如果设计师给了完整的 Design Tokens,只需要对 tailwindcss 自带的系统做覆盖或扩展即可。

一步到位:反色模式

如果是一些个人小项目,例如个人博客、项目主页之类,没有设计师的支持怎样快速实现暗黑模式呢?

《让 Tailwind 内置颜色支持暗黑模式》 中作者提出一个巧妙的思路:让 tailwind 自带的颜色支持暗色模式。

实现原理是在 tailwind + CSS 变量的基础上,依据「暗色=浅色取反色」,用脚本自动生成暗黑模式色组。

脚本将 tailwind 的自带色组改为 CSS 变量表示

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

如何取反色呢?Tailwind 色组中两两色块对应,一个亮色对应一个暗色,并且标号相加等于 1000,例如 slate-50slate-950 对应,slate-100slate-900 对应,对应色即是反色。

如何用 Tailwindcss 实现暗黑主题

脚本生成亮暗模式下的色组值

: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;
}

脚本部分也已经有现成的工具 pacexy/tailwindcss-variable-colors: Add dark mode support with just one class (github.com)

// tailwind.config.ts
import { Config } from 'tailwindcss'
import colors from 'tailwindcss/colors'
import { createVariableColors, variableColorsPlugin } from 'tailwindcss-variable-colors'

const config: Config = {
  content: ['./src/**/*.tsx'],
  theme: {
    colors: createVariableColors(colors),
  },
  plugins: [variableColorsPlugin(colors)],
}

export default config
// Button.tsx
const Button = () => (
  <button className="text-slate-700 bg-slate-100 hover:bg-blue-100 active:bg-blue-200 ...">
    Button
  </button>
)

使用工具后只需按浅色模式实现,暗色模式自然支持。

总结

文章介绍了使用 Tailwindcss 实现暗黑模式的三种方案:

  • 在极简项目中用 Dark Mode 即可
  • 有设计好的 Design Tokens,用 CSS 变量集成进 tailwind
  • 无严格设计的个人项目,使用 pacexy/tailwindcss-variable-colors 一步到位
转载自:https://juejin.cn/post/7239715560698593317
评论
请登录