diff --git a/lib/exporter/a11yResponsive.test.ts b/lib/exporter/a11yResponsive.test.ts
index 081b859..270a2e7 100644
--- a/lib/exporter/a11yResponsive.test.ts
+++ b/lib/exporter/a11yResponsive.test.ts
@@ -41,7 +41,7 @@ describe('Exporter a11y/responsive enhancements', () => {
test('hero image alt derives from heading', () => {
const out = exportPage(makePage())
expect(out.html).toContain('
Welcome
')
- expect(out.html).toContain('
')
+ expect(out.html).toMatch(/
]*src="https:\/\/example\.com\/img\.png"[^>]*alt="Welcome"[^>]*\/>/)
})
test('responsive and a11y CSS tokens exist', () => {
diff --git a/lib/exporter/html.ts b/lib/exporter/html.ts
index 45ce4a0..3eff894 100644
--- a/lib/exporter/html.ts
+++ b/lib/exporter/html.ts
@@ -108,7 +108,7 @@ function renderHero(section: { props: { heading: string; subheading?: string; im
${subheading ? `${escapeHtml(subheading)}
` : ''}
@@ -237,6 +237,7 @@ function buildHead(page: Page) {
return `
+
${escapeHtml(page.seo.title)}
@@ -272,6 +273,7 @@ function buildCss(page: Page) {
.testimonial blockquote{ margin:0; font-style:italic; color:#1f2937 }
.testimonial figcaption{ color:#475569; margin-top:.25rem }
@media (max-width: 640px){ .container{ padding: 1rem } h1{ font-size: 1.5rem } }
+ @media (prefers-color-scheme: dark){ body{ color:#e5e7eb; background:#0b1020 } .feature-item{ background:#0f172a; border-color:#1f2937 } }
@media (prefers-reduced-motion: reduce){ *{ transition: none !important; animation: none !important } }
`
}
diff --git a/lib/exporter/seoPerf.tokens.test.ts b/lib/exporter/seoPerf.tokens.test.ts
new file mode 100644
index 0000000..de4d41a
--- /dev/null
+++ b/lib/exporter/seoPerf.tokens.test.ts
@@ -0,0 +1,34 @@
+import { describe, it, expect } from 'vitest'
+import { exportPage } from '@/lib/exporter/html'
+import { pageSchema, type Page } from '@/lib/schema/page'
+
+function makePage(): Page {
+ return pageSchema.parse({
+ title: 'SEO/Perf',
+ locale: 'en',
+ theme: { primaryColor: '#0ea5e9', fontFamily: 'Inter' },
+ sections: [
+ { type: 'hero', props: { heading: 'Welcome', subheading: 'Sub', imageUrl: 'https://img.example/hero.png' } },
+ ],
+ form: { actionUrl: '', fields: [], spamProtection: { honeypotFieldName: '_hp', minSubmitSeconds: 2 } },
+ seo: { title: 'SEO', description: 'Desc' },
+ })
+}
+
+describe('Exporter - small SEO/Perf improvements', () => {
+ it('includes canonical link and dark mode tokens; hero image lazy/async', () => {
+ const out = exportPage(makePage())
+ const html = out.html
+ const css = out.css
+
+ // canonical link present in head
+ expect(html).toMatch(/]+rel="canonical"/)
+
+ // hero image has loading/decoding attributes
+ expect(html).toMatch(/
]*loading="lazy"/)
+ expect(html).toMatch(/
]*decoding="async"/)
+
+ // dark mode media query token exists
+ expect(css).toMatch(/@media \(prefers-color-scheme: dark\)/)
+ })
+})