Ai design-system-patterns

Foundational design system architecture — token hierarchies, theming infrastructure, token pipelines, and governance. Use when creating design tokens, implementing theme switching, setting up Style Dictionary, or establishing multi-brand theming. Triggers on design tokens, theme provider, Style Dictionary, token pipeline, multi-brand theming, CSS custom properties architecture.

install
source · Clone the upstream repo
git clone https://github.com/wpank/ai
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/wpank/ai "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/design-systems/design-system-patterns" ~/.claude/skills/wpank-ai-design-system-patterns && rm -rf "$T"
manifest: skills/design-systems/design-system-patterns/SKILL.md
source content

Design System Patterns

Foundational architecture for scalable design systems: token hierarchies, theming infrastructure, token pipelines, and governance patterns.

Installation

OpenClaw / Moltbot / Clawbot

npx clawhub@latest install design-system-patterns

When to Use

  • Defining token architecture (primitive → semantic → component layers)
  • Implementing light/dark/system theme switching with React
  • Setting up Style Dictionary or Figma-to-code token pipelines
  • Building multi-brand theming systems
  • Establishing token naming conventions and governance
  • Preventing flash of unstyled content (FOUC) in SSR

Pattern 1: Token Hierarchy

Three-layer token architecture separates raw values from meaning from usage.

/* Layer 1: Primitive tokens — raw values, never used directly in components */
:root {
  --color-blue-500: #3b82f6;
  --color-blue-600: #2563eb;
  --color-gray-50: #fafafa;
  --color-gray-900: #171717;

  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-4: 1rem;

  --font-size-sm: 0.875rem;
  --font-size-base: 1rem;
  --radius-sm: 0.25rem;
  --radius-md: 0.5rem;
}

/* Layer 2: Semantic tokens — contextual meaning, theme-aware */
:root {
  --text-primary: var(--color-gray-900);
  --text-secondary: var(--color-gray-600);
  --surface-default: white;
  --surface-elevated: var(--color-gray-50);
  --border-default: var(--color-gray-200);
  --interactive-primary: var(--color-blue-500);
  --interactive-primary-hover: var(--color-blue-600);
}

/* Layer 3: Component tokens — specific usage, optional */
:root {
  --button-bg: var(--interactive-primary);
  --button-bg-hover: var(--interactive-primary-hover);
  --button-text: white;
  --button-radius: var(--radius-md);
  --button-padding-x: var(--space-4);
  --button-padding-y: var(--space-2);
}

Semantic tokens are the most important layer — they enable theming. Component tokens are optional and useful for complex component libraries.


Pattern 2: Theme Switching with React

Key capabilities:

theme
(user selection),
resolvedTheme
(actual light/dark),
setTheme
, system preference detection, localStorage persistence, DOM attribute application.

type Theme = "light" | "dark" | "system";

