home / skills / openclaw / skills / design-system-patterns

design-system-patterns skill

/skills/wpank/design-system-patterns

This skill helps you implement scalable design systems by guiding token hierarchies, theming, and multi-brand patterns across platforms.

npx playbooks add skill openclaw/skills --skill design-system-patterns

Review the files below or copy the command above to add this skill to your agents.

Files (6)
SKILL.md
9.4 KB
---
name: design-system-patterns
model: standard
description: 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.
---

# Design System Patterns

Foundational architecture for scalable design systems: token hierarchies, theming infrastructure, token pipelines, and governance 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.

```css
/* 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.

```tsx
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](references/theming-architecture.md).

### Preventing FOUC in SSR (Next.js)

Inline script in `<head>` runs before paint:

```tsx
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:

```css
[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:

```javascript
// 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](references/design-tokens.md) for token category definitions, custom transforms, and platform-specific output examples.

---

## Pattern 5: Accessibility Tokens

```css
@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: **Propose** → **Review** (design + eng) → **Test** (all platforms/themes) → **Deprecate** (with migration path) → **Remove** (after deprecation period).

```json
{
  "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

- [design-system-components](../design-system-components/) — CVA variant patterns and Surface primitives
- [distinctive-design-systems](../distinctive-design-systems/) — Aesthetic documentation and visual identity
- [theme-factory](../theme-factory/) — Pre-built theme palettes for artifacts

---

## References

- [references/design-tokens.md](references/design-tokens.md) — Complete token category definitions
- [references/theming-architecture.md](references/theming-architecture.md) — Detailed theming implementation
- [references/component-architecture.md](references/component-architecture.md) — Compound, polymorphic, and headless patterns

Overview

This skill provides foundational design system architecture focused on token hierarchies, theming infrastructure, token pipelines, and governance. It captures practical patterns for creating semantic tokens, enabling theme switching, preventing FOUC in SSR, and supporting multi-brand theming. The guidance is implementation-oriented and platform-agnostic so teams can apply it across web and mobile.

How this skill works

I describe a three-layer token hierarchy (primitive → semantic → component) so values are authored once and mapped to usage. I show how to implement theme providers that resolve user, system, and persisted preferences, including an inline SSR script to avoid FOUC. I also cover Style Dictionary pipelines for multi-platform token generation, brand layering, accessibility token rules, naming conventions, and a governance workflow for safe token changes.

When to use it

  • Designing or refactoring a token architecture for scalability and theming
  • Implementing light/dark/system theme switching in React or SSR apps
  • Setting up Style Dictionary or Figma-to-code token pipelines
  • Building multi-brand or white-label theming systems
  • Creating token naming conventions and a governance process

Best practices

  • Name tokens by purpose (semantic names), not visual descriptions
  • Maintain a strict hierarchy: primitives → semantic → component
  • Treat token changes as API changes and version with semver
  • Automate pipelines and CI/CD for Figma-to-code synchronization
  • Provide deprecation and migration paths with clear replacements
  • Validate color contrast and reduced-motion accessibility automatically

Example use cases

  • Map a shared JSON token source to CSS variables, iOS Swift, and Android XML via Style Dictionary
  • Add a ThemeProvider that stores preference, detects system theme, and applies a DOM attribute or class
  • Layer brand tokens with data-brand attributes to support multiple white-label products
  • Inject an inline theme script in SSR layouts to prevent flash of unstyled content
  • Create accessibility variants using media queries for reduced motion and high contrast

FAQ

What is the most important token layer?

Semantic tokens are most important because they express intent and enable theme switching without changing component code.

How do I prevent FOUC when server rendering?

Run a small inline script in the document head that reads persisted theme, checks system preference, and sets a theme attribute and color-scheme before paint.