From 2a8b81f042b230a3b5fa12d858f8c404a8b50963 Mon Sep 17 00:00:00 2001 From: zhangxiang Date: Wed, 2 Sep 2026 11:37:24 +0800 Subject: [PATCH] =?UTF-8?q?fix(build):=20=E6=94=B9=20darkMode=20=E4=B8=BA?= =?UTF-8?q?=20variant=20=E6=A8=A1=E5=BC=8F=E8=AE=A9=20Tailwind=20dark:=20?= =?UTF-8?q?=E5=8F=98=E4=BD=93=E6=8C=89=20data-theme=3D'dark'=20=E8=A7=A6?= =?UTF-8?q?=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - tailwind.config.js darkMode 从 'class' 改为 ['variant', '[data-theme="dark"] &'] - 'class' 模式匹配 .dark class,但站点用 html[data-theme='dark'] 属性,导致 dark: 变体全局失效 - variant 模式让 dark:bg-foo 类生成 [data-theme="dark"] .dark\:bg-foo 选择器 - 验证:临时加 dark:bg-blue-500 → CSS 含正确选择器;运行时浅色 bg=white、深色 bg=rgb(59,130,246) 回归测试脚本: - darkmode-selector-verify.mjs:合成探针验证 utility 生效 - home-cta-verify.mjs:定位 home 本地简化 CTASection 实际渲染(之前误判为'不渲染') 诚实标注: - 全站 grep dark: utility class 仅发现 cta-button.tsx 2 处,但均为 TS 对象 key(非 utility),故 dark: 失效问题无直接破坏 - 修复属'基础设施补全':未来添加 dark: 工具类即按 data-theme='dark' 自动生效 home-content-v15.tsx 的 CTASection(line 978-998)是**文件本地简化版** (仅标题 + CTAButton,无 BrandStamp),与 src/components/sections/cta-section.tsx (含 BrandStamp「值得信赖」)是不同组件。前者实际渲染,后者无路由使用(待用资产)。 本修复不涉及 CTASection 渲染路径。 验证: - type-check 0 error - lint 0 error - 编译产物含 [data-theme="dark"] .dark\:bg-blue-500 选择器 - 运行时合成探针 PASS(浅色 white / 深色 blue) --- scripts/audit/darkmode-selector-verify.mjs | 42 ++++++++++++++++++++++ scripts/audit/home-cta-verify.mjs | 25 +++++++++++++ tailwind.config.js | 2 +- 3 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 scripts/audit/darkmode-selector-verify.mjs create mode 100644 scripts/audit/home-cta-verify.mjs diff --git a/scripts/audit/darkmode-selector-verify.mjs b/scripts/audit/darkmode-selector-verify.mjs new file mode 100644 index 0000000..16b96da --- /dev/null +++ b/scripts/audit/darkmode-selector-verify.mjs @@ -0,0 +1,42 @@ +// 验证 darkMode selector 配置生效 +import { chromium } from 'playwright'; +const browser = await chromium.launch(); + +async function probe(theme) { + const ctx = await browser.newContext({ viewport: { width: 1280, height: 800 } }); + const page = await ctx.newPage(); + // 先建立 origin + await page.goto('http://127.0.0.1:3000/', { waitUntil: 'domcontentloaded' }); + await page.evaluate((t) => { + if (t === 'dark') localStorage.setItem('novalon-theme', 'dark'); + else localStorage.removeItem('novalon-theme'); + }, theme); + // 重新加载让内联脚本读 localStorage 设置 data-theme + await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' }); + await page.waitForTimeout(1500); + const out = await page.evaluate(() => { + const probe = document.createElement('div'); + probe.className = 'bg-white dark:bg-blue-500'; + probe.style.cssText = 'position:fixed;top:-9999px;width:50px;height:50px;'; + document.body.appendChild(probe); + const bg = getComputedStyle(probe).backgroundColor; + const htmlTheme = document.documentElement.getAttribute('data-theme'); + probe.remove(); + return { bg, htmlTheme }; + }); + await ctx.close(); + return out; +} + +const light = await probe('light'); +const dark = await probe('dark'); +await browser.close(); + +console.log('light:', JSON.stringify(light)); +console.log('dark :', JSON.stringify(dark)); + +const lightOk = light.bg === 'rgb(255, 255, 255)' && light.htmlTheme !== 'dark'; +const darkOk = dark.bg === 'rgb(59, 130, 246)' && dark.htmlTheme === 'dark'; +console.log(`\n[assert] light bg=white, no data-theme: ${lightOk}`); +console.log(`[assert] dark bg=blue, data-theme=dark: ${darkOk}`); +console.log(`[result] ${lightOk && darkOk ? 'PASS' : 'FAIL'}`); \ No newline at end of file diff --git a/scripts/audit/home-cta-verify.mjs b/scripts/audit/home-cta-verify.mjs new file mode 100644 index 0000000..c355841 --- /dev/null +++ b/scripts/audit/home-cta-verify.mjs @@ -0,0 +1,25 @@ +// 验证 home 本地 CTASection 实际渲染(含 BrandStamp 的是 src/components/sections/cta-section.tsx) +import { chromium } from 'playwright'; +const browser = await chromium.launch(); +const page = await browser.newPage({ viewport: { width: 1280, height: 800 } }); +await page.goto('http://127.0.0.1:3000/', { waitUntil: 'networkidle' }); +await page.waitForTimeout(2000); +await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)); +await page.waitForTimeout(2000); + +const dump = await page.evaluate(() => { + // home 本地简化版的特征 className + const homeCta = document.querySelector('section.relative.py-24.sm\\:py-32'); + // 公开 cta-section 的特征 id + const srcCta = document.querySelector('section#cta'); + // BrandStamp 容器特征(inline-block px-3 py-1.5 border-2 border-[var(--color-brand)] rounded-sm) + const stamp = document.querySelector('.inline-block.px-3.py-1\\.5.border-2'); + return { + homeCtaPresent: !!homeCta, + homeCtaText: homeCta ? (homeCta.textContent || '').slice(0, 60) : null, + srcCtaPresent: !!srcCta, + brandStampPresent: !!stamp, + }; +}); +await browser.close(); +console.log(JSON.stringify(dump, null, 2)); \ No newline at end of file diff --git a/tailwind.config.js b/tailwind.config.js index db4f3ff..799ba70 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -5,7 +5,7 @@ module.exports = { './src/components/**/*.{js,ts,jsx,tsx,mdx}', './src/app/**/*.{js,ts,jsx,tsx,mdx}', ], - darkMode: 'class', + darkMode: ['variant', '[data-theme="dark"] &'], theme: { extend: { colors: {