主題系統總覽
Это содержимое пока не доступно на вашем языке.
lh-ui 的主題系統是雙源的,這一點必須先講清楚:
| 源 | 內容 | 消費方式 |
|---|---|---|
styles/theme/common/var.scss + var-dark.scss |
CSS 自訂屬性(--lh-*) |
元件 SCSS 裡 var(--lh-*),執行時求值 |
styles/theme/common/variables.scss |
SCSS 變數($lh-*) |
編譯期烤死成具體值,用於計算(顏色函式、陰影合成等) |
src/theme/themes/index.ts |
5 個預設的 vars 映射 |
JS 執行時注入 <style>(ConfigProvider / ThemeStudio) |
元件只認 CSS 變數。JS 側的主題系統存在的意義是:把一套設計令牌在執行時注入到 DOM 的某個子樹上,讓元件無需重新編譯就換膚。
1. 預設主題(開箱即用)
Section titled “1. 預設主題(開箱即用)”五個內建預設:青花瓷 / 松花綠 / 朱砂 / 水墨 / 藤黃。UI 層用 buildPresetStyleText(vars) 生成 <style> 文字寫入 DOM。
import { LH_THEME_PRESETS, buildPresetStyleText, LH_PRESET_THEME_STYLE_ID } from '@lh-ui/share/theme'
const preset = LH_THEME_PRESETS[0] // 青花瓷const styleText = buildPresetStyleText(preset.vars) // 拼成 CSS 文字// → 注入到 <style id="lh-preset-theme">詳見 主題預設。
2. 執行時客製(themeVars)
Section titled “2. 執行時客製(themeVars)”ConfigProvider 接收 themeVars,鍵為簡寫(自動補 --lh- 前綴):
<lh-config-provider :theme-vars="{ 'primary-color': '#1677ff' }"> <App /></lh-config-provider>詳見 執行時客製。
五個執行時原語
Section titled “五個執行時原語”src/theme/index.ts 匯出(實作在 src/theme/themes/index.ts):
| 原語 | 簽名 | 用途 |
|---|---|---|
LH_THEME_PRESETS |
LhThemePreset[] |
全部預設陣列,docs 與 ConfigProvider 遍歷它 |
registerTheme(preset) |
(preset: LhThemePreset) => void |
註冊或覆蓋預設(同名覆蓋,內建預設也可覆蓋) |
getThemePreset(name) |
(name: LhThemePresetName) => LhThemePreset | undefined |
按名取預設 |
getThemeVars(name) |
(name: LhThemePresetName) => LhThemeVars | undefined |
按名取其 vars(未註冊傳回 undefined) |
buildPresetStyleText(vars) |
(vars: Record<string, string>) => string |
把 vars 拼成可注入的 <style> 文字 |
另有對比度計算原語(verify:theme 與 ThemeStudio 用):
| 原語 | 簽名 | 用途 |
|---|---|---|
getRelativeLuminance(color) |
(color: string | undefined) => number | undefined |
WCAG 相對亮度(非 hex 傳回 undefined) |
getContrastRatio(a, b) |
(a, b: string | undefined) => number | undefined |
WCAG 對比度(1~21) |
buildPresetDarkVars(vars) |
(vars: Record<string, string>) => Record<string, string> |
由預設 vars 推導暗色塊變數 |
LH_PRESET_THEME_STYLE_ID |
'lh-preset-theme' |
注入元素的固定 id |
export type LhThemePresetName = | 'cerblue' | 'songhua' | 'zhusha' | 'shuimo' | 'golden' | (string & {}) // ← 開放自訂名稱,同時保留字面量自動補全
export interface LhThemePreset { name: LhThemePresetName label: string // 中文名稱 labelEn: string // 英文名稱 description: string // 主題描述 vars: Record<string, string> // CSS 變數映射}
export type LhThemeVars = Record<string, string>LhThemePresetName 結尾的 (string & {}) 是 TS 技巧:保持前五個字面量的自動補全,同時允許 registerTheme({ name: 'my-brand', ... }) 傳任意字串。未註冊的名字在 getThemePreset / getThemeVars 中傳回 undefined。
注入約束:只能寫 <style>,不能寫內聯 style
Section titled “注入約束:只能寫 <style>,不能寫內聯 style”這是主題系統最重要的約束,踩過一次坑:
// ✅ 正確:寫 <style> 元素,特異性 0,1,0const el = document.createElement('style')el.id = LH_PRESET_THEME_STYLE_IDel.textContent = buildPresetStyleText(vars)document.head.appendChild(el)
// ❌ 錯誤:寫 documentElement 的內聯 style,特異性 1,0,0,0Object.assign(document.documentElement.style, vars)為什麼:內聯 style 的特異性高於任何樣式表,會壓過 var-dark.scss 裡整段的暗色變數宣告([data-theme='dark'], .dark { --lh-*: ... }),導致暗色模式下配色失真——暗色變數被內聯的明色值蓋死。
buildPresetStyleText 生成的文字結構:
:root { --lh-primary-color: #2B4C7C; /* ...預設的全部 vars */}
[data-theme='dark'],.dark { --lh-primary-color-bg: color-mix(in srgb, var(--lh-primary-color) 25%, transparent); --lh-selected-color: color-mix(in srgb, var(--lh-primary-color) 40%, #fff); /* 深主色預設額外提亮主色 */}暗色塊在 :root 塊之後、同特異性下後者勝——所以淺主色預設在 :root 裡宣告的 --lh-selected-color 明色值不會洩漏進暗色。暗色塊的內容由 buildPresetDarkVars 推導。
buildPresetDarkVars 的暗色推導
Section titled “buildPresetDarkVars 的暗色推導”三個規則:
-
無條件重定義兩個變數(用
var()引用,在套用元素上求值,跟隨當前主色):--lh-primary-color-bg: color-mix(in srgb, var(--lh-primary-color) 25%, transparent)--lh-selected-color: color-mix(in srgb, var(--lh-primary-color) 40%, #fff)
-
深主色提亮:若
--lh-primary-color相對暗色頁面底#1A1A2E(DARK_PAGE_BG)對比度小於 3(DARK_PRIMARY_CONTRAST_THRESHOLD),判定為深主色,額外提亮:--lh-primary-color: color-mix(in srgb, <字面值> 55%, #fff)—— 直接代入字面值,因為color-mix(var(--lh-primary-color) …)會形成 CSS 自引用循環--lh-primary-color-hover: color-mix(in srgb, var(--lh-primary-color) 70%, #fff)--lh-primary-color-active: color-mix(in srgb, var(--lh-primary-color) 85%, #000)--lh-color-on-primary: #101410(INK_ON_LIGHTENED_PRIMARY)—— 提亮後白字對比度反而不足 3:1,改墨字
-
對比度計算用
getContrastRatio(getRelativeLuminance(primary), getRelativeLuminance(DARK_PAGE_BG))。非 hex 值(含var()的動態值)傳回undefined,按「無法判定」處理——所以預設 vars 裡的主色必須是字面 hex。
改內建主題的主色時,兩邊要同步改:
src/theme/themes/index.ts裡預設的vars['--lh-primary-color'](JS 執行時注入用)styles/theme/common/variables.scss裡的$lh-primary-color(元件 SCSS 計算用)
tests/unit/theme.spec.ts 有一致性回歸,跑 pnpm test 會攔不同步。
主題不能做什麼
Section titled “主題不能做什麼”- 不能改控制項高度:高度令牌
--lh-control-height-*尚未建立(各控制項高度不統一,記錄在「高度令牌專項」待後續 changeset)。 - 不能改元件結構:主題只換令牌值,版面配置與 DOM 結構不變。
- 花紋是獨立系統:見 國風花紋。
cerblue · Ceramic Blue青花瓷釉下彩,清雅素净
--lh-primary-color#2B4C7C--lh-primary-color-hover#3D649E--lh-primary-color-active#1F3A5F--lh-primary-color-bg#E8EDF3--lh-primary-color-border#D4DCE8
songhua · Pine Green竹林修竹,生机勃勃
--lh-primary-color#5B8C5A--lh-primary-color-hover#7BB079--lh-primary-color-active#4F8F4C--lh-primary-color-bg#E8F5E8--lh-primary-color-border#D0E8CF--lh-color-on-primary#101410--lh-selected-color#456F44
zhusha · Vermilion传统印章朱砂红,喜庆庄重
--lh-primary-color#B23A3A--lh-primary-color-hover#C45050--lh-primary-color-active#9A3030--lh-primary-color-bg#FDF0F0--lh-primary-color-border#F0D8D8
shuimo · Ink Wash水墨丹青,素雅极简
--lh-primary-color#2C2C2C--lh-primary-color-hover#5A5A5A--lh-primary-color-active#1A1A1A--lh-primary-color-bg#F5F5F5--lh-primary-color-border#E0E0E0
golden · Golden Autumn深秋枫黄,温暖金黄
--lh-primary-color#B8860B--lh-primary-color-hover#D4A843--lh-primary-color-active#A87F0A--lh-primary-color-bg#FBF6EA--lh-primary-color-border#F0E8D0--lh-color-on-primary#1E1A16--lh-selected-color#82600A
Copyright © 2024-2026 FengMo WenXue