ThemeProvider
A container that applies CSS custom properties from a theme object to its children, enabling scoped visual theming without affecting layout.
This headless component uses a <div> element with display: contents that flattens a nested theme object into --theme-* CSS custom properties on its style attribute.
Implementation Notes
- Uses
<div>element withdisplay: contentsto avoid affecting layout - Flattens nested theme object keys into
--theme-*CSS custom properties - Merges custom theme with a base light or dark theme
- Nested ThemeProvider components can override parent theme values
- CSS custom property output:
{ color: { text: "#1a1a1a" } }becomes--theme-color-text: #1a1a1a
Props
theme: object (required) -- theme object with key-value pairs flattened to CSS custom propertiesbase: "light" | "dark" (default: "light") -- base theme to merge withchildren: slot (required) -- themed content...restProps: Any additional HTML attributes
Usage
<ThemeProvider theme={{ color: { text: "#1a1a1a", background: "#ffffff", accent: "#2563eb" } }} base="light">
<Card>
<p>This content uses the custom theme colors.</p>
</Card>
</ThemeProvider>
<ThemeProvider theme={{ color: { text: "#f0f0f0", background: "#1a1a1a" } }} base="dark">
<Panel>
<p>Dark-themed content area.</p>
</Panel>
</ThemeProvider>
<ThemeProvider theme={{ spacing: { sm: "0.5rem", md: "1rem", lg: "2rem" } }}>
<ThemeProvider theme={{ color: { accent: "#dc2626" } }}>
<p>Nested theme overrides the accent color while inheriting spacing.</p>
</ThemeProvider>
</ThemeProvider>
Keyboard Interactions
- None -- presentational wrapper; interactive children handle their own keyboard interactions
ARIA
- None -- presentational wrapper. Ensure sufficient color contrast in custom themes to maintain WCAG 2.2 AAA compliance.
When to Use
- Use to scope visual themes to sections of a page
- Use to provide light and dark mode variants within the same page
- Use to allow nested theme overrides for specific content areas
- Use when different sections of a page require different visual styling
When Not to Use
- Do not use for global app theming -- set CSS custom properties on
:rootinstead - Do not use when only a single style property needs to change -- use inline styles directly
- Do not use for layout concerns -- ThemeProvider is purely for visual theming
Headless
This component provides a <div> element with display: contents that applies CSS custom properties from a theme object, with zero visual styling. The consumer is responsible for referencing the --theme-* custom properties in their CSS and ensuring all themed content uses these properties consistently.
Styles
The consumer provides all CSS styling. The component renders with a .theme-provider class for targeting. No default styles are included -- this is a fully headless component. The component sets display: contents to avoid affecting layout.
Testing
- Verify the component renders a
<div>element with classtheme-provider - Verify nested theme object is flattened into
--theme-*CSS custom properties - Verify base theme values are applied when no custom override is provided
- Verify nested ThemeProvider overrides parent theme values
- Verify
display: contentsis applied - Verify pass-through attributes are applied
Advice
- Designers: Ensure all theme color combinations meet WCAG 2.2 AAA contrast ratios. Define a complete theme object so that no component is left unstyled. Test themes at multiple viewport sizes.
- Developers: Use consistent naming conventions in theme objects. Always provide a base theme to ensure all custom properties have fallback values. Use CSS
var()with fallbacks when referencing theme properties.
Related components
theme-picker— a picker for selecting a visual themetheme-select— a select dropdown for choosing a themetheme-view— a read-only display of the current theme