Files
Santhosh Janardhanan f50a828535
Some checks failed
ci / site (push) Has been cancelled
publish-image / publish (push) Has been cancelled
Now I remember the theme
2026-02-10 20:38:38 -05:00

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 localStorage is 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: localStorage primary, 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

  1. Persistence mechanism and precedence
  • Decision: Read theme preference in this order:
    1. localStorage (site.theme)
    2. Cookie (site_theme)
    3. Environment signals (forced-colors, prefers-color-scheme)
  • Rationale: localStorage is 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.
  1. Cookie format and attributes
  • Decision: Store site_theme=<theme> with Max-Age=31536000, Path=/, SameSite=Lax. Set Secure when 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.
  1. 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_notch
    • theme: 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). Using target_id/placement keeps it compatible with the site's interaction taxonomy.
  • Alternatives considered:
    • Reuse click event: consistent, but mixes preference changes into general click reporting.
  1. Avoid tracking initial theme restoration
  • Decision: Do not emit theme_switch from 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 localStorage and cookie consistent on successful theme changes.