Multi-Brand & White-Label Token Architecture

Operating one design-token system across many brands, white-label clients, and SaaS tenants is the hardest scaling problem in design-ops. A single careless token reference—a component reading a primitive hex value instead of a semantic alias—collapses the entire brand surface the moment a second client appears. This guide defines the layered resolution model that keeps one codebase serving N brands without branching CSS, duplicating components, or losing the ability to enforce global quality standards. The audience is frontend architects and design-ops engineers responsible for a shared component library consumed by product lines that each have distinct visual identities.

4-Layer Token Resolution Pipeline Four stacked layers—Base Primitives, Brand Layer, Tenant Override Layer, and Component—showing how CSS custom property values cascade downward through scoped selectors, resolving to a final computed value for each tenant and brand combination. Layer 1 — Base / Core Primitives Immutable raw values: color scales, spacing steps, type ramp. Never referenced by components directly. Layer 2 — Brand A [data-brand="alpha"] semantic tokens --color-action-primary: #0ea5e9 Layer 2 — Brand B [data-brand="beta"] semantic tokens --color-action-primary: #7c3aed Tenant A1 [data-tenant="a1"] accent: #0369a1 Tenant A2 [data-tenant="a2"] accent: #0c4a6e Tenant B1 [data-tenant="b1"] accent: #6d28d9 Tenant B2 [data-tenant="b2"] accent: #4c1d95 Layer 4 — Component Reads only semantic tokens. Never reads primitives. Resolved value is the tenant→brand→base cascade winner.
The 4-layer token resolution pipeline. Each brand scope overrides only semantic tokens; each tenant scope overrides only within its brand. Components sit at the bottom of the cascade and consume whatever the layers above have resolved.

Architectural Objectives

  • Maintain one component library that renders correctly under any brand or tenant combination without feature flags or forked stylesheets.
  • Enforce a one-way dependency chain: primitives feed semantics, semantics feed components. No component references a primitive directly.
  • Define a theme contract—a fixed, versioned set of semantic tokens that every brand must supply—so the component library can make safe assumptions at compile time.
  • Support both build-time brand resolution (generating per-brand static CSS bundles) and runtime tenant injection (loading a tenant’s delta tokens via a lightweight JSON payload or a <link> swap).
  • Keep the tenant override surface minimal and auditable: tenants change accent color, logo treatment, and radius personality—not typography scales or spacing grids.
  • Enable cascade-layer ordering (@layer base, brand, tenant, components) so specificity never needs to be managed by hand.

Token Taxonomy & Layering Hierarchy

The system defines four layers with distinct roles. Understanding the boundary between each layer is what prevents the system from collapsing into a monolith where every brand ships its own fork of the component library.

Layer Responsibility Mutability Scope
Base / Primitives Raw design values: hex colors, px steps, ms durations, unitless line-heights Immutable — only the core design-ops team can change these Global :root; shared across all brands
Brand Maps primitives to semantic roles for one brand identity Owned by each brand team; changes are versioned [data-brand="<id>"] attribute selector on :root or <body>
Tenant / Client Micro-overrides within a brand: accent hue shift, logo-adjacent color, custom radius Owned by tenant onboarding pipeline; validated against the theme contract [data-tenant="<id>"] nested inside [data-brand] or applied to a component mount point
Component Reads semantic tokens to produce final rendered values Read-only consumers; produce localized component tokens (--btn-*) only for layout math Shadow DOM :host or BEM block selector

The critical insight is that brand teams never touch primitives and tenant teams never touch brand decisions. Primitives are the shared language. Semantic tokens are the brand’s dialect. Tenant overrides are accent-level adjustments that the theme contract permits. Any override that falls outside the contract is rejected at build time.

The relationship between the primitive→semantic→component hierarchy described in Token Fundamentals & Naming Conventions becomes load-bearing at scale: if components reference --ds-blue-500 instead of --ds-color-action-primary, the brand layer has nothing to intercept, and every brand must ship its own version of every component. The three-tier hierarchy is not optional once you have more than one brand.

Naming Conventions & Scoping

Stable naming is the API surface of the token system. Every name communicates layer membership and prevents accidental cross-layer coupling.

Lexical rules

  1. All tokens use the -- custom property prefix.
  2. Layer membership is signaled by the second segment: --ds-primitive-*, --brand-*, --tenant-*, --comp-*.
  3. Semantic tokens omit brand identity from the name—the brand is established by the attribute scope, not the variable name. --ds-color-action-primary has the same name under every brand; only its value differs.
  4. Tenant overrides shadow the exact same semantic token names they change. They do not introduce new names.
  5. Component tokens are defined and consumed within the same component file; they are never global.
