Skip to Content
Puzzle

Theme Provider

The Theme component injects design token CSS variables, loads fonts, and applies Puzzle's global styles.

The Theme component is the entry point of the theme system. It resolves tokens into CSS custom properties, renders font stylesheet links, and provides theme context to Puzzle components.

Import

import { Theme } from "@puzzle-ui/theme";

Props

PropTypeDefaultDescription
theme"light" | "dark" | "system""system"The color scheme mode. system follows prefers-color-scheme.
scope"root" | "local""root"Whether variables apply to :root or only to the component's subtree.
globalStylesbooleantrueApplies Puzzle's global base styles. Only used with root scope.
tokensThemeTokenOverrides—Deep-partial overrides of the default design tokens.
colorThemeColorThemes—One of the built-in color themes.
fontsPartial<Record<FontRole, FontConfig>>—Google Fonts for the heading, body, and accent roles. See Typography & Fonts.
componentsThemeComponents—Global link and image component overrides.
noncestring—CSP nonce applied to the injected <style> elements.

Root scope

With the default scope="root", the resolved CSS variables are assigned to :root and the theme applies to the whole document. This is the standard setup for an application:

<Theme theme="system" colorTheme="emerald-forest">
  <App />
</Theme>

Root scope also applies Puzzle's global styles unless you opt out:

<Theme globalStyles={false}>
  <App />
</Theme>

Local scope

With scope="local", the variables are assigned to a generated scope attribute instead of :root, and the children are wrapped in a scope element that resolves the color scheme locally. Use this to theme a single section differently from the rest of the page—for example, a permanently dark hero or an embedded preview:

<Theme scope="local" theme="dark">
  <HeroSection />
</Theme>

Local scopes do not create a layout box (display: contents), so wrapping content in a scoped theme does not affect layout. Local scopes never apply global styles and can be nested inside a root theme; descendants inherit the scoped variables while the rest of the document keeps the root theme.

Theme modes

  • light — forces the light color scheme.
  • dark — forces the dark color scheme.
  • system — follows the user's prefers-color-scheme setting and updates automatically.

The active mode controls which color scheme tokens resolve into the semantic --puzzle-color-* variables, and sets the CSS color-scheme so native controls, scrollbars, and form elements match.

Fonts

When fonts are provided, Theme renders preconnect links for Google Fonts plus one stylesheet link per configured font, and wires the resolved font families into the typography CSS variables:

<Theme
  fonts={{
    heading: { family: "Sora", weights: [600, 700] },
    body: { family: "Inter" },
  }}
>
  <App />
</Theme>

Content Security Policy

If your application uses a CSP that restricts inline styles, pass a nonce. It is applied to the injected <style> elements:

<Theme nonce={cspNonce}>
  <App />
</Theme>
;