Files
zhangxiang 918d845aef fix(ui,a11y): 修 logo 深色模式不可见 + 补 tailwind font-calligraphy 工具类
修复 A:header logo 深色模式字被吞
- logo.svg 右侧公司名(睿新致遠 + NOVALON)硬编码 fill='#0A0E14',
  深色背景下 background 同色不可见,红色印章下书法字与英文全部被吞。
- 复用既有 logo-white.svg(白字 + 红印章)作为深色版,
  header 渲染双 <Image>,按 html[data-theme] CSS 显隐。
- 双 Image 不占位(display:none),零 JS 闪烁,未来主题切换器自动生效。

修复 B:font-calligraphy 工具类不生成
- tailwind.config.js 的 fontFamily 此前未声明 calligraphy key,
  导致 font-calligraphy utility 永不生成,4 处装饰书法字
  (BrandStamp「值得信赖」/ ChallengeSection「使命」/ WhyUsSection「睿」「我们」)
  静默回退 sans。
- fontFamily 加 calligraphy: ['var(--font-calligraphy-system)']。

审计脚本:
- logo-compare*.mjs:生产 vs 本地 logo 对比定位
- logo-dark-verify.mjs:明暗双版渲染验证
- font-calligraphy-verify.mjs:合成探针验证 utility 生效

验证:
- type-check 0 error · lint 0 error(127 warnings 既有项与本次无关)
- 深浅色 header 截图:明暗版正确切换,书法字 + NOVALON 清晰可见
- font-calligraphy 合成探针 computed fontFamily = STKaiti, KaiTi, 楷体, SimKai, serif ✓
2026-09-20 11:50:58 +08:00

63 lines
2.2 KiB
JavaScript

/**
* 深色模式 logo 对比:生产 vs 本地
* 强制 prefers-color-scheme: dark,截 header logo
*/
import { chromium } from 'playwright';
import fs from 'node:fs';
import path from 'node:path';
const OUT = path.join(process.cwd(), 'dogfood-ui-audit', 'logo-compare');
fs.mkdirSync(OUT, { recursive: true });
const TARGETS = [
{ tag: 'prod-dark', url: 'https://www.novalon.cn/' },
{ tag: 'local-dark', url: 'http://127.0.0.1:3000/' },
];
const browser = await chromium.launch();
for (const t of TARGETS) {
const ctx = await browser.newContext({
viewport: { width: 1440, height: 900 },
deviceScaleFactor: 2,
colorScheme: 'dark', // 强制深色模式
});
const page = await ctx.newPage();
try {
await page.goto(t.url, { waitUntil: 'networkidle', timeout: 60000 });
await page.waitForTimeout(1500);
// 1) 截 header
const header = page.locator('header').first();
await header.screenshot({ path: path.join(OUT, `${t.tag}-header.png`) }).catch(async () => {
await page.screenshot({ path: path.join(OUT, `${t.tag}-header.png`), clip: { x: 0, y: 0, width: 1440, height: 80 } });
});
// 2) 探测 logo 元素的可视状态(在深色背景下)
const info = await page.evaluate(() => {
const imgs = [...document.querySelectorAll('img')].filter(i => /logo\.svg$/.test(i.getAttribute('src') || ''));
return imgs.map(i => {
const r = i.getBoundingClientRect();
const cs = getComputedStyle(i);
const parentCs = getComputedStyle(i.closest('header') || document.body);
return {
src: i.getAttribute('src'),
renderedW: Math.round(r.width),
renderedH: Math.round(r.height),
filter: cs.filter,
opacity: cs.opacity,
visibility: cs.visibility,
parentBg: parentCs.backgroundColor,
htmlBg: getComputedStyle(document.documentElement).backgroundColor,
};
});
});
console.log(`[${t.tag}]`);
for (const l of info) console.log(' ' + JSON.stringify(l));
} catch (e) {
console.log(`[${t.tag}] FAILED ${e.message.slice(0, 120)}`);
}
await ctx.close();
}
await browser.close();
console.log('\n✓ 输出目录:' + OUT);