# SEO Theme Support Update **Date:** 2026-03-21 **Agent:** seo-specialist **Task:** Update SEO metadata for theme support --- ## Summary of Changes ### 1. `src/layouts/BaseLayout.astro` — `theme-color` meta tags **Before:** ```html ``` **After:** ```html ``` The dual `media` attribute approach lets browsers natively use the correct browser-chrome color based on OS theme — even before JavaScript runs. This is important for users who haven't yet interacted with the site. The inline theme-init script (already present for FOUC prevention) was extended to also sync these meta tags when a user has a **stored explicit preference** in localStorage that overrides their OS setting. --- ### 2. `src/components/ThemeToggle.astro` — Meta tag sync on toggle **Before:** `applyTheme()` used `querySelector` (selects only the first meta), and the system-preference change listener did not update theme-color at all. **After:** - `applyTheme()` uses `querySelectorAll('meta[name="theme-color"]').forEach(...)` to update **all** theme-color metas simultaneously - System preference change listener (`matchMedia` change event) now also syncs all theme-color metas when no stored preference exists --- ## What Was NOT Changed (and Why) ### Structured Data (JSON-LD) All schema.org structured data in `BaseLayout.astro` and `SEO.astro` is **theme-agnostic by design**. JSON-LD describes content/entities, not visual presentation. Search engines and AI crawlers have no concept of visual theme when consuming structured data. No changes needed. ### Open Graph / Twitter Card Images Social preview images (`og-image.jpg`) are static assets served to social crawlers and link-unfurling bots. These crawlers: - Do not respect OS `prefers-color-scheme` - Do not execute JavaScript - Do not honor `media` query attributes Providing separate dark/light OG images would require serving different `` values based on the server-side request context, which adds complexity without meaningful SEO benefit. The existing `og-image.jpg` works universally. ### Other Meta Tags All other meta tags (title, description, canonical, robots, OG/Twitter properties) are theme-agnostic and correct as-is. --- ## Color Values Used | Theme | `theme-color` value | Token | |-------|-------------------|-------| | Light | `#0891b2` | `--palette-primary-500` (brand cyan) | | Dark | `#0f172a` | `--color-surface` dark (deep navy) | These values match the design tokens in `src/styles/design-tokens.css`. --- ## How It Works End-to-End 1. **Page load (no JS yet):** Browser reads both `theme-color` meta tags and picks the one matching OS preference via the `media` attribute — instant, no flash. 2. **Inline init script fires:** If user has a stored preference that differs from OS, the script updates both meta tag `content` values to the stored preference color. 3. **User clicks ThemeToggle:** `applyTheme()` toggles the `.dark` class AND updates all `theme-color` metas in one synchronous call. 4. **User changes OS theme (no stored preference):** `matchMedia` change listener updates `.dark` class AND syncs all theme-color metas. --- ## Testing Verify correct behavior by: 1. Opening DevTools → Elements → search for `theme-color` meta tags 2. Toggle between light/dark using the site toggle — both meta `content` values should update to the same color 3. Remove localStorage `theme` key, change OS theme — meta tags should track OS preference 4. Check browser tab/address bar color updates on Chrome/Edge (Android) after toggle