feat(brand): themed brands + prefers-color-scheme auto-mode (#125)#132
Merged
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ThemedBrandshape ({ 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).useColorScheme()hook subscribes tomatchMedia('(prefers-color-scheme: dark)').useResolvedBrand(brand)combines them: auto-mode follows the OS;mode: 'light'|'dark'pins.BrandProvidernow usesuseResolvedBrandinternally, so wrapping a themed brand in<BrandProvider brand={...}>gives auto-theming app-wide.Brandpasses through unchanged — fully additive, no breaking change.Why narrower than the issue
Charts read
resolveBrand(brand).palettedirectly 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 swapresolveBrandforuseResolvedBrandgoing forward.Verification
npm run typecheckcleannpm test— 421 tests pass (10 new: SSR fallback, matchMedia dark/light, mode-pinned branches, plain-brand passthrough, ThemedBrand light/dark resolution)npm run build && npm run check:bundle— browser entry 50 KB gzipped (budget 75 KB, unchanged from feat: client-side export — toSvgString / toPng / downloadChart / copyToClipboard (#123) #131)Test plan
Closes #125.