3.4 KiB
3.4 KiB
Context
The site already supports theme selection via a floating notch and persists the user's choice using localStorage (key: site.theme). Theme selection affects the root document via html[data-theme] and is initialized before first paint via an inline head script.
Two gaps remain:
- Returning users should reliably see their last-selected theme even in environments where
localStorageis unavailable. - We need analytics to measure theme switcher usage and preferred themes.
The site uses Umami for analytics. Most interactions are tracked via data-umami-event* attributes, and runtime-only events use window.umami.track(...).
Goals / Non-Goals
Goals:
- Persist theme selection across visits with a robust fallback:
localStorageprimary, client-side cookie fallback. - Apply the stored theme before first paint when possible.
- Emit a deterministic Umami event when a user changes theme via the theme notch.
Non-Goals:
- Server-side rendering of theme choice (the site is statically built; no request-time HTML variation).
- Tracking theme selection on initial page load (only user-initiated changes).
- Adding new UI beyond the existing theme notch.
Decisions
- Persistence mechanism and precedence
- Decision: Read theme preference in this order:
localStorage(site.theme)- Cookie (
site_theme) - Environment signals (forced-colors, prefers-color-scheme)
- Rationale:
localStorageis already in use and provides a stable primary store. Cookies provide a resilient fallback when storage access is blocked or throws. - Alternatives considered:
- Cookie-only: simpler but unnecessary regression from existing behavior.
- URL param: not persistent and adds user-visible noise.
- Cookie format and attributes
- Decision: Store
site_theme=<theme>withMax-Age=31536000,Path=/,SameSite=Lax. SetSecurewhen running under HTTPS. - Rationale: First-party cookie with long TTL provides continuity across visits. The cookie is readable from the inline head script for pre-paint initialization.
- Analytics event shape
- Decision: Emit a custom Umami event via
window.umami.track("theme_switch", data)only on user-initiated changes. - Event properties:
target_id:theme.switch.<theme>placement:theme_notchtheme: new theme value (dark|light|high-contrast)prev_theme: previous theme value (same enum) when known
- Rationale: A dedicated event name makes reporting straightforward (no need to filter general
click). Usingtarget_id/placementkeeps it compatible with the site's interaction taxonomy. - Alternatives considered:
- Reuse
clickevent: consistent, but mixes preference changes into general click reporting.
- Reuse
- Avoid tracking initial theme restoration
- Decision: Do not emit
theme_switchfrom the head theme-init script. - Rationale: We want to measure explicit user interaction with the notch, not implicit restoration.
Risks / Trade-offs
- Cookie and storage may both be blocked in restrictive environments → fallback to environment signals; no persistence.
- Umami may be disabled/unconfigured or not loaded at event time → guard with
typeof window.umami !== "undefined"and keep behavior non-fatal. - Using cookies introduces another persistence layer → must keep
localStorageand cookie consistent on successful theme changes.