Skip to content

feat(brand): themed brands + prefers-color-scheme auto-mode (#125)#132

Merged
Ben Severn (benzsevern) merged 1 commit into
mainfrom
feat/125-brand-auto-dark-mode
May 28, 2026
Merged

feat(brand): themed brands + prefers-color-scheme auto-mode (#125)#132
Ben Severn (benzsevern) merged 1 commit into
mainfrom
feat/125-brand-auto-dark-mode

Conversation

@benzsevern
Copy link
Copy Markdown
Collaborator

Summary

  • New ThemedBrand shape ({ mode?, light, dark }) — pass one brand prop, get the right side for the OS.
  • resolveBrand(brand, scheme?) takes an optional 'light'|'dark' scheme (defaults to 'light' — SSR-safe).
  • New useColorScheme() hook subscribes to matchMedia('(prefers-color-scheme: dark)').
  • New useResolvedBrand(brand) combines them: auto-mode follows the OS; mode: 'light'|'dark' pins.
  • BrandProvider now uses useResolvedBrand internally, so wrapping a themed brand in <BrandProvider brand={...}> gives auto-theming app-wide.
  • Plain Brand passes through unchanged — fully additive, no breaking change.

Why narrower than the issue

Charts read resolveBrand(brand).palette directly in their own body (CLAUDE.md: they render above Surface's providers), so wrapping every chart in a hook would be invasive. This PR ships the primitives + provider integration; charts that want auto-theming inside their body can swap resolveBrand for useResolvedBrand going forward.

Verification

Test plan

  • Typecheck clean
  • Full test suite green
  • Bundle guard green
  • CI green

Closes #125.

Adds a ThemedBrand shape ({ mode?, light, dark }) so consumers can pass
a single brand prop and have the chart pick the right side based on the
OS colour scheme. Plain Brand passes through unchanged.

- new BrandMode + ThemedBrand types in src/types/brand.ts plus
  `isThemedBrand` guard
- `resolveBrand(brand, scheme?)` accepts an optional 'light'|'dark'
  scheme; defaults to 'light' (SSR-safe)
- new useColorScheme() hook in src/brand/useColorScheme.ts subscribes
  to matchMedia('(prefers-color-scheme: dark)')
- new useResolvedBrand(brand) hook combines the two — auto-mode
  follows the OS, mode='light'/'dark' pins the side
- BrandProvider now uses useResolvedBrand internally, so wrapping a
  themed brand in <BrandProvider brand={...}> gives auto-theming app-wide
- tests cover SSR fallback, matchMedia dark/light reads, mode-pinned
  branches, plain-brand passthrough

Bundle 50 KB gzipped (budget 75 KB).

Charts that read `resolveBrand(brand)` directly (per CLAUDE.md, because
their bodies render above Surface's providers) default to the light side
for themed brands. To get auto-theming inside a chart body, swap that
call for `useResolvedBrand(brand)` — additive, no breaking change.
@benzsevern Ben Severn (benzsevern) merged commit 5f37cb5 into main May 28, 2026
5 checks passed
@benzsevern Ben Severn (benzsevern) deleted the feat/125-brand-auto-dark-mode branch May 28, 2026 17:02
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.

Auto dark mode: brand='auto' honoring prefers-color-scheme

1 participant