Pattern Example Use Case
--ds-primitive-<category>-<step> --ds-primitive-color-blue-500 Base value storage; never consumed by components
--ds-color-<role> --ds-color-action-primary Semantic color; consumed by components; overridden per brand
--ds-space-<step> --ds-space-4 Semantic spacing; rarely overridden per brand
--brand-<property> --brand-radius-personality Brand-specific token outside the primitive scale
--tenant-<property> --tenant-accent-hue Tenant-scoped hue shift or spot color
--comp-<component>-<property> --comp-btn-padding-inline Internal layout math; scoped to the component block

Attribute scoping convention

Brand identity is stored on a root element as a data-brand attribute. Tenant identity layers on top with data-tenant. The HTML looks like:

<html data-brand="alpha" data-tenant="acme">

CSS uses attribute selectors to scope overrides:

[data-brand="alpha"] {
  --ds-color-action-primary: #0ea5e9;
}

[data-brand="alpha"][data-tenant="acme"] {
  --ds-color-action-primary: #0369a1;
}

The cascade resolves the most-specific matching rule. No JavaScript specificity hacks. No class toggles. The browser does the work.

The Theme Contract

A theme contract is the fixed set of semantic tokens that every brand must define. It is the interface between the shared component library and the brand teams. Without a contract, brand teams can omit tokens that components depend on, causing silent fallbacks to browser defaults or broken visual states.

The contract is expressed as a JSON Schema that the build pipeline validates against each brand’s token file before compilation. A brand token file that fails validation blocks the CI run—it cannot reach the CDN.

A minimal theme contract specifies:

  • All semantic color roles (--ds-color-action-primary, --ds-color-surface-default, --ds-color-text-primary, --ds-color-border-default, --ds-color-status-error, --ds-color-status-success, --ds-color-status-warning)
  • The focus ring color (--ds-color-focus-ring) — required for accessibility compliance
  • Corner radius personality (--ds-radius-surface, --ds-radius-control)
  • Brand typeface stack (--ds-font-family-base, --ds-font-family-display)
  • Elevation shadow formula (--ds-shadow-overlay)

Every token in the contract gets a version number. When the component library requires a new semantic token, the contract version increments and brand teams receive a migration window before the old version is sunset. This versioning strategy is the core subject of Theme Contract Versioning.

Cascade-Layer Ordering

CSS cascade layers eliminate the specificity management problem that plagues multi-brand systems. Declare the layer order once in the base stylesheet:

@layer base, brand, tenant, components;

This single declaration establishes resolution priority regardless of stylesheet load order. Styles in tenant always beat styles in brand; styles in components always beat styles in tenant. Selector weight within a layer becomes irrelevant for cross-layer precedence.

The layer assignment for each token file follows the taxonomy exactly:

@layer base {
  :root {
    --ds-primitive-color-blue-500: #3b82f6;
    --ds-color-action-primary: var(--ds-primitive-color-blue-500); /* fallback */
  }
}

@layer brand {
  [data-brand="alpha"] {
    --ds-color-action-primary: #0ea5e9;
    --ds-color-surface-default: #f0f9ff;
  }
}

@layer tenant {
  [data-brand="alpha"][data-tenant="acme"] {
    --ds-color-action-primary: #0369a1;
  }
}

@layer components {
  .btn-primary {
    background-color: var(--ds-color-action-primary);
    border-radius: var(--ds-radius-control, 0.375rem);
  }
}

The component in the components layer reads --ds-color-action-primary. The cascade resolves from the most-specific applicable @layer rule: if tenant has a value, that wins; if only brand has a value, that wins; if neither, the base fallback applies. The brand theme layering strategies section covers the trade-offs between this flat approach and nested @layer blocks when you have dozens of brands.

Note that dark mode behaves as a peer theme layer alongside brand, not a sub-layer of it. The relationship between brand theming and dark mode implementation is one of orthogonal axes: a tenant can have both a brand identity and a dark preference simultaneously, which requires the @media (prefers-color-scheme) block to live inside the brand layer rather than outside it.

Build-Time vs. Runtime Brand Resolution

The choice between generating static per-brand CSS bundles at build time versus loading brand tokens dynamically at runtime affects bundle size, caching strategy, CDN architecture, and deployment complexity.

