Codeskill

Learn to code, step by step

Design-system scale (multi-brand, multi-product)

Scaling a design system across multiple brands and products without forking your entire SCSS tree every time marketing asks for a new colour palette.

One system, many skins

A single-product token file works until you ship a white-label portal, a sub-brand with different typography, or a partner site that shares components but not colours. The mistake is copying _variables.scss into a second repo and hoping someone keeps them in sync.

Instead, separate core tokens (spacing scale, radius steps, shadow levels) from brand tokens (primary colour, font stack, logo spacing). Core tokens rarely change. Brand tokens swap per theme.

Layered token maps

// tokens/_core.scss - shared across all brands
$core-space: (
  'xs': 0.25rem,
  'sm': 0.5rem,
  'md': 1rem,
  'lg': 2rem,
);

$core-radius: (
  'sm': 4px,
  'md': 8px,
  'lg': 16px,
);

// tokens/_brand-acme.scss
$brand: (
  'color-primary': #0066cc,
  'color-surface': #ffffff,
  'font-body': ('Inter', sans-serif),
);

// tokens/_brand-beta.scss
$brand: (
  'color-primary': #cc3300,
  'color-surface': #f5f0eb,
  'font-body': ('Georgia', serif),
);

Each brand file is a map. Components never hard-code #0066cc – they read from whichever brand map is active at compile time.

Compile-time theming with @use

Build one CSS file per brand by pointing the entry file at the right brand partial:

// themes/acme.scss - entry for Acme brand
@use '../tokens/core' as core;
@use '../tokens/brand-acme' as brand;
@use '../components/button';
@use '../components/card';

// components/_button.scss
@use '../tokens/core' as *;
@use '../tokens/brand' as brand;

.button {
  padding: map-get($core-space, 'sm') map-get($core-space, 'md');
  border-radius: map-get($core-radius, 'md');
  background: map-get(brand.$brand, 'color-primary');
  font-family: map-get(brand.$brand, 'font-body');
}

Your build script compiles themes/acme.scss and themes/beta.scss separately. Each output CSS file is brand-complete. No runtime theme switcher required unless the product needs one.

Runtime theming when you need it

When one HTML page must switch brands without a full reload, compile tokens to CSS custom properties instead of literal values:

@mixin emit-brand-vars($brand-map) {
  @each $key, $value in $brand-map {
    --#{$key}: #{$value};
  }
}

[data-brand='acme'] {
  @include emit-brand-vars($brand); // $brand from brand-acme partial
}

[data-brand='beta'] {
  @include emit-brand-vars($brand); // $brand from brand-beta partial
}

.button {
  background: var(--color-primary);
  font-family: var(--font-body);
}

SCSS generates the custom property declarations once. JavaScript or a server-side attribute swap handles the rest. We covered light/dark theming in the intermediate tutorials – multi-brand runtime theming uses the same idea with more maps.

Multi-product boundaries

Products share a design system but not every component. Structure packages so each product imports only what it needs:

  • @org/tokens – core and brand maps, no component CSS
  • @org/components – buttons, cards, forms; depends on tokens
  • @org/product-admin – admin-only components; depends on components + tokens

Products @use the packages they need. A marketing site never pulls in admin table styles. Version tokens and components independently – a breaking rename in tokens is a semver major on that package, not a surprise for every downstream repo.

Governance without bureaucracy

Write down which tokens are public (consumers may override) and which are internal (component implementation detail). Public tokens get stable names and changelog entries. Internal tokens can move freely inside the package.

Review new brand maps against contrast and type scale rules before merge. A map is cheaper to fix in SCSS than after three products ship the wrong primary colour.

Try it

  • Split an existing token file into _core.scss and two brand map files.
  • Compile two entry files and confirm each outputs different primary colours with identical component markup.
  • List which tokens are public API and which are internal-only for one component.
PreviousGoing deep with SCSS