brand-theme-scaffolder
Paste into Claude Code, Claude.ai, the desktop app, or the browser extension — ⌘/Ctrl+V. ✓ Copied. Switch to Claude and paste — ⌘/Ctrl+V.
Brand Theme Scaffolder
Scaffold a new brand theme as a thin semantic override layer on top of a shared base token set. The output is a DTCG-compliant brand file that changes only what the new brand requires, leaving primitive and component tokens shared across all brands.
Usage
/brand-theme-scaffolder <base-tokens-path> --brand <brand-slug> --primary <hex> [--secondary <hex>] [--font-family '<stack>'] [--radius sharp|soft|rounded|pill] [--platform sitecore|optimizely|umbraco|headless]
Examples:
/brand-theme-scaffolder tokens/ --brand nova --primary #0F4C81— minimal brand override/brand-theme-scaffolder tokens/ --brand nova --primary #0F4C81 --secondary #00A86B --radius rounded --platform sitecore/brand-theme-scaffolder tokens/ --brand acme --primary #E63946 --font-family 'Inter, sans-serif' --platform headless
Phase 1 — Base Token Discovery
Read the existing base semantic token set:
ls <base-tokens-path>/semantic/
Parse each semantic token file to build a complete inventory of:
- All semantic color tokens and their current values
- Typography semantic tokens (font-family assignments)
- Border radius semantic tokens
- All
action.*tokens (these are the primary brand identifiers)
If no tokens/base/semantic/ or tokens/semantic/ directory exists, run design-token-generator
first to establish the base architecture.
Phase 2 — Brand Color Generation
From the --primary hex value, generate the brand's full primitive color scale:
Scale generation using oklch (perceptually uniform):
- Convert
--primaryhex to oklch:L C H - Keep the hue
Hconstant across all stops. - Generate 10 stops (50–900) by interpolating lightness:
- 50: L=97%
- 100: L=94%
- 200: L=87%
- 300: L=76%
- 400: L=64%
- 500: L = provided primary's lightness (anchor)
- 600: L=42%
- 700: L=34%
- 800: L=25%
- 900: L=17%
- Verify WCAG AA contrast for
action-primarytext-on-background combinations. Adjust the semantic pointer (action-primary→brand.600instead ofbrand.500) if the 500 stop fails 4.5:1 contrast against white.
If --secondary is provided, generate a second 10-stop scale using the same method.
Phase 3 — Generate Brand Primitive File
Create tokens/core/colors-<brand-slug>.json with the generated scale:
{
"core": {
"color": {
"<brand-slug>": {
"50": { "$value": "<oklch or hex>", "$type": "color" },
"100": { "$value": "...", "$type": "color" },
"200": { "$value": "...", "$type": "color" },
"300": { "$value": "...", "$type": "color" },
"400": { "$value": "...", "$type": "color" },
"500": { "$value": "<--primary hex>", "$type": "color" },
"600": { "$value": "...", "$type": "color" },
"700": { "$value": "...", "$type": "color" },
"800": { "$value": "...", "$type": "color" },
"900": { "$value": "...", "$type": "color" }
}
}
}
}
Include $description on each token: "Brand Nova — blue 500 — primary action color".
Phase 4 — Generate Brand Semantic Override File
Create tokens/brands/<brand-slug>/semantic/color.json.
This file only declares tokens that differ from the base. Every token must alias the
brand's own primitive scale, not hard-coded values.
Minimum required overrides (always):
{
"semantic": {
"color": {
"action": {
"primary": { "$value": "{core.color.<brand-slug>.500}", "$type": "color" },
"primary-hover": { "$value": "{core.color.<brand-slug>.600}", "$type": "color" },
"primary-active": { "$value": "{core.color.<brand-slug>.700}", "$type": "color" },
"primary-disabled": { "$value": "{core.color.<brand-slug>.200}", "$type": "color" },
"primary-focus-ring": { "$value": "{core.color.<brand-slug>.400}", "$type": "color" }
}
}
}
}
If --secondary was provided, also override:
"action": {
"secondary": { "$value": "{core.color.<brand-slug>-secondary.500}", "$type": "color" },
"secondary-hover": { "$value": "{core.color.<brand-slug>-secondary.600}", "$type": "color" }
}
Phase 5 — Typography Override (if --font-family provided)
Create tokens/brands/<brand-slug>/semantic/typography.json:
{
"semantic": {
"typography": {
"font-family-primary": {
"$value": "<provided stack>",
"$type": "fontFamily"
}
}
}
}
Note in the output: if the brand uses a Google Font or custom web font, a @font-face or
<link> to the font CDN must be added to the HTML <head> separately — tokens cannot load fonts.
Phase 6 — Border Radius Override (if --radius provided)
Map --radius flag to semantic overrides:
| Flag value | border-radius-sm |
border-radius-md |
border-radius-lg |
|---|---|---|---|
sharp |
{core.border-radius.none} |
{core.border-radius.xs} |
{core.border-radius.sm} |
soft |
{core.border-radius.sm} |
{core.border-radius.md} |
{core.border-radius.lg} |
rounded |
{core.border-radius.lg} |
{core.border-radius.xl} |
{core.border-radius.2xl} |
pill |
{core.border-radius.full} |
{core.border-radius.full} |
{core.border-radius.full} |
Create tokens/brands/<brand-slug>/semantic/border-radius.json with these overrides.
Phase 7 — Token Diff Report
After writing all brand files, generate a diff report showing exactly what changes from base:
## Brand: <brand-slug> — Token Diff from Base
### Changed (semantic overrides)
| Token path | Base value | Brand value |
|-----------|-----------|-------------|
| semantic.color.action.primary | #3b82f6 | #0F4C81 |
| ... | | |
### Unchanged (inherited from base)
<n> tokens inherited without override
### New tokens (brand-only additions)
<list any tokens not in base>
### Contrast check
| Pairing | Ratio | WCAG AA (4.5:1) | WCAG AA Large (3:1) |
|---------|-------|-----------------|---------------------|
| action.primary on white | x.xx:1 | ✅/❌ | ✅/❌ |
| text.default on surface.page | x.xx:1 | ✅/❌ | ✅/❌ |
Phase 8 — Theme Switcher Integration
Generate a theme-switcher.<js|ts> snippet in dist/brands/<brand-slug>/:
// theme-switcher.ts
type Brand = '<brand-a>' | '<brand-b>' | '<brand-slug>';
export function setBrand(brand: Brand): void {
document.documentElement.dataset.brand = brand;
localStorage.setItem('preferred-brand', brand);
}
export function restoreBrand(): void {
const saved = localStorage.getItem('preferred-brand') as Brand | null;
if (saved) setBrand(saved);
}
Platform-specific integration notes:
- Sitecore: Set
data-brandon<body>in the SXA main layout Razor file from a site-level configuration item field. - Optimizely: Inject
data-brandvia a custom IContentRenderer or via a site-specific global settings content type. - Umbraco: Render
data-brandfrom the site root node's property in_Layout.cshtml. - Headless: Set
data-brandin_app.tsx(Next.js) or the root layout component (Astro) from an environment variable or site config.
Phase 9 — Style Dictionary Config Update
Print instructions to add the new brand to the existing Style Dictionary config (or generate
a new build-tokens.js entry if one exists). See token-transform-pipeline skill for full
pipeline setup.
Reference files
reference.md— Contrast ratio calculation method, oklch scale generation algorithm, per-platformdata-brandinjection patterns
Technical details
- Plugin
- design-system-manager
- Author
- Dakota Smith
- Skill name
brand-theme-scaffolder- Maturity
- experimental
- Security review
- No security review
- Owners
-
- design
- engineering
- Argument hint
<base-tokens-path> --brand <brand-slug> --primary <hex> [--secondary <hex>] [--font-family '<family stack>'] [--radius sharp|soft|rounded|pill] [--platform sitecore|optimizely|umbraco|headless]- Allowed tools
-
ReadWriteEditGlobBash(node *)Bash(ls *)
- Source
plugins/design-system-manager/skills/brand-theme-scaffolder/SKILL.md
Edit this skill on GitHub — opens GitHub's web editor. No git knowledge needed.