Build-time resolution (static bundles)

The build pipeline reads all brand token files, compiles one CSS bundle per brand, and serves the matching file from the CDN based on a request header or subdomain routing rule.

  • Advantages: zero runtime overhead; full tree-shaking of unused tokens; brand tokens baked into the file hash for long-lived caching.
  • Disadvantages: adding a new brand requires a full build; tenant overrides cannot be applied after the bundle is generated unless a separate tenant layer file is loaded at runtime.

Runtime brand resolution (dynamic injection)

The application loads a small base stylesheet (primitives + component definitions) and fetches a brand-specific token JSON payload at startup. The payload is converted to a <style> block injected into <head> or applied to a CSS custom property store.

  • Advantages: new brands can be onboarded without a code deploy; tenants can supply their own token delta without touching the CDN.
  • Disadvantages: first-paint latency increases; the token-to-style injection must be idempotent and handle race conditions.

Most enterprise SaaS platforms land on a hybrid model: build-time bundles for the handful of known first-party brands (low count, high traffic), and a runtime injection path for the broad population of white-label clients. The per-tenant runtime theming section covers the runtime injection architecture in detail, including how to avoid a flash of unstyled brand during hydration.

Cross-Domain Workflow: From Brand Token File to Production CSS

The pipeline that takes a brand’s raw token decisions and delivers production CSS has seven stages. Each stage is a quality gate; failures block progression.

  1. Brand token file authoring — Brand team delivers a JSON file that maps semantic token names to values. The file must not reference primitive names or introduce new token names outside the contract.
  2. Contract validation — The JSON Schema validator checks completeness (all required tokens present), type correctness (colors must be valid CSS color values), and naming convention compliance. Failures produce a structured error report, not a vague compilation error.
  3. Primitive resolution — Style Dictionary resolves {ref} references, substituting primitive values where brand tokens use {ds.primitive.color.blue.500} syntax.
  4. CSS compilation — The compiler wraps each brand’s resolved tokens in [data-brand="<id>"] selectors and assigns them to the correct @layer. It also generates a per-brand TypeScript constant file for consumers who need token values at runtime (e.g., for <canvas> rendering).
  5. Tenant overlay merge — Tenant-specific overrides (from a separate, smaller JSON file) are validated against the subset of tokens the contract marks as tenant-overridable, then compiled into [data-brand][data-tenant] selectors.
  6. Bundle assembly — All brand and tenant layers are assembled into the final stylesheet. If using build-time bundles, one file per brand is emitted; if using a base + delta approach, the base file and per-brand delta files are emitted separately.
  7. Contract version stamp — The output stylesheet receives a /* @contract-version: 2.1 */ comment. The component library checks this at startup in development mode to warn when a brand’s token file was compiled against an older contract version than the components expect.

The handling of multi-brand export conflicts during design-tool sync—particularly when Figma token plugins flatten brand hierarchies into a single namespace—is covered in handling multi-brand export conflicts in token sync.

Production Code Reference

Multi-brand token structure (JSON)

{
  "contract": {
    "version": "2.1",
    "requires": [
      "color.action.primary",
      "color.surface.default",
      "color.text.primary",
      "color.border.default",
      "color.focus.ring",
      "color.status.error",
      "color.status.success",
      "radius.surface",
      "radius.control",
      "font.family.base",
      "shadow.overlay"
    ]
  },
  "brands": {
    "alpha": {
      "color": {
        "action": { "primary": "#0ea5e9", "primary-hover": "#0284c7" },
        "surface": { "default": "#f0f9ff", "elevated": "#ffffff" },
        "text": { "primary": "#0c4a6e", "secondary": "#0369a1" },
        "border": { "default": "#bae6fd" },
        "focus": { "ring": "#0ea5e9" },
        "status": { "error": "#dc2626", "success": "#16a34a" }
      },
      "radius": { "surface": "0.75rem", "control": "0.375rem" },
      "font": { "family": { "base": "'Inter', system-ui, sans-serif" } },
      "shadow": { "overlay": "0 8px 24px rgba(14,165,233,0.15)" }
    },
    "beta": {
      "color": {
        "action": { "primary": "#7c3aed", "primary-hover": "#6d28d9" },
        "surface": { "default": "#f5f3ff", "elevated": "#ffffff" },
        "text": { "primary": "#2e1065", "secondary": "#5b21b6" },
        "border": { "default": "#ddd6fe" },
        "focus": { "ring": "#7c3aed" },
        "status": { "error": "#dc2626", "success": "#16a34a" }
      },
      "radius": { "surface": "1rem", "control": "0.5rem" },
      "font": { "family": { "base": "'Outfit', system-ui, sans-serif" } },
      "shadow": { "overlay": "0 8px 24px rgba(124,58,237,0.15)" }
    }
  },
  "tenants": {
    "acme": {
      "brand": "alpha",
      "overrides": {
        "color.action.primary": "#0369a1",
        "color.focus.ring": "#0369a1"
      }
    }
  }
}

