Files
novalon-website/tailwind.config.js
T
zhangxiang 045c5095c1 fix(build): 改 darkMode 为 variant 模式让 Tailwind dark: 变体按 data-theme='dark' 触发
- tailwind.config.js darkMode 从 'class' 改为 ['variant', '[data-theme="dark"] &']
- 'class' 模式匹配 .dark class,但站点用 html[data-theme='dark'] 属性,导致 dark: 变体全局失效
- variant 模式让 dark:bg-foo 类生成 [data-theme="dark"] .dark\:bg-foo 选择器
- 验证:临时加 dark:bg-blue-500 → CSS 含正确选择器;运行时浅色 bg=white、深色 bg=rgb(59,130,246)

回归测试脚本:
- darkmode-selector-verify.mjs:合成探针验证 utility 生效
- home-cta-verify.mjs:定位 home 本地简化 CTASection 实际渲染(之前误判为'不渲染')

诚实标注:
- 全站 grep dark: utility class 仅发现 cta-button.tsx 2 处,但均为 TS 对象 key(非 utility),故 dark: 失效问题无直接破坏
- 修复属'基础设施补全':未来添加 dark: 工具类即按 data-theme='dark' 自动生效

home-content-v15.tsx 的 CTASection(line 978-998)是**文件本地简化版**
(仅标题 + CTAButton,无 BrandStamp),与 src/components/sections/cta-section.tsx
(含 BrandStamp「值得信赖」)是不同组件。前者实际渲染,后者无路由使用(待用资产)。
本修复不涉及 CTASection 渲染路径。

验证:
- type-check 0 error
- lint 0 error
- 编译产物含 [data-theme="dark"] .dark\:bg-blue-500 选择器
- 运行时合成探针 PASS(浅色 white / 深色 blue)
2026-09-20 11:50:58 +08:00

