修复 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 ✓
80 lines
3.0 KiB
JavaScript
80 lines
3.0 KiB
JavaScript
/**
|
|
* 模拟点深色模式按钮后的 header 截图
|
|
* 找切换按钮:theme toggle / data-theme / aria-label 含亮暗
|
|
*/
|
|
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', url: 'https://www.novalon.cn/' },
|
|
{ tag: 'local', 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,
|
|
});
|
|
const page = await ctx.newPage();
|
|
try {
|
|
await page.goto(t.url, { waitUntil: 'networkidle', timeout: 60000 });
|
|
await page.waitForTimeout(1500);
|
|
|
|
// 1) 找主题切换按钮
|
|
const btnInfo = await page.evaluate(() => {
|
|
const candidates = [
|
|
...document.querySelectorAll('header button, header [role="button"], [data-theme-toggle], [aria-label*="深" i], [aria-label*="暗" i], [aria-label*="dark" i], [aria-label*="theme" i], [aria-label*="切换" i]'),
|
|
];
|
|
return candidates.map(b => ({
|
|
tag: b.tagName,
|
|
label: b.getAttribute('aria-label'),
|
|
text: (b.textContent || '').slice(0, 30),
|
|
className: (b.className || '').toString().slice(0, 60),
|
|
data: JSON.stringify(b.dataset).slice(0, 80),
|
|
})).slice(0, 6);
|
|
});
|
|
console.log(`[${t.tag}] 候选切换按钮:`, JSON.stringify(btnInfo, null, 2));
|
|
|
|
// 2) 直接通过 localStorage 强制 dark(更稳)
|
|
await page.evaluate(() => {
|
|
try { localStorage.setItem('novalon-theme', 'dark'); } catch {}
|
|
});
|
|
await page.reload({ waitUntil: 'networkidle' });
|
|
await page.waitForTimeout(1500);
|
|
|
|
// 3) 截深色模式下的 header
|
|
const header = page.locator('header').first();
|
|
await header.screenshot({ path: path.join(OUT, `${t.tag}-dark-header.png`) }).catch(async () => {
|
|
await page.screenshot({ path: path.join(OUT, `${t.tag}-dark-header.png`), clip: { x: 0, y: 0, width: 1440, height: 80 } });
|
|
});
|
|
|
|
// 4) 探测 logo 在深色模式下的实际颜色
|
|
const logoState = await page.evaluate(() => {
|
|
const img = [...document.querySelectorAll('img')].find(i => /logo\.svg$/.test(i.getAttribute('src') || ''));
|
|
if (!img) return { found: false };
|
|
const r = img.getBoundingClientRect();
|
|
const parent = img.closest('header') || document.body;
|
|
return {
|
|
found: true,
|
|
src: img.getAttribute('src'),
|
|
renderedW: Math.round(r.width),
|
|
renderedH: Math.round(r.height),
|
|
parentBg: getComputedStyle(parent).backgroundColor,
|
|
htmlClass: document.documentElement.className,
|
|
htmlDataTheme: document.documentElement.getAttribute('data-theme'),
|
|
};
|
|
});
|
|
console.log(`[${t.tag}] dark-mode logo state:`, JSON.stringify(logoState, null, 2));
|
|
} catch (e) {
|
|
console.log(`[${t.tag}] FAILED ${e.message.slice(0, 200)}`);
|
|
}
|
|
await ctx.close();
|
|
}
|
|
await browser.close();
|
|
console.log('\n✓ 输出:' + OUT);
|