Files
crawler-plugin/admin-frontend-vue/src/styles/theme.ts
T
huangzd1997 af68b20f87 task-4(壳层/路由): 定义深色侧边栏与浅色内容区设计 token 并锁定不变量
新增 src/styles/theme.ts 语义 token 单源 + 相对亮度/对比度/isDarkSurface,
tests 校验侧边栏暗、内容区亮、正文 WCAG AA、与 main.css 变量交叉一致防漂移。
2026-09-05 14:43:52 +08:00

70 lines
2.4 KiB
TypeScript
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.
/**
* Admin 主题 token 单源(任务 4):深色侧边栏 + 浅色内容区。
* main.css 使用同名 --admin-* CSS 变量承载渲染值;tests 用本模块校验
* “侧边栏为暗、内容区为亮、正文对比度达标”的不变量,防止误改成浅色侧栏。
*/
export const ADMIN_THEME = {
sidebar: '#192132',
sidebarDeep: '#121827',
primary: '#6366f1',
primarySoft: '#eef0ff',
bg: '#f3f5f9',
border: '#e5e7ef',
text: '#1f2937',
muted: '#7b8495',
} as const
export type AdminThemeKey = keyof typeof ADMIN_THEME
export const ADMIN_THEME_KEYS = Object.keys(ADMIN_THEME) as AdminThemeKey[]
/** CSS 变量名:camelCase -> kebabsidebarDeep -> --admin-sidebar-deep)。 */
export function cssVarName(key: AdminThemeKey): string {
const kebab = key.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`)
return `--admin-${kebab}`
}
function parseHex(hex: string): { r: number; g: number; b: number } {
if (!/^#[0-9a-fA-F]{6}$/.test(hex)) {
throw new Error(`主题色必须是 #rrggbb 六位十六进制: ${hex}`)
}
const raw = hex.slice(1)
return {
r: parseInt(raw.slice(0, 2), 16),
g: parseInt(raw.slice(2, 4), 16),
b: parseInt(raw.slice(4, 6), 16),
}
}
/** sRGB 通道 -> 线性亮度。 */
function channelLuminance(channel: number): number {
const s = channel / 255
return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4
}
/** 相对亮度(0 纯黑 ~ 1 纯白)。 */
export function relativeLuminance(hex: string): number {
const { r, g, b } = parseHex(hex)
return 0.2126 * channelLuminance(r) + 0.7152 * channelLuminance(g) + 0.0722 * channelLuminance(b)
}
export function isDarkSurface(hex: string, threshold = 0.2): boolean {
return relativeLuminance(hex) < threshold
}
/** WCAG 对比度(1 ~ 21),fg/bg 可为 8 位 hex。 */
export function contrastRatio(fgHex: string, bgHex: string): number {
const lighter = Math.max(relativeLuminance(fgHex), relativeLuminance(bgHex))
const darker = Math.min(relativeLuminance(fgHex), relativeLuminance(bgHex))
return (lighter + 0.05) / (darker + 0.05)
}
/** 取某个语义 token 的 hex;未知 key 给出可操作错误。 */
export function tokenHex(key: AdminThemeKey): string {
const value = ADMIN_THEME[key]
if (!value) {
throw new Error(`未知 Admin 主题 token: ${String(key)},允许值: ${ADMIN_THEME_KEYS.join(', ')}`)
}
return value
}