278 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
],
darkMode: ['variant', '[data-theme="dark"] &'],
theme: {
extend: {
colors: {
/* 重要:Tailwind v3 无法对纯 hex 的 CSS 变量套用 /50 这类透明度修饰符,
会生成无效 CSS 并静默失效(样式不报错,但效果不出现)。
故常用色改用 rgb(通道 / <alpha-value>) 形式 —— 无修饰符时 alpha 默认 1,
视觉结果与原来一致,同时 bg-brand/10、border-ink/30 等能正确编译。 */
ink: {
DEFAULT: 'rgb(var(--color-ink-rgb) / <alpha-value>)',
light: 'var(--color-ink-light)',
lighter: 'var(--color-ink-lighter)',
rgb: 'var(--color-ink-rgb)',
},
brand: {
DEFAULT: 'rgb(var(--color-brand-rgb) / <alpha-value>)',
hover: 'var(--color-brand-hover)',
light: 'var(--color-brand-light)',
soft: 'rgb(var(--color-brand-rgb) / calc(0.12 * <alpha-value>))',
bg: 'var(--color-brand-bg)',
rgb: 'var(--color-brand-rgb)',
section: 'var(--color-brand-section)',
'section-text': 'var(--color-brand-section-text)',
'section-muted': 'var(--color-brand-section-muted)',
},
accent: {
blue: 'rgb(var(--color-accent-blue-rgb) / <alpha-value>)',
'blue-soft': 'var(--color-accent-blue-soft)',
teal: 'rgb(var(--color-accent-teal-rgb) / <alpha-value>)',
'teal-soft': 'var(--color-accent-teal-soft)',
amber: 'rgb(var(--color-accent-amber-rgb) / <alpha-value>)',
'amber-soft': 'var(--color-accent-amber-soft)',
purple: 'rgb(var(--color-accent-purple-rgb) / <alpha-value>)',
'purple-soft': 'var(--color-accent-purple-soft)',
cyan: 'rgb(var(--color-accent-cyan-rgb) / <alpha-value>)',
'cyan-soft': 'var(--color-accent-cyan-soft)',
rose: 'rgb(var(--color-accent-rose-rgb) / <alpha-value>)',
'rose-soft': 'var(--color-accent-rose-soft)',
indigo: 'rgb(var(--color-accent-indigo-rgb) / <alpha-value>)',
'indigo-soft': 'var(--color-accent-indigo-soft)',
},
bg: {
primary: 'rgb(var(--color-bg-primary-rgb) / <alpha-value>)',
secondary: 'rgb(var(--color-bg-secondary-rgb) / <alpha-value>)',
tertiary: 'var(--color-bg-tertiary)',
section: 'var(--color-bg-section)',
hover: 'var(--color-bg-hover)',
elevated: 'var(--color-bg-elevated)',
},
text: {
primary: 'var(--color-text-primary)',
secondary: 'rgb(var(--color-text-secondary-rgb) / <alpha-value>)',
tertiary: 'var(--color-text-tertiary)',
muted: 'rgb(var(--color-text-muted-rgb) / <alpha-value>)',
subtle: 'var(--color-text-subtle)',
placeholder: 'var(--color-text-placeholder)',
hint: 'var(--color-text-hint)',
inverse: 'var(--color-text-inverse)',
},
border: {
primary: 'rgb(var(--color-border-primary-rgb) / <alpha-value>)',
secondary: 'rgb(var(--color-border-secondary-rgb) / <alpha-value>)',
accent: 'rgb(var(--color-border-accent-rgb) / <alpha-value>)',
light: 'rgb(var(--color-border-light-rgb) / <alpha-value>)',
dark: 'rgb(var(--color-border-dark-rgb) / <alpha-value>)',
brand: 'rgb(var(--color-border-brand-rgb) / <alpha-value>)',
},
link: {
DEFAULT: 'var(--color-link)',
hover: 'var(--color-link-hover)',
},
success: {
DEFAULT: 'rgb(var(--color-success-rgb) / <alpha-value>)',
hover: 'var(--color-success-hover)',
bg: 'var(--color-success-bg)',
rgb: 'var(--color-success-rgb)',
/** 文字请用此变体:DEFAULT 仅 3.30:1,不达 AA */
text: 'var(--color-success-text)',
},
warning: {
DEFAULT: 'rgb(var(--color-warning-rgb) / <alpha-value>)',
hover: 'var(--color-warning-hover)',
bg: 'var(--color-warning-bg)',
rgb: 'var(--color-warning-rgb)',
/** 文字请用此变体:DEFAULT 仅 3.19:1,不达 AA */
text: 'var(--color-warning-text)',
},
error: {
DEFAULT: 'rgb(var(--color-error-rgb) / <alpha-value>)',
hover: 'var(--color-error-hover)',
bg: 'var(--color-error-bg)',
rgb: 'var(--color-error-rgb)',
/** 文字请用此变体:DEFAULT(品牌红 #C41E3A)在暗黑底仅 3.3:1,不达 AA;
* 本值浅红,暗黑模式经 html[data-theme='dark'] 覆盖后达 AA。 */
text: 'var(--color-error-text)',
},
info: {
DEFAULT: 'rgb(var(--color-info-rgb) / <alpha-value>)',
bg: 'var(--color-info-bg)',
/** 文字请用此变体:DEFAULT 仅 3.68:1,不达 AA */
text: 'var(--color-info-text)',
},
dark: {
bg: 'var(--color-dark-bg)',
'bg-secondary': 'var(--color-dark-bg-secondary)',
'bg-tertiary': 'var(--color-dark-bg-tertiary)',
'text-primary': 'var(--color-dark-text-primary)',
'text-secondary': 'var(--color-dark-text-secondary)',
'text-muted': 'var(--color-dark-text-muted)',
border: 'var(--color-dark-border)',
},
overlay: 'rgb(var(--color-overlay-rgb) / <alpha-value>)',
},
fontFamily: {
sans: ['var(--font-sans)'],
display: ['var(--font-display)'],
mono: ['var(--font-mono)'],
brand: ['var(--font-brand)'],
serif: ['var(--font-serif)'],
// 书法体:globals.css 的 --font-calligraphy-systemSTKaiti / KaiTi 系统字栈)
// 此前 fontFamily 未声明该 key,导致 font-calligraphy utility 不生成,
// 4 处装饰书法字(why-us / challenge / brand-visuals)静默回退 sans。
calligraphy: ['var(--font-calligraphy-system)'],
},
fontSize: {
caption: 'var(--font-size-caption)',
xs: 'var(--font-size-xs)',
sm: 'var(--font-size-sm)',
base: 'var(--font-size-base)',
lg: 'var(--font-size-lg)',
xl: 'var(--font-size-xl)',
'2xl': 'var(--font-size-2xl)',
'3xl': 'var(--font-size-3xl)',
'4xl': 'var(--font-size-4xl)',
'5xl': 'var(--font-size-5xl)',
'6xl': 'var(--font-size-6xl)',
'7xl': 'var(--font-size-7xl)',
display: 'var(--font-size-display)',
},
lineHeight: {
caption: 'var(--line-height-caption)',
tight: 'var(--line-height-tight)',
snug: 'var(--line-height-snug)',
normal: 'var(--line-height-normal)',
relaxed: 'var(--line-height-relaxed)',
},
letterSpacing: {
tighter: 'var(--letter-spacing-tighter)',
tight: 'var(--letter-spacing-tight)',
normal: 'var(--letter-spacing-normal)',
wide: 'var(--letter-spacing-wide)',
wider: 'var(--letter-spacing-wider)',
widest: 'var(--letter-spacing-widest)',
eyebrow: 'var(--letter-spacing-eyebrow)',
},
spacing: {
xs: 'var(--spacing-xs)',
sm: 'var(--spacing-sm)',
md: 'var(--spacing-md)',
lg: 'var(--spacing-lg)',
xl: 'var(--spacing-xl)',
'2xl': 'var(--spacing-2xl)',
'3xl': 'var(--spacing-3xl)',
'4xl': 'var(--spacing-4xl)',
'5xl': 'var(--spacing-5xl)',
'6xl': 'var(--spacing-6xl)',
},
borderRadius: {
xs: 'var(--radius-xs)',
sm: 'var(--radius-sm)',
md: 'var(--radius-md)',
lg: 'var(--radius-lg)',
xl: 'var(--radius-xl)',
'2xl': 'var(--radius-2xl)',
'3xl': 'var(--radius-3xl)',
full: 'var(--radius-full)',
},
boxShadow: {
xs: 'var(--shadow-xs)',
sm: 'var(--shadow-sm)',
md: 'var(--shadow-md)',
lg: 'var(--shadow-lg)',
xl: 'var(--shadow-xl)',
brand: 'var(--shadow-brand)',
'brand-hover': 'var(--shadow-brand-hover)',
inset: 'var(--shadow-inset)',
glow: 'var(--shadow-glow)',
},
transitionDuration: {
instant: 'var(--transition-instant)',
fast: 'var(--transition-fast)',
normal: 'var(--transition-normal)',
slow: 'var(--transition-slow)',
slower: 'var(--transition-slower)',
gentle: 'var(--transition-gentle)',
},
transitionTimingFunction: {
standard: 'var(--ease-standard)',
decelerate: 'var(--ease-decelerate)',
accelerate: 'var(--ease-accelerate)',
'ease-out': 'var(--ease-ink)',
'ease-in-out': 'var(--ease-sharp)',
'ease-spring': 'var(--ease-spring)',
'ease-spring-soft': 'var(--ease-spring-soft)',
sharp: 'var(--ease-sharp)',
ink: 'var(--ease-ink)',
},
maxWidth: {
container: 'var(--container-max)',
},
animation: {
'fade-in-up': 'fadeInUp 0.7s var(--ease-out) forwards',
'fade-in': 'fadeIn 0.5s var(--ease-out) forwards',
'scale-in': 'scaleIn 0.5s var(--ease-out) forwards',
'slide-in-left': 'slideInLeft 0.6s var(--ease-out) forwards',
'slide-in-right': 'slideInRight 0.6s var(--ease-out) forwards',
'pulse-soft': 'pulse-soft 2s ease-in-out infinite',
'ripple': 'ripple 0.6s ease-out forwards',
'skeleton-pulse': 'skeletonPulse 2s ease-in-out infinite',
'accordion-down': 'accordionDown 0.2s ease-out forwards',
'accordion-up': 'accordionUp 0.2s ease-out forwards',
},
keyframes: {
fadeInUp: {
from: { opacity: '0', transform: 'translateY(24px)' },
to: { opacity: '1', transform: 'translateY(0)' },
},
fadeIn: {
from: { opacity: '0' },
to: { opacity: '1' },
},
slideInLeft: {
from: { opacity: '0', transform: 'translateX(-32px)' },
to: { opacity: '1', transform: 'translateX(0)' },
},
slideInRight: {
from: { opacity: '0', transform: 'translateX(32px)' },
to: { opacity: '1', transform: 'translateX(0)' },
},
scaleIn: {
from: { opacity: '0', transform: 'scale(0.95)' },
to: { opacity: '1', transform: 'scale(1)' },
},
'pulse-soft': {
'0%, 100%': { opacity: '1' },
'50%': { opacity: '0.6' },
},
ripple: {
'0%': { transform: 'scale(0)', opacity: '0.5' },
'100%': { transform: 'scale(4)', opacity: '0' },
},
skeletonPulse: {
'0%, 100%': { backgroundColor: 'var(--color-bg-tertiary)', opacity: '1' },
'50%': { backgroundColor: 'color-mix(in srgb, var(--color-brand) 8%, var(--color-bg-tertiary))', opacity: '0.8' },
},
accordionDown: {
from: { height: '0', opacity: '0' },
to: { height: 'var(--radix-accordion-content-height)', opacity: '1' },
},
accordionUp: {
from: { height: 'var(--radix-accordion-content-height)', opacity: '1' },
to: { height: '0', opacity: '0' },
},
},
},
},
plugins: [
require('tailwindcss-animate'),
],
};