Skip to content

feat: theme-aware favicons (light + dark variants for every format) - #8

Merged
ronaldtse merged 1 commit into
mainfrom
chore/realfavicon-set
Aug 7, 2026
Merged

feat: theme-aware favicons (light + dark variants for every format)#8
ronaldtse merged 1 commit into
mainfrom
chore/realfavicon-set

Conversation

@ronaldtse

Copy link
Copy Markdown
Contributor

Summary

Ships paired light + dark favicons for every format and wires them up to follow both prefers-color-scheme AND the manual [data-theme] toggle.

Files (all in public/, paired)

  • favicon-{light,dark}.svg — scalable, modern browsers
  • favicon-{light,dark}-96x96.png — PNG fallback
  • favicon-{light,dark}.ico — multi-res ICO, legacy contexts
  • apple-touch-icon-{light,dark}.png — iOS home screen (180px)
  • web-app-manifest-{light,dark}-{192x192,512x512}.png — PWA manifest icons

Dropped the old single-variant favicon.svg, favicon-96x96.png, favicon.ico, apple-touch-icon.png.

Mechanism (no <link media> — JS-driven so the manual toggle works)

  • BaseLayout emits one <link> per slot with data-favicon="svg|png|ico|apple", defaulting to the light variant.
  • An inline pre-paint script resolves the theme (stored localStorage OR matchMedia) and rewrites each link's href to the matching variant.
  • A matchMedia listener re-applies on system theme change.
  • ThemeToggle calls window.__applyClaricleFavicons(next) on click so manual toggles update favicons without a page reload.

Reference: favicon.im blog — for sites with custom theme controls, the HTML-only <link media> approach is insufficient because it can't see the [data-theme] attribute. JS is required to override.

Manifest

site.webmanifest corrected: MyWebSiteClaricle. Light icons registered as purpose: "any", dark as purpose: "maskable" (PWA spec doesn't support theme-aware icons — both are listed, platform picks one).

Branding repo

Originals for these will be synced to claricle/branding in a follow-up PR (per the canonical asset hierarchy).

Test plan

  • CI on this PR runs green (99 specs)
  • System light → light favicons everywhere
  • System dark → dark favicons everywhere
  • Manual "light" click on dark-pref system → favicons swap to light
  • Manual "dark" click on light-pref system → favicons swap to dark
  • After merge, deploy serves /favicon-light.svg, /favicon-dark.svg, etc.

The previous favicons shipped a single light variant for every format.
On dark browser chrome, dark OS chrome, or dark iOS home screens, they
looked dim and off-brand.

This PR ships paired light/dark variants for every favicon format and
wires them up to follow both prefers-color-scheme AND the manual
[data-theme] toggle:

Files (all in public/, paired light + dark):
- favicon-{light,dark}.svg           — scalable, for modern browsers
- favicon-{light,dark}-96x96.png     — PNG fallback
- favicon-{light,dark}.ico           — multi-res ICO for legacy
- apple-touch-icon-{light,dark}.png  — iOS home screen (180px)
- web-app-manifest-{light,dark}-{192x192,512x512}.png — PWA

Dropped: favicon.svg, favicon-96x96.png, favicon.ico, apple-touch-icon.png
(the single-variant versions).

Mechanism:
- BaseLayout emits one link per slot with data-favicon="svg|png|ico|apple",
  defaulting to the light variant.
- An inline pre-paint script resolves the theme (stored localStorage OR
  matchMedia) and rewrites each link's href to the matching variant.
- matchMedia listener re-applies on system theme change.
- ThemeToggle calls window.__applyClaricleFavicons(next) on click so
  manual toggles update the favicon without a page reload.

Manifest also fixed up: name/short_name corrected from the RealFaviconGenerator
placeholder, light icons registered as purpose:"any" + dark as purpose:"maskable"
(PWA spec doesn't support theme-aware icons, so both are listed).

Per the canonical asset hierarchy, originals for these live in
claricle/branding and will be synced in a follow-up.

Tested:
- 99 vitest specs pass (added: paired-variant existence + BaseLayout
  applies favicons based on resolved theme).
- Manual: system light -> light favicons everywhere, system dark -> dark,
  manual light on dark-pref system -> light everywhere, manual dark on
  light-pref system -> dark everywhere.
@ronaldtse
ronaldtse merged commit 94b4a5e into main Aug 7, 2026
1 check passed
@ronaldtse
ronaldtse deleted the chore/realfavicon-set branch August 7, 2026 02:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant