feat(theme): 暗黑模式 Phase 1 基础设施 + 首页试点
为 html[data-theme='dark'] 添加完整 CSS 变量覆盖块,建立暗黑模式基石。
--color-ink 收窄为'文字/描边跟随主题'语义,深色 UI 元素(卡片/按钮/遮罩/
代码块/页脚)迁到 --color-dark-bg / --color-overlay 独立 token,避免 ink
反色为浅色文字时连带把深色区块变浅(双语义冲突)。
【基础设施】
- globals.css:
* html/body 顶部硬编码 #FFFFFF !important 改 var() 化(保留 !important)
* :root 新增 --color-overlay / --color-overlay-rgb(遮罩/深色叠加层,
永不反色)
* 新增 html[data-theme='dark'] 覆盖块:墨色反色为浅色文字,bg/text/
border 层级反色,link 反色;保留 --color-dark-* / --color-brand-section
* pre 背景 var(--color-ink) → var(--color-dark-bg)
- tailwind.config.js:注册 overlay 色(rgb() 形式,支持 /50 /60 修饰符)
- layout.tsx:head 内联防 FOUC 同步脚本,读 localStorage['novalon-theme']
设置 data-theme(当前无切换 UI,仅作未来挂载点)
【首页试点 + 布局组件】(浅色值完全等价 → 浅色零视觉差异)
- home-content-v15.tsx:7 处 bg-white → bg-bg-primary(narrative/insights/
cases sections 与卡片),2 处 bg-ink → bg-dark-bg(案例卡 bg-dark-bg 不
随反色,保留深色画布)
- header.tsx / mega-dropdown.tsx:bg-white → bg-bg-primary(导航栏/移动
端菜单/下拉面板)
- footer.tsx:bg-[#0A0E14] → bg-dark-bg(语义统一,视觉零差异)
【全局 UI 组件】(深色区块语义统一 + 遮罩固定深色)
- button.tsx:secondary 'bg-ink hover:bg-ink-light' → bg-dark-bg / hover:bg-dark-bg-secondary;
outline 'hover:bg-ink hover:border-ink' → hover:bg-dark-bg / border-dark-bg
- stats-showcase / milestone-timeline:isDark 分支 bg-ink → bg-dark-bg
- metric-card:isDark 分支 bg-ink/50 → bg-overlay/50,hover:bg-ink → hover:bg-overlay
- dialog / alert-dialog:遮罩 bg-ink/60 → bg-overlay/60(永不反色)
- button.test.tsx:断言 bg-ink → bg-dark-bg,注释同步
【验证】
- type-check 0 errors
- lint 0 errors(127 warnings 既有,非本次引入)
- jest 128 suites / 1623 passed(button 断言同步通过)
- 视觉回归先非 update:21 passed(浅色安全门零 diff)+ 1 failed
(仅 theme-dark-main,dark 快照从浅色名义变真正深色 = 语义修复预期)
- update-snapshots:22 passed,仅 theme-dark-main 基线重写
- computed-style 权威验证(一次性脚本,已删除):
dark 7 项断言全过(html/body/hero/导航栏/案例卡→深色 #0A0E14;
body 文字→浅色 #F8FAFC;footer/overlay→保持深色);light 4 项零回归
【未在 Phase 1 范围】(已识别,留待 Phase 2/3 决策)
- service-detail-content-v4.tsx 等营销页 bg-white:Phase 2 批量 token 化
- admin 页面 bg-white + RichTextEditor:admin 边界决策 + Phase 3
- 品牌红 #C41E3A 在深色底对比度复检:Phase 3
This commit is contained in:
+68
-4
@@ -3,12 +3,12 @@
|
||||
@tailwind utilities;
|
||||
|
||||
html {
|
||||
background-color: #FFFFFF !important;
|
||||
background-color: var(--color-bg-primary) !important;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #FFFFFF !important;
|
||||
color: #0A0E14 !important;
|
||||
background-color: var(--color-bg-primary) !important;
|
||||
color: var(--color-text-primary) !important;
|
||||
}
|
||||
|
||||
:root {
|
||||
@@ -130,6 +130,11 @@ body {
|
||||
--color-dark-text-muted: #94A3B8;
|
||||
--color-dark-border: #1E293B;
|
||||
|
||||
/* 遮罩 / 深色叠加层(dialog 背板、metric-card 深色半透明底):
|
||||
语义上"永远是深色覆盖",不随暗黑模式反色 —— 故用独立 token。 */
|
||||
--color-overlay: #0A0E14;
|
||||
--color-overlay-rgb: 10 14 20;
|
||||
|
||||
/* 阴影系统(按层级 + 用途分类) */
|
||||
/* 基础层级:几乎看不见,用于微妙的边界定义 */
|
||||
--shadow-xs: 0 1px 1px rgba(10, 14, 20, 0.03);
|
||||
@@ -364,6 +369,65 @@ body {
|
||||
--grid-gap-2xl: 3rem;
|
||||
}
|
||||
|
||||
/* === 暗黑模式(html[data-theme='dark'])===
|
||||
策略:仅覆盖 CSS 变量,组件无需逐元素反色。
|
||||
- 文字墨色 --color-ink 反色为浅色(其语义收窄为"文字/描边跟随主题")
|
||||
- 页面底色 --color-bg-* 反色为深色
|
||||
- 深色区块(--color-dark-* / --color-brand-section / --color-overlay / footer)
|
||||
使用独立 token,不随反色 —— 由组件显式引用(如 bg-dark-bg) */
|
||||
html[data-theme='dark'] {
|
||||
/* 墨色反色 → 文字浅色 */
|
||||
--color-ink: #F8FAFC;
|
||||
--color-ink-light: #E2E8F0;
|
||||
--color-ink-lighter: #CBD5E1;
|
||||
--color-ink-rgb: 248 250 252;
|
||||
|
||||
/* 页面底色反色 */
|
||||
--color-bg-primary: #0A0E14;
|
||||
--color-bg-primary-rgb: 10 14 20;
|
||||
--color-bg-secondary: #0F1419;
|
||||
--color-bg-secondary-rgb: 15 20 25;
|
||||
--color-bg-tertiary: #151B23;
|
||||
--color-bg-section: #0F1419;
|
||||
--color-bg-hover: #151B23;
|
||||
--color-bg-elevated: #151B23;
|
||||
|
||||
/* 文字层级反色 */
|
||||
--color-text-primary: #F8FAFC;
|
||||
--color-text-secondary: #CBD5E1;
|
||||
--color-text-secondary-rgb: 203 213 225;
|
||||
--color-text-tertiary: #94A3B8;
|
||||
--color-text-muted: #94A3B8;
|
||||
--color-text-muted-rgb: 148 163 184;
|
||||
--color-text-subtle: #94A3B8;
|
||||
--color-text-placeholder: #94A3B8;
|
||||
--color-text-hint: #94A3B8;
|
||||
--color-text-inverse: #0A0E14;
|
||||
|
||||
/* 边框层级反色 */
|
||||
--color-border-primary: #1E293B;
|
||||
--color-border-primary-rgb: 30 41 59;
|
||||
--color-border-secondary: #334155;
|
||||
--color-border-secondary-rgb: 51 65 85;
|
||||
--color-border-accent: #F8FAFC;
|
||||
--color-border-accent-rgb: 248 250 252;
|
||||
--color-border-light: #1E293B;
|
||||
--color-border-light-rgb: 30 41 59;
|
||||
--color-border-dark: #CBD5E1;
|
||||
--color-border-dark-rgb: 203 213 225;
|
||||
|
||||
/* 链接反色 */
|
||||
--color-link: #F8FAFC;
|
||||
--color-link-hover: #E04A68;
|
||||
}
|
||||
|
||||
/* html/body 背景跟随主题 token(覆盖顶部 !important 硬编码) */
|
||||
html[data-theme='dark'],
|
||||
html[data-theme='dark'] body {
|
||||
background-color: var(--color-bg-primary) !important;
|
||||
color: var(--color-text-primary) !important;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply antialiased;
|
||||
@@ -541,7 +605,7 @@ body {
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--font-size-sm);
|
||||
line-height: var(--line-height-snug);
|
||||
background: var(--color-ink);
|
||||
background: var(--color-dark-bg);
|
||||
color: var(--color-dark-text-primary);
|
||||
padding: 1.5em;
|
||||
border-radius: var(--radius-lg);
|
||||
|
||||
Reference in New Issue
Block a user