/** @type {import('tailwindcss').Config} */ /** * 颜色走 CSS 变量而不是写死的十六进制。 * * # 为什么不逐处加 dark: 前缀 * * 全站约 700 处颜色用法散在 21 个组件里。逐个写 `bg-white dark:bg-gray-900` * 有两个致命问题:漏一处就是深色下的白底白字(而且只有肉眼能发现), * 以及此后每加一个组件都要记得写两遍 —— 那种约定活不过三次改动。 * * # 为什么改调色板就够了 * * 这套代码里灰阶**本身就是语义色阶**: * - `white` / `gray-50` / `gray-100` = 表面层次(卡片 / 页面底 / 悬停) * - `gray-200` / `gray-300` = 分隔线 * - `gray-900` → `gray-400` = 文字主次 * * 深色模式要做的正是把这条色阶**反转**:white 变近黑、gray-900 变近白。 * 于是零组件改动就能整体切换,新组件照常写 `bg-white text-gray-900` * 也自动适配 —— 不需要任何人记得任何约定。 * * # 为什么是 `rgb(var(--x) / )` 而不是直接存颜色串 * * 代码里有 `bg-blue-50/70`、`bg-gray-50/60` 这样的透明度修饰符。 * 变量若存 `#f9fafb`,Tailwind 生成的 `rgb(#f9fafb / 0.7)` 是无效 CSS, * 那些半透明高亮会静默失效(不报错,只是不透明)。存 RGB 三元组才行。 */ const withAlpha = (v) => `rgb(var(${v}) / )`; const grayScale = { 50: withAlpha('--c-gray-50'), 100: withAlpha('--c-gray-100'), 200: withAlpha('--c-gray-200'), 300: withAlpha('--c-gray-300'), 400: withAlpha('--c-gray-400'), 500: withAlpha('--c-gray-500'), 600: withAlpha('--c-gray-600'), 700: withAlpha('--c-gray-700'), 800: withAlpha('--c-gray-800'), 900: withAlpha('--c-gray-900'), 950: withAlpha('--c-gray-950') }; /** 强调色只需要三档:浅底(chip/提示条)、主色(按钮)、深色(hover/文字)。 */ const accent = (name) => ({ 50: withAlpha(`--c-${name}-50`), 100: withAlpha(`--c-${name}-100`), 200: withAlpha(`--c-${name}-200`), 300: withAlpha(`--c-${name}-300`), 400: withAlpha(`--c-${name}-400`), 500: withAlpha(`--c-${name}-500`), 600: withAlpha(`--c-${name}-600`), 700: withAlpha(`--c-${name}-700`), 800: withAlpha(`--c-${name}-800`), 900: withAlpha(`--c-${name}-900`) }); export default { content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], // class 而不是 media:主题要能被人显式选择。跟系统走是**默认值**, // 不是唯一选项 —— 白天开深色主题是常见偏好。 darkMode: 'class', theme: { extend: { /** * textColor 单独覆盖 white。 * * `--c-white` 服务两种**互相冲突**的用途: * - `bg-white` = 卡片表面 → 深色模式必须变暗 * - `text-white` = 彩色按钮上的文字 → 深色模式必须**保持浅色** * * 只有一个变量时后者跟着变暗,白字落在 `bg-chrome-700` 的激活导航项上 * 只剩 1.34:1 —— 几乎不可见(实测发现)。按钮底色在深色模式下依然是 * blue-600 那样的彩色,上面的文字本来就该是白的。 * * Tailwind 的 textColor 默认继承 colors,这里只改 white 一项, * 其余(gray/blue/...)仍走反转的色阶。 */ textColor: { white: withAlpha('--c-on-accent') }, colors: { white: withAlpha('--c-white'), gray: grayScale, // slate 在这套代码里只用于登录页与少数深色块,与 gray 同源即可 —— // 保留两个名字是为了不改那些组件,但它们指向同一条色阶。 slate: grayScale, /** * chrome —— 应用框架(侧栏 / 底部导航)的专用色阶。 * * 为什么不能跟 gray 走:这两块**在浅色模式下本来就是深色的** * (深色侧栏配浅色内容区是这套 UI 的原本设计)。把它们并入反转的 * gray 之后,深色模式下 `bg-slate-900` 变成了近白色 —— 侧栏比内容区 * 还亮,整个层次翻了过来(实测 rgb(243,245,248),而内容区是 rgb(17,19,24))。 * * 独立成一条色阶后:浅色模式下它是深色框架,深色模式下**微调即可** * (比内容区略深一点,保持"框架比内容更沉"的关系),两种模式下 * 语义一致。 */ chrome: { 100: withAlpha('--c-chrome-100'), 200: withAlpha('--c-chrome-200'), 400: withAlpha('--c-chrome-400'), 600: withAlpha('--c-chrome-600'), 700: withAlpha('--c-chrome-700'), 800: withAlpha('--c-chrome-800'), 900: withAlpha('--c-chrome-900') }, /* accent 色走固定值,不随主题反转。深色模式下彩色按钮底色不变, 变的是卡片/表面的深浅,所以白色文字的对比度始终稳定。 */ blue: { 50:'#eff6ff', 100:'#dbeafe', 200:'#bfdbfe', 300:'#93c5fd', 400:'#60a5fa', 500:'#3b82f6', 600:'#2563eb', 700:'#1d4ed8', 800:'#1e40af', 900:'#1e3a8a' }, red: { 50:'#fef2f2', 100:'#fee2e2', 200:'#fecaca', 300:'#fca5a5', 400:'#f87171', 500:'#ef4444', 600:'#dc2626', 700:'#b91c1c', 800:'#991b1b', 900:'#7f1d1d' }, green: { 50:'#f0fdf4', 100:'#dcfce7', 200:'#bbf7d0', 300:'#86efac', 400:'#4ade80', 500:'#22c55e', 600:'#16a34a', 700:'#15803d', 800:'#166534', 900:'#14532d' }, amber: { 50:'#fffbeb', 100:'#fef3c7', 200:'#fde68a', 300:'#fcd34d', 400:'#fbbf24', 500:'#f59e0b', 600:'#d97706', 700:'#b45309', 800:'#92400e', 900:'#78350f' }, orange: { 50:'#fff7ed', 100:'#ffedd5', 200:'#fed7aa', 300:'#fdba74', 400:'#fb923c', 500:'#f97316', 600:'#ea580c', 700:'#c2410c', 800:'#9a3412', 900:'#7c2d12' }, yellow: { 50:'#fefce8', 100:'#fef9c3', 200:'#fef08a', 300:'#fde047', 400:'#facc15', 500:'#eab308', 600:'#ca8a04', 700:'#a16207', 800:'#854d0e', 900:'#713f12' }, red: accent('red'), green: accent('green'), amber: accent('amber'), orange: accent('orange'), yellow: accent('yellow') } } }, plugins: [] };