48 lines
2.9 KiB
JavaScript
48 lines
2.9 KiB
JavaScript
import { loadThemes, resolve, ratio } from './contrast-audit.mjs';
|
|
|
|
/*** 表面阶梯审计:同一条阶梯上相邻两档必须有肉眼可辨的亮度差
|
|
* —— 对比度门禁只管「文字 vs 底色」,管不到「底色 vs 底色」;而悬停底 / 按下底 / 凹陷底
|
|
* 全是底色对底色,差 1% 就等于用户看不出状态变化(历史实测表面→二阶表面仅 1.008)。 ***/
|
|
|
|
export const LADDER = [
|
|
{ label: '页面底 → 表面', from: 'bg', to: 'surface', min: 1.04 }, // 卡片与页底:靠边框也能分,阈值最低
|
|
{ label: '表面 → 悬停底', from: 'surface', to: 'surface-2', min: 1.04 }, // 悬停必须有反馈,低一档就白改
|
|
{ label: '悬停底 → 按下底', from: 'surface-2', to: 'surface-3', min: 1.04 }, // 按下要比悬停更实,两档不能糊在一起
|
|
{ label: '表面 → 凹陷底', from: 'surface', to: 'surface-inset', min: 1.02 }, // 凹陷区自带边框与内阴影,可放宽一档
|
|
];
|
|
|
|
/*** 作用:按四套主题(亮 / 暗 × 常规 / 高对比)逐档算亮度比,返回明细与未达标项 ***/
|
|
export function auditSurface(root) {
|
|
const themes = loadThemes(root);
|
|
const rows = [];
|
|
for (const [theme, vars] of Object.entries(themes)) {
|
|
for (const step of LADDER) {
|
|
const from = resolve(vars.get(`--ua-color-${step.from}`), vars);
|
|
const to = resolve(vars.get(`--ua-color-${step.to}`), vars);
|
|
const value = ratio(to, from);
|
|
rows.push({ theme, label: step.label, from: step.from, to: step.to, min: step.min, ratio: value, bad: value < step.min });
|
|
}
|
|
}
|
|
return { rows, failures: rows.filter((r) => r.bad) };
|
|
}
|
|
|
|
/*** 作用:按终端显示宽度补空格(中文占两格,padEnd 只按字符数补,表头会歪) ***/
|
|
const pad = (text, width) => {
|
|
const wide = [...text].reduce((n, ch) => n + (ch.codePointAt(0) > 0x2e80 ? 2 : 1), 0);
|
|
return text + ' '.repeat(Math.max(0, width - wide));
|
|
};
|
|
|
|
/*** 作用:把审计结果渲染成对齐的文本表(--json 之外的人读视图) ***/
|
|
export function formatSurfaceReport({ rows, failures }) {
|
|
const themes = [...new Set(rows.map((r) => r.theme))];
|
|
const lines = ['表面阶梯亮度比(1.00 = 完全看不出差别)', `${pad('阶梯', 22)}${themes.map((t) => t.padStart(14)).join('')} 阈值 结论`];
|
|
for (const step of LADDER) {
|
|
const cells = themes.map((t) => rows.find((r) => r.theme === t && r.label === step.label).ratio.toFixed(3).padStart(14));
|
|
const bad = rows.filter((r) => r.label === step.label && r.bad).map((r) => r.theme);
|
|
lines.push(`${pad(step.label, 22)}${cells.join('')}${String(step.min).padStart(6)} ${bad.length ? `FAIL(${bad.join('+')})` : 'OK'}`);
|
|
}
|
|
lines.push('');
|
|
lines.push(failures.length === 0 ? '表面阶梯全部达标' : `不达标 ${failures.length} 处:${failures.map((f) => `${f.theme}/${f.label}`).join(' / ')}`);
|
|
return lines.join('\n');
|
|
}
|