export function ThemeProvider({ children, defaultTheme = "system", storageKey = "theme",
  attribute = "data-theme" }: { children: React.ReactNode; defaultTheme?: Theme;
  storageKey?: string; attribute?: "class" | "data-theme" }) {
  const [theme, setThemeState] = useState<Theme>(() =>
    typeof window === "undefined" ? defaultTheme
      : (localStorage.getItem(storageKey) as Theme) || defaultTheme);
  const [resolvedTheme, setResolvedTheme] = useState<"light" | "dark">("light");

  const getSystem = useCallback(() =>
    matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" as const, []);

  const apply = useCallback((r: "light" | "dark") => {
    const root = document.documentElement;
    attribute === "class"
      ? (root.classList.remove("light", "dark"), root.classList.add(r))
      : root.setAttribute(attribute, r);
    root.style.colorScheme = r;
    setResolvedTheme(r);
  }, [attribute]);

  useEffect(() => { apply(theme === "system" ? getSystem() : theme); }, [theme, apply, getSystem]);

  useEffect(() => {  // Listen for system preference changes
    if (theme !== "system") return;
    const mq = matchMedia("(prefers-color-scheme: dark)");
    const handler = () => apply(getSystem());
    mq.addEventListener("change", handler);
    return () => mq.removeEventListener("change", handler);
  }, [theme, apply, getSystem]);

  const setTheme = useCallback((t: Theme) => {
    localStorage.setItem(storageKey, t); setThemeState(t);
  }, [storageKey]);

  return <ThemeContext.Provider value={{ theme, resolvedTheme, setTheme }}>
    {children}
  </ThemeContext.Provider>;
}

Full implementation with

toggleTheme
,
disableTransitionOnChange
, and testing patterns in references/theming-architecture.md.

Preventing FOUC in SSR (Next.js)

Inline script in

<head>
runs before paint:

const themeScript = `(function(){
  var t=localStorage.getItem('theme')||'system';
  var d=t==='dark'||(t==='system'&&matchMedia('(prefers-color-scheme:dark)').matches);
  document.documentElement.setAttribute('data-theme',d?'dark':'light');
  document.documentElement.style.colorScheme=d?'dark':'light';
})()`;

// In layout.tsx
<html lang="en" suppressHydrationWarning>
  <head>
    <script dangerouslySetInnerHTML={{ __html: themeScript }} />
  </head>
  <body><ThemeProvider>{children}</ThemeProvider></body>
</html>

Pattern 3: Multi-Brand Theming

Layer brand tokens on top of semantic tokens for white-label products:

[data-brand="corporate"] {
  --brand-primary: #0066cc;
  --brand-primary-hover: #0052a3;
  --brand-font-heading: "Helvetica Neue", sans-serif;
  --brand-radius: 0.25rem;
}

[data-brand="startup"] {
  --brand-primary: #7c3aed;
  --brand-primary-hover: #6d28d9;
  --brand-font-heading: "Poppins", sans-serif;
  --brand-radius: 1rem;
}

/* Map brand tokens into semantic tokens */
:root {
  --interactive-primary: var(--brand-primary);
  --interactive-primary-hover: var(--brand-primary-hover);
}

Pattern 4: Style Dictionary Pipeline

Multi-platform token generation from a single JSON source:

// style-dictionary.config.js — generates CSS, iOS Swift, and Android XML
module.exports = {
  source: ["tokens/**/*.json"],
  platforms: {
    css: {
      transformGroup: "css", buildPath: "dist/css/",
      files: [{ destination: "variables.css", format: "css/variables",
        options: { outputReferences: true } }],
    },
    ios: {
      transformGroup: "ios-swift", buildPath: "dist/ios/",
      files: [{ destination: "DesignTokens.swift", format: "ios-swift/class.swift",
        className: "DesignTokens" }],
    },
    android: {
      transformGroup: "android", buildPath: "dist/android/",
      files: [{ destination: "colors.xml", format: "android/colors",
        filter: { attributes: { category: "color" } } }],
    },
  },
};

See references/design-tokens.md for token category definitions, custom transforms, and platform-specific output examples.


Pattern 5: Accessibility Tokens

@media (prefers-reduced-motion: reduce) {
  :root {
    --duration-fast: 0ms;
    --duration-normal: 0ms;
    --duration-slow: 0ms;
  }
}

@media (prefers-contrast: high) {
  :root {
    --text-primary: #000000;
    --surface-default: #ffffff;
    --border-default: #000000;
    --interactive-primary: #0000ee;
  }
}

@media (forced-colors: active) {
  .button { border: 2px solid currentColor; }
  .card { border: 1px solid CanvasText; }
}

Token Naming Conventions

Format:

[category]-[property]-[variant]-[state]
(e.g.
color-border-input-focus
)

  1. kebab-case
    text-primary
    not
    textPrimary
  2. Semantic names
    danger
    not
    red
  3. State suffixes
    -hover
    ,
    -focus
    ,
    -active
    ,
    -disabled
  4. Scale indicators
    spacing-4
    ,
    font-size-lg

Token Governance

Change management: ProposeReview (design + eng) → Test (all platforms/themes) → Deprecate (with migration path) → Remove (after deprecation period).

{
  "color.primary": {
    "value": "{color.primitive.blue.500}",
    "deprecated": true,
    "deprecatedMessage": "Use semantic.accent.default instead",
    "replacedBy": "semantic.accent.default"
  }
}

Best Practices

  1. Name tokens by purpose — semantic names, not visual descriptions
  2. Maintain the hierarchy — primitives → semantic → component
  3. Version tokens — treat token changes as API changes with semver
  4. Test all theme combinations — every theme must work with every component
  5. Automate the pipeline — CI/CD for Figma-to-code synchronization
  6. Provide migration paths — deprecate gradually with clear alternatives
  7. Validate contrast — automated WCAG AA/AAA checks on token pairs

Common Pitfalls

  • Token sprawl — too many tokens without clear hierarchy
  • Inconsistent naming — mixing camelCase and kebab-case
  • Hardcoded values — using raw hex/rem instead of token references
  • Circular references — tokens referencing each other in loops
  • Platform gaps — tokens defined for web but missing for mobile
  • Missing dark mode — semantic tokens that don't adapt to themes

Related Skills


References