B/ Box Open Elements

Design Tokens

Foundations express design decisions as data. The token layer is a framework-neutral registry that lets any design language — Box's own or a downstream team's — restyle the component catalog without forking component implementations.

Runtime theme switching, boe:design-system-change, and when to use theming vs token authoring are documented in theming.md.

Model

LayerResponsibility
@box/blueprint-web-assets (upstream, not a dependency)source of Box tokens, icons, and illustrations
src/foundations/tokens/registry.tsframework-neutral token + asset registry
src/foundations/tokens/box-defaults.tsthe Box default design system bundle
componentsconsume registered assets by name and fall back safely when no design system is active

Important constraint carried over from the original review: the upstream asset package ships React-oriented JS modules. box-open-elements stays framework-agnostic, so the core library must not hard-depend on those React exports at runtime. Treat @box/blueprint-web-assets as an upstream asset source and register the pieces you want into the registry.

API surface

import {
  applyDesignTokens,
  boxDefaultDesignSystem,
  createDesignTokenStyleText,
  normalizeDesignTokens,
  registerBoxDefaultDesignSystem,
  registerDesignSystem,
  setActiveDesignSystem,
} from "@unofficialbox/box-open-elements/foundations/tokens";
APIPurpose
registerDesignSystem()register any custom token/icon/illustration bundle
normalizeDesignTokens()translate preferred semantic names to internal Box-compatible keys
registerBoxDefaultDesignSystem()register the built-in Box light bundle
registerBoxDarkDesignSystem()register the built-in Box dark bundle (box-dark)
setActiveDesignSystem()switch the active bundle at runtime
applyDesignTokens()write token values onto an element as CSS custom properties
createDesignTokenStyleText()generate a CSS block for SSR or stylesheet injection
resolveDesignIcon() / resolveDesignIllustration()look up a registered asset by name

Custom bundles should use the typed, lower-camel-case semantic names. The

registry translates them to Box-compatible keys and then applies kebab-cased CSS

custom properties with the --boe-token- prefix:

surfaceBrand  →  SurfaceSurfaceBrand  →  --boe-token-surface-surface-brand

The repetitive PascalCase names remain supported for compatibility with Box's

upstream tokens.json inventory and existing consumers, but they are an

internal/conformance vocabulary rather than the preferred authoring API.

Box default example

import {
  applyDesignTokens,
  registerBoxDefaultDesignSystem,
} from "@unofficialbox/box-open-elements/foundations/tokens";

registerBoxDefaultDesignSystem({ setActive: true });
applyDesignTokens(document.documentElement, "box-default");

Dark theme

box-dark is a built-in bundle with the same token keys, icons, and illustrations as box-default — only the surface / text / stroke / status values change. Because every component reads --boe-token-*, switching the active bundle re-themes the whole catalog with no markup change:

import {
  createThemeController,
} from "@unofficialbox/box-open-elements/foundations/theming";

const theme = createThemeController();
theme.start();
theme.setPreference("dark");

The docs-site theme toggles use this controller. Use the registry calls directly only for low-level bundle authoring or token application; see Theming.

Custom design system example

import {
  applyDesignTokens,
  registerDesignSystem,
} from "@unofficialbox/box-open-elements/foundations/tokens";

registerDesignSystem(
  {
    name: "acme",
    tokens: {
      fontFamilyBase: "InterVariable, Inter, sans-serif",
      surfacePrimary: "#ffffff",
      surfaceBrand: "#5b4bff",
      textPrimary: "#111111",
      borderDefault: "#e8e8e8",
    },
    icons: {
      search: '<svg viewBox="0 0 16 16"><circle cx="8" cy="8" r="4"></circle></svg>',
    },
  },
  { setActive: true },
);

applyDesignTokens(document.documentElement, "acme");

Semantic names

Preferred nameCompatibility keyPurpose
surfacePrimarySurfaceSurfaceprimary application/component surface
surfacePrimaryHoverSurfaceSurfaceHoverhover state for the primary surface
surfaceSecondarySurfaceSurfaceSecondarysecondary surface
surfaceBrandSurfaceSurfaceBrandprimary brand action/surface
surfaceBrandHoverSurfaceSurfaceBrandHoverbrand hover state
surfaceBrandPressedSurfaceSurfaceBrandPressedbrand pressed state
surfaceItemSelectedSurfaceItemSurfaceSelectedselected collection item
surfaceItemHoverSurfaceItemSurfaceHoverhovered collection item
textPrimaryTextTextprimary content text
textSecondaryTextTextSecondarysupporting content text
textPlaceholderTextTextPlaceholderplaceholder text
textOnBrandTextTextOnBrandtext on brand surfaces
textDangerTextTextDangerdestructive/error text
borderDefaultStrokeStrokedefault border/divider
borderHoverStrokeStrokeHoveremphasized or hovered border

