Theme
ThemeScope + IdPrefix providers — primitives.
When to use
- A card / dialog / preview that should render in a non-default theme regardless of the global setting (Mission Control's brand-preview tile rendering every brand × tone in one screen).
- The Storybook themes addon's per-story preview frame.
- A dark-on-light callout block inside a g100 page (or vice versa).
### When not to use
- Application-level theme control (system color-scheme detection, localStorage persistence, tone toggle) → use the
ThemeProviderfrom@avala/design's hooks barrel instead. - A single component that wants different styles per state — that's a CVA variant axis, not a theme override.
### API
brand—avala | investor | learning | pay | tickets | chat | operations | neutral. Defaultavala.tone—g10 | g100. Defaultg100.as—div(default, wraps children withdata-themeanddata-brandattributes) ornone(provides context only, no DOM wrapper).- All native
<div>props pass through.
### Examples
`tsx import { ThemeScope, useScopedTheme } from "@avala/design";
// Render a brand-preview tile in g10 inside an otherwise-g100 page. <ThemeScope brand="avala" tone="g10" className="rounded-sm border p-4"> <Card> <h3>Light tone preview</h3> </Card> </ThemeScope>
// Read the current scope inside a component. function BrandLogo() { const { brand } = useScopedTheme(); return <img src={/logos/${brand}.svg} alt="" />; } `
### Important: tokens come from imported CSS
ThemeScope *announces* the active theme to descendants but does NOT swap the underlying token values. Today the brand-tone CSS files emit declarations against :root. To switch tokens at runtime, consumers must either:
1. Import all candidate brand-tone CSS files, then apply a [data-theme=…] / [data-brand=…] selector ramp on top (planned Phase 4 scoped-CSS emit). 2. Dynamically swap the <link rel="stylesheet"> for the target tone — useScopedTheme() returns the active identifiers so consumer code can drive the swap.
When not to use
- Application-level theme control (system color-scheme detection, localStorage persistence, tone toggle) → use the
ThemeProviderfrom@avala/design's hooks barrel instead. - A single component that wants different styles per state — that's a CVA variant axis, not a theme override.
### API
brand—avala | investor | learning | pay | tickets | chat | operations | neutral. Defaultavala.tone—g10 | g100. Defaultg100.as—div(default, wraps children withdata-themeanddata-brandattributes) ornone(provides context only, no DOM wrapper).- All native
<div>props pass through.
### Examples
`tsx import { ThemeScope, useScopedTheme } from "@avala/design";
// Render a brand-preview tile in g10 inside an otherwise-g100 page. <ThemeScope brand="avala" tone="g10" className="rounded-sm border p-4"> <Card> <h3>Light tone preview</h3> </Card> </ThemeScope>
// Read the current scope inside a component. function BrandLogo() { const { brand } = useScopedTheme(); return <img src={/logos/${brand}.svg} alt="" />; } `
### Important: tokens come from imported CSS
ThemeScope *announces* the active theme to descendants but does NOT swap the underlying token values. Today the brand-tone CSS files emit declarations against :root. To switch tokens at runtime, consumers must either:
1. Import all candidate brand-tone CSS files, then apply a [data-theme=…] / [data-brand=…] selector ramp on top (planned Phase 4 scoped-CSS emit). 2. Dynamically swap the <link rel="stylesheet"> for the target tone — useScopedTheme() returns the active identifiers so consumer code can drive the swap.