Theming
ThemeProvider, design tokens, color modes, spacing density, and how Tailwind variants connect to the BY theme system.
Theming is how @by/experience-system answers “what does this subtree look like, at what density, in which color mode, and under which product theme key?”—without each component importing a shared JavaScript theme object the way Material UI and legacy CCL LuiThemeProvider flows often did.
Three layers work together:
ThemeProvider— Writesdata-ds-theme,data-ds-color,data-ds-spacing, anddiron a wrapperdiv, and exposes React context fortheme,color,spacing,direction, andcontainerRef.- Design tokens — Mostly CSS custom properties shipped through
theme.css(semantic colors, elevation, scales). They are the stable names design and engineering agree on across light and dark. - Tailwind v4 — Utilities and
@custom-variantrules that read those attributes and variables, sodark:/compact:and semantic classes stay in sync with the provider.
Iconography is not a third styling layer, but it ships as the @by/icons package alongside @by/experience-system: the same React and token-aware expectations apply, and BY components that show chevrons, alerts, or menu glyphs assume you consume the BY icon set rather than @jda/lui-common-icon-library-mui5. Install @by/icons with the experience system, wire @source for Tailwind if needed (see Installation), and follow by-icons-skill on Skills.
Install order, @source, and optional style.css are documented in Installation. Portal Shell apps should also read Portal theme consumption so themeMessage.themeObject maps cleanly into ThemeProvider.
Token system
Tokens are the vocabulary of the UI: instead of hard-coding hex values in every feature, you reference variables and utilities that theme.css defines for the active data-ds-color and data-ds-spacing. That is how a single Button implementation can respect dark surfaces and compact density without forking source.
Rough groupings:
| Layer | Role | Where to read more |
|---|---|---|
| Semantic / functional | Page background, surfaces, body text, overlays—what most product chrome should use. | Core colors |
| Palette / scale | Stepped ramps (neutral, accent, brand, status) for charts, badges, and deliberate composition. | Palette colors, |
| Spacing density | standard, compact, ultra-compact change how scaled spacing and typography feel under data-ds-spacing. | Spacing tokens |
| Elevation | Drop and inset shadows that respond to theme and color mode. | Elevation — theme.css and packages/experience-system/src/theme/tokens/elevation |
Prefer semantic names for chrome so when tokens change globally, your screens track without a string replace across hundreds of files. Reach for palette steps when the design language intentionally calls for a scale position (for example heatmaps or data viz).
ThemeProvider
ThemeProvider is a client component. It renders ThemeContext.Provider around a div that carries:
data-ds-theme— Set bytheme;ThemeProviderapplies a branded default so you usually omittheme.data-ds-color—lightordark(required). Drives which values semantic and palette variables resolve to.data-ds-spacing—standard,compact, orultra-compact(required). Drives density: scaled spacing steps, typography scaling, and line-height multipliers used across the subtree.dir—ltrorrtl(optional, defaultltr).
If that wrapper is missing, components still mount, but colors, shadows, and scaled gaps may not match the design specification—because the attributes Tailwind variants and internal styles expect are absent.
Basic usage
'use client';
import { ThemeProvider, Button } from '@by/experience-system';
export function App() {
return (
<ThemeProvider color="light" spacing="standard" direction="ltr">
<Button type="button">OK</Button>
</ThemeProvider>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
theme | 'fractal' | 'fractal' | Sets data-ds-theme. |
color | 'light' | 'dark' | (required) | Sets data-ds-color. |
spacing | 'standard' | 'compact' | 'ultra-compact' | (required) | Sets data-ds-spacing. |
direction | 'ltr' | 'rtl' | 'ltr' | Sets dir. |
children | ReactNode | — | Subtree that should see Experience System context. |
The package exports matching TypeScript aliases for these values: Theme, ThemeColor, ThemeSpacing, and ThemeDirection, plus ThemeContext for the full value from useThemeContext() (for example import type { ThemeSpacing, ThemeContext } from '@by/experience-system').
useThemeContext
Call useThemeContext() only under ThemeProvider. It returns the current theme, color, spacing, direction, and containerRef pointing at the provider’s wrapper div.
Use it when custom code must branch on color or density, or when a third-party library needs a DOM root scoped to the themed region (portals, measurements, “position relative to this shell”). It throws outside the provider so mistakes fail fast during development.
Tailwind variants
theme.css registers @custom-variant hooks that match data-ds-color, data-ds-theme, and data-ds-spacing. That is how utilities like dark:, fractal:, standard:, compact:, and ultra-compact: stay mechanically aligned with ThemeProvider props—without hand-maintaining parallel boolean flags in every feature.
Scaled utilities (names containing scaled) tie into density; see Spacing tokens for how --scale-density and related variables connect to spacing.
Related
- Getting started — Adoption path
- Installation — Tailwind,
theme.css,@source, optionalstyle.css - Portal theme consumption —
themeMessage.themeObject(ThemeResponseMessage) - Skills —
setup-tailwind-theme-provider-skill,portal-theme-message-skill - Spacing tokens — Density and scaled utilities
- Elevation — Shadows, surfaces, and outlines
- Core colors — Functional color usage