Multi-Brand Tailwind CSS Themes from Simple Design System with Extended Collections (for non-enterprise plans)
A demo project for building multi-brand Tailwind CSS themes based on Simple Design System with Extended Collections. Uses TokensBrücke and Style Dictionary to transform Figma design tokens, adapted for non-enterprise Figma environments where Extended Collections and the Variables REST API are not available.
┌─────────────────────────────────────────────────────────────────────────────┐
│ BUILD PIPELINE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Figma │
│ │ │
│ │ Export via TokensBrücke │
│ ▼ │
│ scripts/design.tokens.json │
│ │ │
│ │ Transform via transform-design-tokens.ts │
│ │ • Map Figma structure → Tailwind theme keys │
│ │ • Rewrite token references │
│ ▼ │
│ packages/ui/tokens/theme.tokens.json │
│ packages/ui/tokens/typography.tokens.json │
│ │ │
│ │ Generate via Style Dictionary │
│ │ • Resolve modes (light-dark(), CSS variable toggles) │
│ │ • CSS variables in @theme blocks │
│ │ • Typography utilities with @utility │
│ │ • tailwind-merge configuration │
│ ▼ │
│ packages/themes/*/theme.generated.css │
│ packages/ui/typography.generated.css │
│ packages/ui/src/tailwind-merge-config.json │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Prerequisites: Node.js 24+, pnpm 10+
# Install dependencies
pnpm install
# Build tokens and packages
pnpm build
# Start development server
pnpm dev| Command | Description |
|---|---|
pnpm build |
Transform tokens → Generate CSS → Build all |
pnpm build:sd |
Transform and generate tokens only |
pnpm dev |
Start development server |
multi-brand-tailwindcss-themes-from-sds-with-extended-collections-for-non-enterprise/
├── scripts/
│ ├── design.tokens.json # TokensBrücke export (input)
│ └── transform-design-tokens.ts # Token transformation logic
├── packages/
│ ├── themes/
│ │ ├── default/
│ │ │ ├── theme.generated.css # (generated)
│ │ │ ├── index.css # Theme entry point
│ │ │ └── package.json
│ │ ├── brand-2/
│ │ │ └── ...
│ │ └── brand-3/
│ │ └── ...
│ └── ui/
│ ├── tokens/
│ │ ├── theme.tokens.json # (generated)
│ │ └── typography.tokens.json # (generated)
│ ├── typography.generated.css # (generated)
│ ├── index.css
│ └── src/
├── apps/
│ └── web/ # Demo application
├── sd.config.ts # Style Dictionary configuration
└── package.json
The original Simple Design System with Extended Collections uses Extended Collections for multi-brand management. However, Extended Collections is an enterprise-only feature. Non-enterprise users must add brand themes to the Theme collection before exporting:
- Open the Theme collection in the Variables panel
- Use Duplicate mode on the
Lightmode, then rename it toBrand #2 - Light - Use Duplicate mode on the
Darkmode, then rename it toBrand #2 - Dark - Adjust the variable values in the new modes for your brand
- Repeat for additional brands (Brand #3, etc.)
Use TokensBrücke to export Figma variables and styles:
Required settings:
- Enable Typography-styles
- Enable Effect-styles
- Enable Use DTCG keys format
The export creates scripts/design.tokens.json with this structure:
{
"Color Primitives": {
"Slate": {
"100": { "$type": "color", "$value": "#f3f3f3" }
}
},
"Theme": {
"Background": {
"Primary": {
"$type": "color",
"$value": "#ffffff",
"$extensions": {
"mode": {
"Light": "#ffffff", "Dark": "#1a1a1a",
"Brand #2 - Light": "#f8f8f8", "Brand #2 - Dark": "#1c1c1c",
"Brand #3 - Light": "#fafafa", "Brand #3 - Dark": "#1e1e1e"
}
}
}
}
},
"Typography-styles": { ... },
"Effect-styles": { ... }
}scripts/transform-design-tokens.ts restructures tokens for Tailwind:
Mapping Figma structure to Tailwind theme keys:
| Figma Path | Tailwind Theme Key |
|---|---|
Theme.Background.* |
background-color.* |
Theme.Text.* |
text-color.* |
Theme.Icon.* |
text-color.icon.* |
Theme.Border.* |
border-color.* |
Color Primitives.* |
color.* |
Size.Space.* |
spacing.* |
Size.Radius.* |
radius.* |
Typography Primitives.* |
font.*, text.* |
Effect-styles.* |
shadow.* |
Reference rewriting:
Token references are rewritten to match the new structure:
{Theme.Primary.Font Family} → {font.Primary}
{Color Primitives.Slate.500} → {color.Slate.500}
{Typography Primitives.Scale.Scale 14} → {text.14}
Style Dictionary (sd.config.ts) generates the final CSS. Each theme is defined with its corresponding light/dark mode keys:
const themes: Record<string, [lightModeKey: string, darkModeKey: string]> = {
default: ['Light', 'Dark'],
'brand-2': ['Brand #2 - Light', 'Brand #2 - Dark'],
'brand-3': ['Brand #3 - Light', 'Brand #3 - Dark'],
};Mode resolution (preprocessor):
Each theme registers its own preprocessor (custom/mode-${key}) that resolves the appropriate mode values from $extensions.mode:
// Light/Dark modes → light-dark() function
{ "Light": "#fff", "Dark": "#000", "Brand #2 - Light": "#f8f8f8", ... }
// default theme resolves "Light"/"Dark":
"light-dark(#fff, #000)"
// brand-2 theme resolves "Brand #2 - Light"/"Brand #2 - Dark":
"light-dark(#f8f8f8, ...)"
// Size modes → CSS variable toggle (space toggle hack)
{ "Base": "16px", "Compact": "12px", "Comfortable": "20px" }
// becomes:
"var(--is-size-base, 16px) var(--is-size-compact, 12px) var(--is-size-comfortable, 20px)"Output:
Theme CSS (theme.generated.css):
@theme {
--color-slate-100: #f3f3f3;
--color-slate-200: #e3e3e3;
--background-color-default-default: light-dark(var(--color-white-1000), var(--color-gray-900));
--background-color-default-secondary: light-dark(var(--color-gray-100), var(--color-gray-800));
--background-color-default-tertiary: light-dark(var(--color-gray-300), var(--color-gray-600));
--text-color-default-default: light-dark(var(--color-gray-900), var(--color-white-1000));
--text-color-default-secondary: light-dark(var(--color-gray-500), var(--color-white-500));
--text-color-default-tertiary: light-dark(var(--color-gray-400), var(--color-white-400));
--spacing-100: var(--is-size-base, 4px) var(--is-size-compact, 2px) var(--is-size-comfortable, 8px);
--radius-200: var(--is-size-base, 8px) var(--is-size-compact, 6px) var(--is-size-comfortable, 12px);
}Typography CSS (typography.generated.css):
@utility typography-title-hero {
font-family: var(--font-title-hero);
font-size: var(--text-title-hero);
line-height: var(--leading-title-hero);
font-weight: 700;
letter-spacing: var(--tracking-title-hero);
}
@utility typography-body-base {
font-family: var(--font-body);
font-size: var(--text-body);
line-height: var(--leading-body);
font-weight: 400;
letter-spacing: var(--tracking-body);
}Import Tailwind CSS with your theme:
/* apps/web/src/index.css */
@import 'tailwindcss';
@import '@acme/ui/index.css';
@import '@acme/theme-default/index.css';
@source '../node_modules/@acme/ui';Use the generated Tailwind utilities in your HTML/JSX:
<div class="bg-brand-default p-400 text-brand-on-brand">
<h1 class="typography-title-hero">Hello World</h1>
</div>Import a different theme package:
/* apps/web/src/index.css */
@import 'tailwindcss';
@import '@acme/ui/index.css';
-@import '@acme/theme-default/index.css';
+@import '@acme/theme-brand-2/index.css';
@source '../node_modules/@acme/ui';- Export your Figma tokens using TokensBrücke with the required settings
- Replace
scripts/design.tokens.jsonwith your export - Update
scripts/transform-design-tokens.tsto match your token structure
Modify the mapping in transform-design-tokens.ts to match your Figma organization:
const themeTokens = {
// Map your Figma collections to Tailwind theme keys
'background-color': {
...omit(designTokens.Theme.Background, ['Utilities']),
},
'text-color': {
...omit(designTokens.Theme.Text, ['Utilities']),
icon: omit(designTokens.Theme.Icon, ['Utilities']),
},
// Add your own mappings
'accent-color': {
...designTokens.Theme.Accent,
},
};Add rules for your token paths in the rewriteReferences function:
function rewriteReferences(value: string) {
return (
value
.replaceAll(/{Theme\.(.+)\.Font Family( .+)?}/g, '{font.$1$2}')
.replaceAll(/{Color Primitives\./g, '{color.')
// Add your own rules
.replaceAll(/{YourCollection\./g, '{your-theme-key.')
);
}Light/Dark modes use the CSS light-dark() function, resolved by per-theme preprocessors in sd.config.ts.
Enable dark mode in your application:
:root {
color-scheme: light dark;
}Size modes use the space toggle hack:
/* Enable Base size (default) */
:root {
--is-size-base: ;
--is-size-compact: initial;
--is-size-comfortable: initial;
}
/* Switch to Compact */
:root[data-size='compact'] {
--is-size-base: initial;
--is-size-compact: ;
--is-size-comfortable: initial;
}Figma organizes tokens semantically (e.g., Theme.Background.Primary), while Tailwind organizes by utility (e.g., background-color). This structural mismatch requires transformation regardless of your Figma organization.
The Variables REST API is restricted to enterprise plans. TokensBrücke provides an alternative for non-enterprise users:
- Exports both variables and styles as tokens in one operation via a Figma plugin
- Preserves mode values in token properties (Light/Dark, size variants)
The Simple Design System wasn't designed for Tailwind CSS. This project bridges that gap, showing how to:
- Transform Figma's structure to Tailwind's requirements
- Handle multi-brand themes in non-enterprise Figma environments
- Preserve mode information through the transformation
- Generate production-ready Tailwind CSS themes