ThemeProvider

ThemeProvider owns the document's color preference. Put one at the application root, then decide whether people choose light, dark or system; a nested provider still writes to the same document.

Examples

A saved preference

A control should set an explicit choice, not invert an unknown state. This preview uses its own storageKey, so trying it cannot replace the documentation site's saved preference.

Following the operating system

system is the default. It resolves to a concrete light or dark theme and follows an operating system change, while preserving system as the reader's stored choice.

When to use

Use ThemeProvider once at an application root when color mode is a durable preference. Set the same storageKey in the blocking first-paint script and in the provider, or the page can flash one theme before React applies another.

Reach for something else when:

  • The product has no user-selectable color mode — set data-theme in the document shell and do not add a preference control.
  • The visual identity, rather than the color mode, changes per tenant — use the White-label guide and pass brand from the tenant configuration.

Accessibility

  • ThemeProvider emits no landmark, role or ARIA attribute. It sets data-theme on <html> and, when provided, data-brand.
  • A color-mode control is consumer UI: use a native Button or another named control, and keep it in normal tab order.
  • Changing a theme does not move focus. Do not replace the focused control while applying the new preference.
  • Never make color the only signal for a selected mode; expose the current choice in the control's text or state.

API and code

Blade: Blade has no provider shape. The theme lives in the @lyraThemeScript directive plus the Alpine theme store.

NameTypeRequiredDescription
defaultThemeTheme—Theme to use when nothing is stored yet. Default: `"system"`.
storageKeystring—localStorage key holding the choice. Default: `"lyra-theme"`.
brandstring—Sets `data-brand` on `<html>` for white-label theming. See the white-label guide.
childrenReactNodeRequired

$store.theme

MemberTypeRequiredDescription
themeThemeRequiredThe chosen theme, which may be `"system"`.
resolvedThemeResolvedThemeRequiredThe actual theme applied to `<html data-theme>`.
darkbooleanRequiredConvenience for `resolvedTheme === "dark"`.
init(): voidRequiredInitializes browser state after Alpine registers this singleton store.
setTheme(next: Theme): voidRequiredChooses a theme, persists it, and applies it to the document.
toggle(): voidRequiredFlips from the theme currently applied to the document.

ThemeProvider is React behavior, but the resulting document attribute is plain HTML. The same stylesheet classes then resolve against the selected token set:

html
<html data-theme="dark">
  <body>
    <button class="lyra-btn lyra-btn--primary lyra-btn--md" type="button">Save changes</button>
  </body>
</html>