InfinitiBit Design System

Themes

How @infinitibit_gmbh theme packages work, how to use one, and how to add a brand theme.

Themes

A theme is a published package that provides the design tokens for one brand. @infinitibit_gmbh/ui defines the token contract (the --ib-* variables); a theme package supplies their values. v1 theming is build-time: a product app installs exactly one theme and imports its stylesheet.

Using a theme (consuming teams)

Install one theme and import its CSS once at your app entry:

import '@infinitibit_gmbh/theme-default/theme.css';

That single file is the complete token set — light values at :root and dark values under .dark (see Dark mode). You do not separately import @infinitibit_gmbh/ui/tokens.css in a themed app; the theme supersedes it. Then build with the tokens as usual (see Design Tokens).

Available themes

PackageUseDistinct from default
@infinitibit_gmbh/theme-defaultthe default brand— (the baseline)
@infinitibit_gmbh/theme-docsthis docs siteprimary remapped to the brighter blue ramp; one step rounder control radius

How themes are built

Each theme reuses @infinitibit_gmbh/ui's DTCG token source and layers its own overrides, via Style Dictionary:

  • theme-default → ui source, no overrides (≡ the defaults).
  • theme-docs → ui source + overrides/*.tokens.json (loaded last, so they win).

The build is dual-mode — two Style Dictionary passes (light + dark semantic sources) concatenated, via the shared tokens-source/sd-dual-mode.mjs helper (each theme's build-tokens.mjs calls it). Each theme emits two artifacts (dist-only — built in CI, not committed):

  • theme.css:root { …light… } + .dark { …dark… } — the production import.
  • theme.scoped.css[data-theme="<name>"] { … } + [data-theme="<name>"].dark { … } — used by Storybook's brand + mode toolbars.

Because themes regenerate from ui's source, they always carry exactly the ui token contract and can't drift from the UI version they ship with. They're also in the Changesets fixed group with @infinitibit_gmbh/ui, so versions move together. See ADR 0009 and ADR 0016 (dual-mode).

Dark mode

Themes ship light and dark values: light at the theme selector, dark under a .dark class. Mode is orthogonal to brand — brand rides [data-theme], mode rides .dark — so any brand works in either mode.

To enable dark mode, toggle the .dark class on <html> (or any ancestor):

<html class="dark"> … </html>

With a theme manager like next-themes, use attribute="class" and it adds .dark for you:

<ThemeProvider attribute="class" defaultTheme="system" enableSystem>

Components need no changes — they read --ib-color-*, which resolve to the dark values whenever .dark is active. Load the base font too (Inter): import '@fontsource-variable/inter'.

Adding a brand theme

  1. Create packages/theme-<brand>/ (copy theme-docs's shape).
  2. Add overrides/*.tokens.json redefining only the token paths you want to change (e.g. palette.primary.*, radius.*). Everything else inherits the defaults.
  3. Add a build-tokens.mjs that calls buildDualModeTheme({ buildPath: 'dist/', brand: '<brand>', overrideSources: ['overrides/**/*.tokens.json'] }) from ../ui/tokens-source/sd-dual-mode.mjs (copy theme-docs's).
  4. pnpm --filter @infinitibit_gmbh/theme-<brand> buildtheme.css + scoped variant (each with light :root/[data-theme] + dark .dark blocks).
  5. Add the package to the Changesets fixed group so it stays version-locked.

The product app then imports @infinitibit_gmbh/theme-<brand>/theme.css.

Previewing themes in Storybook

Storybook imports the scoped variants, so the Brand toolbar switches stories between default, docs, and any registered brand, and the Mode toolbar (light/dark) toggles the .dark class — for design review/QA. To add a brand to the toolbar, import its theme.scoped.css in .storybook/preview.ts and add it to the withThemeByDataAttribute map.

Dark mode is supported (toggle .dark, see above). Runtime brand switching (an end user toggling brand live) is still not a v1 default — it requires an RFC.

On this page