/** * Verification test for Prose Styling (Task 8) * * Checks that: * 1. src/styles/prose.css exists * 2. Contains .prose wrapper class * 3. Headings: h2, h3, h4 with correct tokens * 4. Paragraphs with bottom margin * 5. Links with accent colors * 6. Blockquotes with left border * 7. Lists with padding and spacing * 8. Inline code with evidence font * 9. Code blocks with breakout width (position/transform technique) * 10. Horizontal rules * 11. Strong and em * 12. Images * 13. Tables * 14. All values reference CSS custom properties from tokens.css */ import { readFileSync, existsSync } from 'fs'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; const __dirname = dirname(fileURLToPath(import.meta.url)); const root = join(__dirname, '..'); let passed = 0; let failed = 0; function assert(condition, message) { if (condition) { console.log(` ✓ ${message}`); passed++; } else { console.error(` ✗ ${message}`); failed++; } } const prosePath = join(root, 'src/styles/prose.css'); // === 1. File exists === console.log('\n1. prose.css exists'); assert(existsSync(prosePath), 'src/styles/prose.css exists'); if (!existsSync(prosePath)) { console.log('\nFile not found, skipping remaining tests.'); console.log(`\n${'='.repeat(40)}`); console.log(`Results: ${passed} passed, ${failed} failed out of ${passed + failed}`); process.exit(1); } const css = readFileSync(prosePath, 'utf8'); // === 2. .prose wrapper === console.log('\n2. .prose wrapper'); assert(css.includes('.prose'), 'contains .prose class'); // === 3. Headings === console.log('\n3. Headings (Structure voice: Inter)'); // h2 assert(css.includes('.prose h2'), '.prose h2 selector exists'); assert(css.includes('--font-structure'), 'headings use --font-structure'); assert(css.includes('--type-2xl'), 'h2 uses --type-2xl'); assert(/\.prose h2[\s\S]*?font-weight:\s*600/.test(css), 'h2 weight 600'); assert(css.includes('--leading-snug'), 'h2 uses --leading-snug'); assert(css.includes('-0.015em'), 'h2 tracking -0.015em'); assert(css.includes('--space-8'), 'h2 uses --space-8 top margin'); // h3 assert(css.includes('.prose h3'), '.prose h3 selector exists'); assert(css.includes('--type-xl'), 'h3 uses --type-xl'); assert(css.includes('-0.01em'), 'h3 tracking -0.01em'); assert(css.includes('--space-5'), 'h3 uses --space-5 top margin'); assert(css.includes('--space-1-5'), 'h3 uses --space-1-5 bottom margin'); // h4 assert(css.includes('.prose h4'), '.prose h4 selector exists'); assert(css.includes('--type-lg'), 'h4 uses --type-lg'); assert(css.includes('--leading-heading'), 'h4 uses --leading-heading'); assert(css.includes('-0.005em'), 'h4 tracking -0.005em'); // === 4. Paragraphs === console.log('\n4. Paragraphs (Prose voice)'); assert(css.includes('.prose p'), '.prose p selector exists'); assert(css.includes('--space-3'), 'p uses --space-3 bottom margin'); assert(css.includes(':last-child'), 'p:last-child margin 0 rule'); // === 5. Links === console.log('\n5. Links'); assert(css.includes('.prose a'), '.prose a selector exists'); assert(css.includes('--accent-primary'), 'links use --accent-primary'); assert(css.includes('text-decoration: none') || css.includes('text-decoration:none'), 'links have no underline by default'); assert(css.includes('--accent-primary-hover'), 'link hover uses --accent-primary-hover'); // hover should add underline assert(/\.prose a:hover[\s\S]*?underline/.test(css), 'link hover adds underline'); // === 6. Blockquotes === console.log('\n6. Blockquotes'); assert(css.includes('.prose blockquote'), '.prose blockquote selector exists'); assert(css.includes('--font-prose'), 'blockquotes use --font-prose'); assert(css.includes('1.25rem'), 'blockquote 1.25rem size'); assert(css.includes('italic') || css.includes('font-style'), 'blockquote italic'); assert(css.includes('--leading-relaxed'), 'blockquote --leading-relaxed'); assert(css.includes('3px'), 'blockquote 3px left border'); // === 7. Lists === console.log('\n7. Lists'); assert(css.includes('.prose ul') || css.includes('.prose ol'), '.prose ul/ol selector exists'); assert(css.includes('.prose li'), '.prose li selector exists'); // === 8. Inline code === console.log('\n8. Inline code (Evidence voice)'); assert(css.includes('.prose code'), '.prose code selector exists'); assert(css.includes('--font-evidence'), 'inline code uses --font-evidence'); assert(css.includes('0.9em'), 'inline code 0.9em size'); assert(css.includes('--code-text'), 'inline code uses --code-text color'); assert(css.includes('--surface-code'), 'inline code uses --surface-code background'); assert(css.includes('0.15em'), 'inline code 0.15em vertical padding'); assert(css.includes('0.35em'), 'inline code 0.35em horizontal padding'); assert(css.includes('--rounded-sm'), 'inline code uses --rounded-sm'); // === 9. Pre code reset === console.log('\n9. Pre code reset'); assert(css.includes('.prose pre code'), '.prose pre code selector exists'); assert(/\.prose pre code[\s\S]*?1rem/.test(css) || /\.prose pre code[\s\S]*?font-size/.test(css), 'pre code resets font size'); // === 10. Code blocks with breakout width === console.log('\n10. Code blocks (breakout width)'); assert(css.includes('.prose pre'), '.prose pre selector exists'); assert(css.includes('position: relative') || css.includes('position:relative'), 'code block position relative'); assert(css.includes('left: 50%') || css.includes('left:50%'), 'code block left 50%'); assert(css.includes('translateX(-50%)'), 'code block translateX(-50%)'); assert(css.includes('--width-code'), 'code block references --width-code'); assert(css.includes('--gutter'), 'code block references --gutter'); assert(css.includes('--rounded-md'), 'code block uses --rounded-md'); assert(css.includes('overflow-x: auto') || css.includes('overflow-x:auto'), 'code block overflow-x auto'); assert(css.includes('--font-evidence'), 'code block uses --font-evidence'); assert(css.includes('tab-size: 2') || css.includes('tab-size:2'), 'code block tab-size 2'); assert(css.includes('focus-visible'), 'code block has focus-visible outline'); // === 11. Horizontal rules === console.log('\n11. Horizontal rules'); assert(css.includes('.prose hr'), '.prose hr selector exists'); assert(css.includes('border: none') || css.includes('border:none') || css.includes('border: 0') || css.includes('border:0'), 'hr no border'); assert(css.includes('--border-subtle') || css.includes('border-top'), 'hr has subtle top border'); assert(css.includes('--space-6'), 'hr uses --space-6 margins'); assert(css.includes('5rem'), 'hr max-width 5rem'); // === 12. Strong and em === console.log('\n12. Strong and em'); assert(css.includes('.prose strong'), '.prose strong selector exists'); assert(/\.prose strong[\s\S]*?font-weight:\s*600/.test(css), 'strong weight 600'); assert(css.includes('.prose em'), '.prose em selector exists'); // === 13. Images === console.log('\n13. Images'); assert(css.includes('.prose img'), '.prose img selector exists'); assert(css.includes('max-width: 100%') || css.includes('max-width:100%'), 'images max-width 100%'); assert(css.includes('height: auto') || css.includes('height:auto'), 'images auto height'); // === 14. Tables === console.log('\n14. Tables'); assert(css.includes('.prose table'), '.prose table selector exists'); assert(css.includes('border-collapse') || css.includes('border-collapse: collapse'), 'table collapse'); assert(css.includes('--type-sm'), 'table uses --type-sm'); assert(css.includes('.prose th'), '.prose th selector exists'); assert(/\.prose th[\s\S]*?font-weight:\s*600/.test(css) || /\.prose th[\s\S]*?--font-structure/.test(css), 'th uses structure font or weight 600'); assert(css.includes('.prose td'), '.prose td selector exists'); // === 15. Uses CSS custom properties from tokens.css (no raw hex) === console.log('\n15. Token references'); // Check that key styling values use var() references const varCount = (css.match(/var\(--/g) || []).length; assert(varCount >= 30, `uses var(--) references extensively (found ${varCount}, expected >=30)`); // Verify no raw color hex values used (except within specific contexts like border shorthand) // Allow #-style in comments but main properties should use tokens const hexInProperties = css.match(/:\s*#[0-9a-fA-F]{3,8}\b/g) || []; assert(hexInProperties.length === 0, `no raw hex color values in properties (found ${hexInProperties.length})`); // === Summary === console.log(`\n${'='.repeat(40)}`); console.log(`Results: ${passed} passed, ${failed} failed out of ${passed + failed}`); if (failed > 0) { process.exit(1); } else { console.log('All prose verification tests passed!'); }