The exported SemanticDesignTokenMap type includes the full surface, status,

badge, illustration, text, border, and base-font vocabulary. Custom token names

outside that vocabulary remain supported and are converted to --boe-token-*

without translation.

Do not provide both forms of the same token in one bundle. For example,

surfacePrimary and SurfaceSurface together are rejected as ambiguous.

Component consumption rules

  • Components consume tokens as CSS custom properties with safe fallbacks:

background: var(--boe-token-surface-surface-brand, #0061d5);

  • Components must render sensibly with no design system registered.
  • Components that render assets treat asset names as keys into the active design system and fall back to built-in rendering when the key is absent.

Token consumption vs shell / consumer overrides

Source-level Box styling is the default: every catalog surface carries its look in its own shadow styles via --boe-token-*. Demo, docs, and host “shell” CSS must not be treated as the source of the Box look.

ActorResponsibility
Component authorPaint with --boe-token-* + safe fallbacks inside the shadow tree; expose structural parts for overrides
Host / docs shellStart a theme controller on a root; keep layout chrome separate from component paint
App consumerTheme by swapping design-system bundles; customize structure with ::part(); use rare host custom properties only when the component documents them

When to use which lever

NeedUseDo not
Brand / theme colors, text, strokes, status--boe-token-* inside the component (or a registered custom bundle)Hardcode hex in shadow styles without a token + fallback
One-off visual tweak for a single embeddingtag::part(name) { … } from outsideFork the component or restyle via brittle deep selectors
Documented structural host knobs (e.g. collapsed nav label visibility)Component-owned custom properties such as --boe-nav-label-displayInvent undocumented --boe-* vars on the host
Whole-app light/dark or white-label themecreateThemeController with built-in or registered bundle namesPer-page shell CSS that paints over every control
Third-party SCSS → token vocabularyStyle bridge (bun run style-bridge../integration/style-bridge.md)Hand-copy third-party rules into shadow trees

:host { color: inherit; font: inherit; } is intentional so typography can follow the embedding page while paint (background, border, status, brand) still comes from tokens.

Native closed-widget limits

Some native controls only accept limited styling (accent-color, closed UA popups). Prefer tokens for what the platform allows; do not fake a full custom chrome unless the component is upgraded structurally:

  • <datalist> popups (combobox)
  • Native <select> / date / time picker popups
  • Bare <input type="range"> tracks (beyond accent-color)

Docs-site API tab

The component docs API tab lists design tokens used by scanning the primary host’s shadow styles for --boe-token-* references. That inventory is derived from the live preview — empty when a surface truly uses no tokens.

Why this boundary works

DecisionReason
keep asset package out of core runtime depspreserves framework neutrality
use a registry instead of hard-coded importsmakes design-language swaps explicit and testable
keep fallback rendering in componentscomponents still work with no design system registered
apply tokens via CSS custom propertiesworks for both Box defaults and downstream custom themes

Interactive state helpers

Batch 3 fidelity work ships shared CSS snippets from

@unofficialbox/box-open-elements/foundations/tokens so interactive parts share one

focus/hover/active/disabled language:

import {
  boeBrandInteractiveStyles,
  boeNeutralInteractiveStyles,
  boeFocusVisibleStyles,
} from "@unofficialbox/box-open-elements/foundations/tokens";

const styles = `
  ${boeNeutralInteractiveStyles('[part="trigger"]')}
  ${boeBrandInteractiveStyles('[part="confirm"]')}
`;
HelperUse when
boeNeutralInteractiveStyles(selector)surface buttons, chips, triggers, rows
boeBrandInteractiveStyles(selector)primary/confirm actions
boeFocusVisibleStyles(selector)controls that already own hover (native inputs)
boeFocusRingShadowraw box-shadow value for custom rules

The focus ring always resolves through --boe-token-surface-surface-brand (opaque brand fallback #0061d5) so it adapts between box-default and box-dark and stays high-contrast when no design system is registered.

Follow-ups

  • Expand the Box default bundle only with tokens and assets actually used by components or the docs site.
  • Add a build-time extraction script if we want to mirror more of @box/blueprint-web-assets automatically.