如何用 Tailwindcss 实现暗黑主题
给 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-50
和 slate-950
对应,slate-100
和 slate-900
对应,对应色即是反色。
脚本生成亮暗模式下的色组值
: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