task-241(admin.html观感对齐): 蓝白浅色 token 基线翻转(浅色侧栏+蓝灰主色 #4f78a5)
This commit is contained in:
@@ -1,13 +1,22 @@
|
||||
:root {
|
||||
--admin-sidebar: #192132;
|
||||
--admin-sidebar-deep: #121827;
|
||||
--admin-primary: #6366f1;
|
||||
--admin-primary-soft: #eef0ff;
|
||||
--admin-bg: #f3f5f9;
|
||||
--admin-border: #e5e7ef;
|
||||
--admin-text: #1f2937;
|
||||
--admin-muted: #7b8495;
|
||||
font-family: "Microsoft YaHei", "PingFang SC", "Helvetica Neue", sans-serif;
|
||||
--admin-sidebar: #f8fbfe;
|
||||
--admin-sidebar-deep: #edf3f9;
|
||||
--admin-primary: #4f78a5;
|
||||
--admin-primary-soft: #e7f0f8;
|
||||
--admin-bg: #f4f7fb;
|
||||
--admin-border: #d8e3ee;
|
||||
--admin-text: #24384d;
|
||||
--admin-muted: #5b6f83;
|
||||
font-family: Inter, "SF Pro Display", "Microsoft YaHei", "PingFang SC", "Helvetica Neue", sans-serif;
|
||||
/* Element Plus 主色对齐 admin.html 蓝白浅色皮肤(渐变主键 #5f85ad→#4f78a5) */
|
||||
--el-color-primary: #4f78a5;
|
||||
--el-color-primary-light-3: #7d9abf;
|
||||
--el-color-primary-light-5: #a5bad2;
|
||||
--el-color-primary-light-7: #cddbe6;
|
||||
--el-color-primary-light-8: #e1e9f0;
|
||||
--el-color-primary-light-9: #f0f4f8;
|
||||
--el-color-primary-dark-2: #3f628a;
|
||||
--el-border-radius-base: 6px;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
/**
|
||||
* Admin 主题 token 单源(任务 4):深色侧边栏 + 浅色内容区。
|
||||
* Admin 主题 token 单源(任务 4 契约,module 13 task 241 翻转为蓝白浅色)。
|
||||
* 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',
|
||||
sidebar: '#f8fbfe',
|
||||
sidebarDeep: '#edf3f9',
|
||||
primary: '#4f78a5',
|
||||
primarySoft: '#e7f0f8',
|
||||
bg: '#f4f7fb',
|
||||
border: '#d8e3ee',
|
||||
text: '#24384d',
|
||||
muted: '#5b6f83',
|
||||
} as const
|
||||
|
||||
export type AdminThemeKey = keyof typeof ADMIN_THEME
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { readSource } from './helpers.ts'
|
||||
import {
|
||||
ADMIN_THEME,
|
||||
ADMIN_THEME_KEYS,
|
||||
contrastRatio,
|
||||
isDarkSurface,
|
||||
relativeLuminance,
|
||||
tokenHex,
|
||||
} from '../src/styles/theme.ts'
|
||||
|
||||
// module 13 task 241:蓝白浅色基线翻转。旧「深色侧栏 + indigo」token 需整体替换为
|
||||
// admin.html 末层(L4478)生效值:浅色侧栏、蓝灰主色 #4f78a5、淡蓝内容底。
|
||||
|
||||
test('test_task_241_baseline_normal_primary_path', () => {
|
||||
// 正常主路径:token 命中 admin.html 蓝白浅色基线的关键 hex。
|
||||
assert.equal(ADMIN_THEME.sidebar, '#f8fbfe', '侧栏顶色应为浅蓝白')
|
||||
assert.equal(ADMIN_THEME.sidebarDeep, '#edf3f9', '侧栏底/加深色应为浅蓝灰')
|
||||
assert.equal(ADMIN_THEME.primary, '#4f78a5', '主色应为蓝灰(非旧 indigo)')
|
||||
assert.equal(ADMIN_THEME.primarySoft, '#e7f0f8', '主色软底应为浅蓝')
|
||||
assert.equal(ADMIN_THEME.bg, '#f4f7fb', '内容底应为淡蓝')
|
||||
assert.equal(ADMIN_THEME.text, '#24384d', '正文应为深蓝灰')
|
||||
})
|
||||
|
||||
test('test_task_241_baseline_normal_variant_input', () => {
|
||||
// 正常变体:侧栏与内容区均为浅色(翻转核心语义),不再要求侧栏为暗。
|
||||
assert.equal(isDarkSurface(ADMIN_THEME.sidebar), false, '侧栏顶色必须为浅色')
|
||||
assert.equal(isDarkSurface(ADMIN_THEME.sidebarDeep), false, '侧栏底必须为浅色')
|
||||
assert.equal(isDarkSurface(ADMIN_THEME.bg), false, '内容区必须为浅色')
|
||||
assert.equal(isDarkSurface(ADMIN_THEME.border), false, '边框须为亮面中性色')
|
||||
})
|
||||
|
||||
test('test_task_241_baseline_normal_repeated_operation_is_idempotent', () => {
|
||||
// 正常重复:浅色判定与对比度幂等。
|
||||
assert.equal(isDarkSurface(ADMIN_THEME.sidebar), isDarkSurface(ADMIN_THEME.sidebar))
|
||||
assert.equal(relativeLuminance(ADMIN_THEME.bg), relativeLuminance(ADMIN_THEME.bg))
|
||||
})
|
||||
|
||||
test('test_task_241_baseline_boundary_empty_input', () => {
|
||||
// 边界:token 键集合不变(防丢语义键),全部 #rrggbb。
|
||||
assert.ok(ADMIN_THEME_KEYS.length >= 7, '必须覆盖侧栏/内容区/正文等核心语义面')
|
||||
for (const key of ADMIN_THEME_KEYS) {
|
||||
assert.match(ADMIN_THEME[key], /^#[0-9a-fA-F]{6}$/, `${key} 须为 #rrggbb`)
|
||||
}
|
||||
})
|
||||
|
||||
test('test_task_241_baseline_boundary_single_item', () => {
|
||||
// 边界单元素:侧栏与内容区仍是两个可区分的浅色面(不得同色)。
|
||||
assert.notEqual(ADMIN_THEME.sidebar, ADMIN_THEME.bg, '侧栏与内容底必须可区分')
|
||||
assert.notEqual(ADMIN_THEME.sidebar, ADMIN_THEME.sidebarDeep)
|
||||
assert.equal(isDarkSurface(ADMIN_THEME.sidebar), false)
|
||||
})
|
||||
|
||||
test('test_task_241_baseline_boundary_limit_or_missing_field', () => {
|
||||
// 边界上限:正文/次要文本在浅色底上满足 WCAG AA;CSS 变量名映射稳定。
|
||||
assert.ok(contrastRatio(ADMIN_THEME.text, ADMIN_THEME.bg) >= 4.5, '正文/内容区需 ≥4.5')
|
||||
assert.ok(contrastRatio(ADMIN_THEME.primary, ADMIN_THEME.bg) >= 3.0, '主色按钮/内容区 ≥3')
|
||||
assert.ok(contrastRatio(ADMIN_THEME.muted, ADMIN_THEME.bg) >= 3.0, '次要文本 ≥3')
|
||||
assert.equal(tokenHex('sidebar'), '#f8fbfe')
|
||||
})
|
||||
|
||||
test('test_task_241_baseline_invalid_input_rejected', () => {
|
||||
// 异常:theme.ts 不得残留旧 indigo 主色与深色侧栏值(防只改一半漂移)。
|
||||
const ts = readSource('src/styles/theme.ts')
|
||||
assert.equal(/#6366f1/.test(ts), false, 'theme.ts 不得残留旧 indigo #6366f1')
|
||||
assert.equal(/#192132|#121827/.test(ts), false, 'theme.ts 不得残留旧深色侧栏')
|
||||
assert.throws(() => tokenHex('unknown' as never), /未知 Admin 主题 token/)
|
||||
})
|
||||
|
||||
test('test_task_241_baseline_dependency_failure_returns_actionable_message', () => {
|
||||
// 依赖失败:theme.ts 与 main.css 渲染变量交叉校验一致(防双源漂移)。
|
||||
const css = readSource('src/styles/main.css')
|
||||
const missing = ADMIN_THEME_KEYS.filter((key) => {
|
||||
const tokenVar = `--admin-${key.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`)}`
|
||||
return !css.includes(`${tokenVar}: ${ADMIN_THEME[key]}`)
|
||||
})
|
||||
assert.deepEqual(missing, [], `theme.ts 与 main.css 不一致,缺失变量: ${missing.join(', ')}`)
|
||||
assert.equal(/@import/.test(css), false, 'main.css 不得 @import 旧后台整份样式')
|
||||
})
|
||||
@@ -12,16 +12,16 @@ import {
|
||||
} from '../src/styles/theme.ts'
|
||||
|
||||
test('test_task_004_theme_tokens_normal_primary_path', () => {
|
||||
// 正常主路径:侧边栏与加深面是暗色,内容区/卡片底是亮色。
|
||||
assert.equal(isDarkSurface(ADMIN_THEME.sidebar), true, '侧边栏必须为暗色')
|
||||
assert.equal(isDarkSurface(ADMIN_THEME.sidebarDeep), true, '侧边栏加深面必须为暗色')
|
||||
// 正常主路径(module 13 翻转):侧边栏与内容区均为浅色,主色为蓝灰。
|
||||
assert.equal(isDarkSurface(ADMIN_THEME.sidebar), false, '侧边栏必须为浅色')
|
||||
assert.equal(isDarkSurface(ADMIN_THEME.sidebarDeep), false, '侧边栏加深面必须为浅色')
|
||||
assert.equal(isDarkSurface(ADMIN_THEME.bg), false, '内容区背景必须为亮色')
|
||||
assert.equal(isDarkSurface(ADMIN_THEME.border), false, '边框须为亮面中性色')
|
||||
})
|
||||
|
||||
test('test_task_004_theme_tokens_normal_variant_input', () => {
|
||||
// 正常变体:强调色与软底/正文高对比,正文 token 自成一致体系。
|
||||
assert.equal(relativeLuminance(ADMIN_THEME.primary), relativeLuminance('#6366f1'))
|
||||
// 正常变体:主色为蓝灰基线、正文 token 自成一致体系。
|
||||
assert.equal(relativeLuminance(ADMIN_THEME.primary), relativeLuminance('#4f78a5'))
|
||||
assert.ok(contrastRatio(ADMIN_THEME.text, ADMIN_THEME.bg) >= 4.5, '正文/内容区需满足 WCAG AA 正文')
|
||||
assert.ok(contrastRatio(ADMIN_THEME.primary, ADMIN_THEME.bg) >= 3.0, '品牌色按钮须达到 3:1')
|
||||
})
|
||||
@@ -42,9 +42,10 @@ test('test_task_004_theme_tokens_boundary_empty_input', () => {
|
||||
})
|
||||
|
||||
test('test_task_004_theme_tokens_boundary_single_item', () => {
|
||||
// 边界单元素:单个中性边界 token 与内容区可区分;侧边栏深于内容区。
|
||||
// 边界单元素:单个中性边界 token 与内容区可区分;侧栏为独立浅色面(不再要求深于内容区)。
|
||||
assert.notEqual(ADMIN_THEME.border, ADMIN_THEME.bg)
|
||||
assert.ok(relativeLuminance(ADMIN_THEME.sidebar) < relativeLuminance(ADMIN_THEME.bg))
|
||||
assert.notEqual(ADMIN_THEME.sidebar, ADMIN_THEME.bg)
|
||||
assert.equal(isDarkSurface(ADMIN_THEME.sidebar), false, '侧栏须保持浅色')
|
||||
})
|
||||
|
||||
test('test_task_004_theme_tokens_boundary_limit_or_missing_field', () => {
|
||||
|
||||
Reference in New Issue
Block a user