3.7 KiB
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:
<meta name="theme-color" content="#0891b2" />
After:
<!-- OS-level dual media query approach -->
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#0891b2" />
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#0f172a" />
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()usesquerySelectorAll('meta[name="theme-color"]').forEach(...)to update all theme-color metas simultaneously- System preference change listener (
matchMediachange 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
mediaquery attributes
Providing separate dark/light OG images would require serving different <meta property="og:image"> 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
- Page load (no JS yet): Browser reads both
theme-colormeta tags and picks the one matching OS preference via themediaattribute — instant, no flash. - Inline init script fires: If user has a stored preference that differs from OS, the script updates both meta tag
contentvalues to the stored preference color. - User clicks ThemeToggle:
applyTheme()toggles the.darkclass AND updates alltheme-colormetas in one synchronous call. - User changes OS theme (no stored preference):
matchMediachange listener updates.darkclass AND syncs all theme-color metas.
Testing
Verify correct behavior by:
- Opening DevTools → Elements → search for
theme-colormeta tags - Toggle between light/dark using the site toggle — both meta
contentvalues should update to the same color - Remove localStorage
themekey, change OS theme — meta tags should track OS preference - Check browser tab/address bar color updates on Chrome/Edge (Android) after toggle