diff --git a/docs/color-heading-optimization-report.md b/docs/color-heading-optimization-report.md new file mode 100644 index 0000000..3076d27 --- /dev/null +++ b/docs/color-heading-optimization-report.md @@ -0,0 +1,3 @@ +# 颜色对比度和标题层级优化报告 +## 项目概述 +本报告记录了Novalon网站的颜色对比度和标题层级结构优化工作,旨在提升网站的可访问性(WCAG 2.1 AA标准)和SEO性能。 diff --git a/docs/heading-hierarchy-guidelines.md b/docs/heading-hierarchy-guidelines.md new file mode 100644 index 0000000..7dca8ad --- /dev/null +++ b/docs/heading-hierarchy-guidelines.md @@ -0,0 +1 @@ +# 标题层级结构规范 diff --git a/package.json b/package.json index 83d2d9a..228a7af 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,8 @@ "test": "playwright test", "test:smoke": "playwright test --grep @smoke", "test:report": "allure generate test-results/allure-results && allure open", + "check:contrast": "tsx scripts/check-color-contrast.ts", + "check:headings": "tsx scripts/check-heading-hierarchy.ts", "audit:performance": "node scripts/performance-audit.js", "audit:seo": "node scripts/seo-check.js", "audit:accessibility": "node scripts/accessibility-test.js", @@ -19,6 +21,7 @@ }, "dependencies": { "@antv/g2": "^5.4.8", + "@playwright/test": "^1.58.2", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", "@types/three": "^0.183.1", @@ -48,6 +51,7 @@ "eslint-config-next": "^0.2.4", "lighthouse": "^13.0.3", "tailwindcss": "^4", + "tsx": "^4.21.0", "typescript": "^5" } } diff --git a/scripts/check-color-contrast.ts b/scripts/check-color-contrast.ts new file mode 100644 index 0000000..9158310 --- /dev/null +++ b/scripts/check-color-contrast.ts @@ -0,0 +1,62 @@ +import { calculateContrastRatio, meetsWCAGStandard } from '../src/lib/color-contrast.ts'; + +interface ColorPair { + name: string; + foreground: string; + background: string; + textSize?: 'normal' | 'large'; +} + +const criticalColorPairs: ColorPair[] = [ + { name: 'Primary text on primary background', foreground: '#1C1C1C', background: '#FFFFFF' }, + { name: 'Secondary text on primary background', foreground: '#3D3D3D', background: '#FFFFFF' }, + { name: 'Tertiary text on primary background', foreground: '#4A4A4A', background: '#FFFFFF' }, + { name: 'Muted text on primary background', foreground: '#6B6B6B', background: '#FFFFFF' }, + { name: 'Brand primary on white', foreground: '#C41E3A', background: '#FFFFFF' }, + { name: 'Brand primary on brand bg', foreground: '#C41E3A', background: '#FEF2F4' }, + { name: 'Link on hover', foreground: '#C41E3A', background: '#FFFFFF' }, +]; + +function checkContrast() { + console.log('🎨 Color Contrast Check\n'); + + let failures = 0; + let passes = 0; + + criticalColorPairs.forEach(pair => { + const result = meetsWCAGStandard( + pair.foreground, + pair.background, + 'AA', + pair.textSize || 'normal' + ); + + const status = result.passes ? '✅ PASS' : '❌ FAIL'; + const ratio = result.ratio.toFixed(2); + + console.log(`${status} ${pair.name}`); + console.log(` Ratio: ${ratio}:1 (Required: ${result.requiredRatio}:1)`); + console.log(` FG: ${pair.foreground} | BG: ${pair.background}\n`); + + if (result.passes) { + passes++; + } else { + failures++; + } + }); + + console.log(`\n📊 Summary:`); + console.log(` Total: ${criticalColorPairs.length}`); + console.log(` ✅ Passes: ${passes}`); + console.log(` ❌ Failures: ${failures}`); + + if (failures > 0) { + console.log('\n⚠️ Some color pairs do not meet WCAG AA standards!'); + process.exit(1); + } else { + console.log('\n✅ All color pairs meet WCAG AA standards!'); + process.exit(0); + } +} + +checkContrast(); diff --git a/scripts/check-heading-hierarchy.ts b/scripts/check-heading-hierarchy.ts new file mode 100644 index 0000000..17fe327 --- /dev/null +++ b/scripts/check-heading-hierarchy.ts @@ -0,0 +1 @@ +import { chromium } from "playwright"; diff --git a/scripts/test-css-contrast.js b/scripts/test-css-contrast.js new file mode 100644 index 0000000..a303dcf --- /dev/null +++ b/scripts/test-css-contrast.js @@ -0,0 +1,25 @@ +const { calculateContrastRatio, meetsWCAGStandard } = require('../src/lib/color-contrast.ts'); + +console.log('Testing CSS color contrast...'); + +const primaryResult = meetsWCAGStandard('#1C1C1C', '#FFFFFF', 'AA', 'normal'); +console.log('Primary text (#1C1C1C) on background (#FFFFFF):', primaryResult); + +const tertiaryResult = meetsWCAGStandard('#4A4A4A', '#FFFFFF', 'AA', 'normal'); +console.log('Tertiary text (#4A4A4A) on background (#FFFFFF):', tertiaryResult); + +const mutedResult = meetsWCAGStandard('#6B6B6B', '#FFFFFF', 'AA', 'normal'); +console.log('Muted text (#6B6B6B) on background (#FFFFFF):', mutedResult); + +console.log('\nExpected: All should pass (passes: true)'); +console.log('Actual results:'); +console.log('- Primary:', primaryResult.passes ? '✓ PASS' : '✗ FAIL', `(ratio: ${primaryResult.ratio.toFixed(2)}:1)`); +console.log('- Tertiary:', tertiaryResult.passes ? '✓ PASS' : '✗ FAIL', `(ratio: ${tertiaryResult.ratio.toFixed(2)}:1)`); +console.log('- Muted:', mutedResult.passes ? '✓ PASS' : '✗ FAIL', `(ratio: ${mutedResult.ratio.toFixed(2)}:1)`); + +if (!primaryResult.passes || !tertiaryResult.passes || !mutedResult.passes) { + console.log('\n⚠️ Some tests failed - need to optimize CSS variables'); + process.exit(1); +} + +console.log('\n✅ All tests passed!'); diff --git a/scripts/verify-color-contrast.js b/scripts/verify-color-contrast.js new file mode 100644 index 0000000..29d34bc --- /dev/null +++ b/scripts/verify-color-contrast.js @@ -0,0 +1,15 @@ +const { calculateContrastRatio, meetsWCAGStandard } = require('../src/lib/color-contrast.ts'); + +console.log('Testing color contrast functions...'); + +const ratio = calculateContrastRatio('#000000', '#FFFFFF'); +console.log('Black on white ratio:', ratio); +console.log('Expected: ~21, Actual:', ratio); + +const result = meetsWCAGStandard('#000000', '#FFFFFF', 'AA', 'normal'); +console.log('WCAG AA compliance:', result); + +const lowContrastResult = meetsWCAGStandard('#808080', '#FFFFFF', 'AA', 'normal'); +console.log('Low contrast test:', lowContrastResult); + +console.log('All tests completed!'); diff --git a/src/app/(marketing)/about/client.tsx b/src/app/(marketing)/about/client.tsx index 6e7b97e..b6dd273 100644 --- a/src/app/(marketing)/about/client.tsx +++ b/src/app/(marketing)/about/client.tsx @@ -223,7 +223,7 @@ export function AboutClient() { {milestone.date}
{milestone.description}