Each brand key supplies exactly the tokens the contract requires. The tenants block references a parent brand and lists only the delta. A tenant that tries to override a token not listed in the contract’s tenant-overridable set is rejected by the validator.

Compiled CSS with [data-brand] scoping

/* === @layer base: Primitives & semantic fallbacks === */
@layer base {
  :root {
    /* Primitives: never consumed by components directly */
    --ds-primitive-blue-500: #3b82f6;
    --ds-primitive-violet-600: #7c3aed;
    --ds-primitive-sky-500: #0ea5e9;

    /* Semantic fallbacks (active when no brand is applied) */
    --ds-color-action-primary: var(--ds-primitive-blue-500);
    --ds-color-surface-default: #ffffff;
    --ds-color-text-primary: #0f172a;
    --ds-color-border-default: #e2e8f0;
    --ds-color-focus-ring: var(--ds-primitive-blue-500);
    --ds-radius-surface: 0.5rem;
    --ds-radius-control: 0.25rem;
    --ds-font-family-base: system-ui, sans-serif;
    --ds-shadow-overlay: 0 8px 24px rgba(0,0,0,0.12);
  }
}

/* === @layer brand: Per-brand semantic overrides === */
@layer brand {
  [data-brand="alpha"] {
    --ds-color-action-primary: #0ea5e9;
    --ds-color-action-primary-hover: #0284c7;
    --ds-color-surface-default: #f0f9ff;
    --ds-color-surface-elevated: #ffffff;
    --ds-color-text-primary: #0c4a6e;
    --ds-color-text-secondary: #0369a1;
    --ds-color-border-default: #bae6fd;
    --ds-color-focus-ring: #0ea5e9;
    --ds-color-status-error: #dc2626;
    --ds-color-status-success: #16a34a;
    --ds-radius-surface: 0.75rem;
    --ds-radius-control: 0.375rem;
    --ds-font-family-base: 'Inter', system-ui, sans-serif;
    --ds-shadow-overlay: 0 8px 24px rgba(14,165,233,0.15);
  }

  [data-brand="beta"] {
    --ds-color-action-primary: #7c3aed;
    --ds-color-action-primary-hover: #6d28d9;
    --ds-color-surface-default: #f5f3ff;
    --ds-color-surface-elevated: #ffffff;
    --ds-color-text-primary: #2e1065;
    --ds-color-text-secondary: #5b21b6;
    --ds-color-border-default: #ddd6fe;
    --ds-color-focus-ring: #7c3aed;
    --ds-color-status-error: #dc2626;
    --ds-color-status-success: #16a34a;
    --ds-radius-surface: 1rem;
    --ds-radius-control: 0.5rem;
    --ds-font-family-base: 'Outfit', system-ui, sans-serif;
    --ds-shadow-overlay: 0 8px 24px rgba(124,58,237,0.15);
  }
}

/* === @layer tenant: Tenant micro-overrides === */
@layer tenant {
  [data-brand="alpha"][data-tenant="acme"] {
    --ds-color-action-primary: #0369a1;
    --ds-color-focus-ring: #0369a1;
  }
}

