Experience System
Foundation

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:

  1. ThemeProvider — Writes data-ds-theme, data-ds-color, data-ds-spacing, and dir on a wrapper div, and exposes React context for theme, color, spacing, direction, and containerRef.
  2. 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.
  3. Tailwind v4 — Utilities and @custom-variant rules that read those attributes and variables, so dark: / 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:

LayerRoleWhere to read more
Semantic / functionalPage background, surfaces, body text, overlays—what most product chrome should use.Core colors
Palette / scaleStepped ramps (neutral, accent, brand, status) for charts, badges, and deliberate composition.Palette colors,
Spacing densitystandard, compact, ultra-compact change how scaled spacing and typography feel under data-ds-spacing.Spacing tokens
ElevationDrop and inset shadows that respond to theme and color mode.Elevationtheme.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 by theme; ThemeProvider applies a branded default so you usually omit theme.
  • data-ds-colorlight or dark (required). Drives which values semantic and palette variables resolve to.
  • data-ds-spacingstandard, compact, or ultra-compact (required). Drives density: scaled spacing steps, typography scaling, and line-height multipliers used across the subtree.
  • dirltr or rtl (optional, default ltr).

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

PropTypeDefaultDescription
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.
childrenReactNodeSubtree 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.