/* === @layer components: Token consumers === */
@layer components {
  .btn-primary {
    /* Components read semantic tokens only */
    background-color: var(--ds-color-action-primary);
    color: var(--ds-color-surface-elevated, #ffffff);
    border-radius: var(--ds-radius-control);
    font-family: var(--ds-font-family-base);
    outline-color: var(--ds-color-focus-ring);
  }

  .btn-primary:hover {
    background-color: var(--ds-color-action-primary-hover,
      var(--ds-color-action-primary));
  }

  .card {
    background-color: var(--ds-color-surface-default);
    border: 1px solid var(--ds-color-border-default);
    border-radius: var(--ds-radius-surface);
    box-shadow: var(--ds-shadow-overlay);
  }
}

The [data-brand][data-tenant] compound selector in the tenant layer has higher specificity than the bare [data-brand] in the brand layer, and because it also lives in a higher-precedence @layer, it wins cleanly without requiring !important. The component rules contain zero color or radius literals—every value is a var() call.

Build configuration generating per-brand stylesheets

// build/compile-brands.mjs
import StyleDictionary from 'style-dictionary';
import { tokenData } from './tokens.json' assert { type: 'json' };
import { validateContract } from './validate-contract.mjs';

const CONTRACT_VERSION = tokenData.contract.version;

for (const [brandId, brandTokens] of Object.entries(tokenData.brands)) {
  // 1. Validate every brand file against the contract before compilation
  const validationResult = validateContract(brandTokens, tokenData.contract);
  if (!validationResult.ok) {
    console.error(`[${brandId}] Contract violation:`, validationResult.errors);
    process.exit(1);
  }

  // 2. Configure Style Dictionary for this brand
  const sd = new StyleDictionary({
    tokens: brandTokens,
    platforms: {
      css: {
        transformGroup: 'css',
        prefix: 'ds',
        buildPath: `dist/brands/${brandId}/`,
        files: [
          {
            destination: 'tokens.css',
            format: 'css/variables',
            options: {
              // Wrap in data-brand selector + assign to @layer brand
              selector: `[data-brand="${brandId}"]`,
              outputReferences: true,
              // Prepend layer declaration comment consumed by PostCSS plugin
              fileHeader: () => [
                `@contract-version: ${CONTRACT_VERSION}`,
                `@brand: ${brandId}`,
              ],
            },
          },
          {
            // TypeScript constants for canvas / SVG rendering contexts
            destination: 'tokens.ts',
            format: 'javascript/es6',
          },
        ],
      },
    },
  });

  await sd.buildAllPlatforms();
  console.log(`[${brandId}] Built successfully (contract v${CONTRACT_VERSION})`);
}

// 3. Compile tenant overlays separately
for (const [tenantId, tenantConfig] of Object.entries(tokenData.tenants)) {
  const { brand, overrides } = tenantConfig;
  const overrideRules = Object.entries(overrides)
    .map(([path, value]) => {
      const cssVarName = `--ds-${path.replace(/\./g, '-')}`;
      return `  ${cssVarName}: ${value};`;
    })
    .join('\n');

  const tenantCSS = [
    `/* @contract-version: ${CONTRACT_VERSION} */`,
    `/* @tenant: ${tenantId} @brand: ${brand} */`,
    `@layer tenant {`,
    `  [data-brand="${brand}"][data-tenant="${tenantId}"] {`,
    overrideRules,
    `  }`,
    `}`,
  ].join('\n');

  await fs.writeFile(`dist/tenants/${tenantId}/overrides.css`, tenantCSS, 'utf8');
  console.log(`[${tenantId}] Tenant overlay built`);
}

The build script validates before compiling, which means malformed brand files never produce corrupted output—they fail loudly with a structured error rather than generating CSS with missing tokens. The contract version stamp in the output allows the component library to warn engineers when they are running against a stale brand bundle. Token compiler options and the trade-offs between Style Dictionary, Theo, and Cobalt for multi-brand workflows are covered in the token compiler comparison section.

Implementation Boundaries & CSS Architecture

:root vs [data-brand] vs :host

The [data-brand] attribute selector works for most host-rendered applications but breaks inside shadow DOM because the shadow root cannot see attribute selectors on the document <html> element by default. Three options exist:

  1. CSS custom property inheritance (preferred): custom properties inherit across shadow boundaries. If the [data-brand] rule sets --ds-color-action-primary on <html>, a shadow DOM component reading var(--ds-color-action-primary) inside :host will inherit the value. No extra work required for inheritance to work.
  2. @property inheritance explicit declaration: register each token with inherits: true (which is the default for custom properties) to be explicit and survive minifiers that strip comments.
  3. Adopted stylesheets: inject the brand layer stylesheet via shadowRoot.adoptedStyleSheets for programmatic control over which brand tokens are visible inside a specific shadow root.

White-label client isolation

When a SaaS platform renders multiple tenants on the same page (e.g., an admin dashboard comparing two client environments side by side), [data-brand][data-tenant] scoping on individual container elements—not on <html>—prevents cross-contamination. The same CSS architecture applies; only the anchor element changes from <html> to .tenant-container.

This per-container isolation pattern is the architectural basis for white-label token overrides, where the token delta applied to each client container must also not bleed into shared chrome elements (navigation, modals rendered at the document root) that belong to the platform brand, not the white-label client.

Common Pitfalls & Anti-Patterns

Issue Root Cause Mitigation Strategy
Components referencing primitives directly Engineers copy-paste --ds-primitive-blue-500 into component CSS instead of using the semantic alias. Lint rule (custom stylelint plugin) that flags any var(--ds-primitive-*) usage outside of @layer base. Fail CI on violation.
Tenant overrides leaking into shared platform chrome [data-tenant] selector applied to <html> rather than the tenant-scoped container, so platform navigation inherits tenant tokens. Apply [data-tenant] to the tenant’s root container element. Platform chrome elements must not descend from this container.
Brand contract drift — components need a new token, brands have not supplied it Component library adds --ds-color-action-tertiary without incrementing the contract version or giving brand teams a migration window. Semantic versioning on the contract. Minor version additions are additive with fallbacks; major version bumps require all brands to update before the component ships.
Specificity collision when combining brand + dark mode Dark mode @media block is declared outside @layer brand, giving it unpredictable precedence. Declare dark mode rules inside the relevant @layer. @media blocks inside a layer respect layer precedence; those outside do not.
Runtime injection race — flash of wrong brand Tenant token JSON loads asynchronously, causing a frame where the base (unbranded) tokens are visible. Block first paint on brand token resolution server-side using a Set-Cookie header with the brand preference, then inject a <style> block from the SSR response before the <body>.
Tenant overrides outside the permitted set Tenants supply overrides for spacing or typography tokens that the contract does not mark as overridable, causing unintended layout shifts. Whitelist approach in the contract schema: only tokens in tenant-overridable can appear in a tenant override file. Validator rejects unknown keys.

Frequently Asked Questions

How many semantic tokens should a theme contract include?

Keep the required set as small as defensible. A contract with 200 required tokens is a maintenance liability—every new brand team faces a cliff. Start with the minimum viable set: one primary action color, one surface color, one text color, one border color, focus ring, status colors, one radius token, one font family, and one shadow. Add tokens to the contract only when a component genuinely cannot fall back gracefully without them. Tokens that have obvious fallbacks (hover states can fall back to the base action color) should be optional contract extensions, not required fields.

Should brand tokens reference primitives using {ref} syntax, or hardcode the resolved value?

Use {ref} syntax in the source JSON and resolve to hardcoded values only in the compiled CSS output. The reference in JSON documents the design intent (this brand uses blue-500 for its action color) and survives a primitive value change without requiring the brand team to update their file. If primitives are immutable and a brand hardcodes #3b82f6 instead of {primitive.blue.500}, a future palette adjustment creates a silent divergence. The Style Dictionary build resolves references; the brand team never sees the compiled hex unless they look at the build artifact.

Is it safe to use [data-brand] attribute selectors instead of class-based selectors?

Yes, with one caveat: [data-brand] and .brand-alpha have the same specificity (both are single attribute or class selectors), so there is no specificity advantage either way. The data-* approach is preferable because it separates the concern of brand identity from CSS class naming, which is typically owned by component and BEM conventions. The data-brand attribute is readable in DevTools and accessible to JavaScript without coupling to CSS class nomenclature.

How do we handle a brand that needs to override a component’s internal layout token?

This is the boundary violation to watch for. If a brand needs .btn-primary to have different padding than the default, the correct fix is to promote --comp-btn-padding-inline to a semantic token in the contract (e.g., --ds-control-padding-inline) and let brands supply it. Brands must not reach inside component internals. If a brand team reports that they cannot achieve a visual requirement without touching a component-internal token, that is a signal that the component is under-parameterized—it needs to expose more semantic knobs.

How does this model interact with micro-frontend architectures where each team ships its own CSS?

The @layer declaration order is the coordination mechanism. Each micro-frontend’s stylesheet must declare the same layer order at the top and must place its component styles in @layer components. The brand and tenant layers live in a shared stylesheet loaded once by the shell. Because custom properties inherit through the DOM, a micro-frontend component mounted inside a [data-brand][data-tenant] container automatically receives the resolved tenant tokens without any additional coordination. The only failure mode is if a micro-frontend declares its own @layer without prefixing it into the shared layer namespace—which is an architectural rule enforced by code review and, where possible, a build-time lint check.