Claude Code for Design Systems: Tokens, Component Libraries, and Documentation — Claude Skills 360 Blog
Blog / Development / Claude Code for Design Systems: Tokens, Component Libraries, and Documentation
Development

Claude Code for Design Systems: Tokens, Component Libraries, and Documentation

Published: July 12, 2026
Read time: 9 min read
By: Claude Skills 360

A design system is only as good as its adoption — and adoption fails when the system is too rigid to accommodate real product needs or too loosely defined to enforce consistency. Claude Code builds design systems with the right abstraction level: tokens for the fundamental decisions (color, spacing, typography), components with constrained but flexible APIs, and documentation that shows actual usage rather than contrived examples.

This guide covers building a design system with Claude Code: design tokens, component architecture, theming, and documentation.

Design Tokens

Set up a token system that works across web, iOS, and Android.
Our brand has 3 color schemes: light, dark, and high-contrast.

Token Definition with Style Dictionary

// tokens/colors.json
{
  "color": {
    "brand": {
      "50": { "value": "#eff6ff" },
      "100": { "value": "#dbeafe" },
      "500": { "value": "#3b82f6", "description": "Primary brand color" },
      "600": { "value": "#2563eb", "description": "Interactive hover state" },
      "700": { "value": "#1d4ed8" },
      "900": { "value": "#1e3a8a" }
    },
    "neutral": {
      "0": { "value": "#ffffff" },
      "50": { "value": "#f8fafc" },
      "200": { "value": "#e2e8f0" },
      "500": { "value": "#64748b" },
      "900": { "value": "#0f172a" },
      "1000": { "value": "#000000" }
    },
    "semantic": {
      "background": {
        "default": { "value": "{color.neutral.50}" },
        "surface": { "value": "{color.neutral.0}" },
        "raised": { "value": "{color.neutral.0}" }
      },
      "text": {
        "primary": { "value": "{color.neutral.900}" },
        "secondary": { "value": "{color.neutral.500}" },
        "disabled": { "value": "{color.neutral.200}" },
        "action": { "value": "{color.brand.600}" }
      },
      "border": {
        "default": { "value": "{color.neutral.200}" },
        "focus": { "value": "{color.brand.500}" }
      }
    }
  }
}
// tokens/spacing.json
{
  "spacing": {
    "0": { "value": "0px" },
    "1": { "value": "4px" },
    "2": { "value": "8px" },
    "3": { "value": "12px" },
    "4": { "value": "16px" },
    "5": { "value": "20px" },
    "6": { "value": "24px" },
    "8": { "value": "32px" },
    "10": { "value": "40px" },
    "12": { "value": "48px" },
    "16": { "value": "64px" }
  }
}

Style Dictionary Build

// build-tokens.js
const StyleDictionary = require('style-dictionary');

// Platform: CSS Custom Properties
StyleDictionary.registerTransformGroup({
  name: 'css/design-tokens',
  transforms: ['attribute/cti', 'name/cti/kebab', 'color/hex', 'size/px'],
});

module.exports = {
  source: ['tokens/**/*.json'],
  platforms: {
    css: {
      transformGroup: 'css/design-tokens',
      buildPath: 'src/tokens/',
      files: [{
        destination: 'tokens.css',
        format: 'css/variables',
        selector: ':root',
      }],
    },
    
    ios: {
      transformGroup: 'ios/swift',
      buildPath: 'ios/Tokens/',
      files: [{
        destination: 'DesignTokens.swift',
        format: 'ios-swift/class.swift',
        className: 'DesignTokens',
      }],
    },
    
    android: {
      transformGroup: 'android',
      buildPath: 'android/src/main/res/values/',
      files: [{
        destination: 'design_tokens.xml',
        format: 'android/resources',
      }],
    },
    
    ts: {
      transformGroup: 'js',
      buildPath: 'src/tokens/',
      files: [{
        format: 'javascript/es6',
        destination: 'tokens.ts',
      }],
    },
  },
};

Component API Design

Design a Button component that's flexible but constrained.
Variants: primary, secondary, ghost, danger.
Sizes: sm, md, lg.
Must support icon-only mode with accessible label.
// src/components/Button/Button.tsx
import { forwardRef, type ButtonHTMLAttributes, type ReactNode } from 'react';
import styles from './Button.module.css';

type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'danger';
type ButtonSize = 'sm' | 'md' | 'lg';

interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: ButtonVariant;
  size?: ButtonSize;
  loading?: boolean;
  leadingIcon?: ReactNode;
  trailingIcon?: ReactNode;
  // For icon-only buttons — required when no text children
  'aria-label'?: string;
}

export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(
  {
    variant = 'primary',
    size = 'md',
    loading = false,
    leadingIcon,
    trailingIcon,
    children,
    disabled,
    className,
    ...props
  },
  ref,
) {
  const isIconOnly = !children;
  
  // Enforce accessible label for icon-only buttons
  if (isIconOnly && !props['aria-label']) {
    console.warn('Button: icon-only buttons require an aria-label for accessibility');
  }
  
  return (
    <button
      ref={ref}
      className={[
        styles.button,
        styles[variant],
        styles[size],
        loading && styles.loading,
        isIconOnly && styles.iconOnly,
        className,
      ].filter(Boolean).join(' ')}
      disabled={disabled || loading}
      aria-busy={loading}
      {...props}
    >
      {loading && <Spinner className={styles.spinner} aria-hidden="true" />}
      {!loading && leadingIcon && (
        <span className={styles.leadingIcon} aria-hidden="true">{leadingIcon}</span>
      )}
      {children && <span className={styles.label}>{children}</span>}
      {trailingIcon && (
        <span className={styles.trailingIcon} aria-hidden="true">{trailingIcon}</span>
      )}
    </button>
  );
});
/* Button.module.css — using design tokens */
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--spacing-2);
  border: 1px solid transparent;
  border-radius: var(--border-radius-md, 6px);
  font-weight: var(--font-weight-medium, 500);
  transition: background 150ms ease, border-color 150ms ease, box-shadow 150ms ease;
  cursor: pointer;
  
  /* Focus visible — keyboard nav */
  &:focus-visible {
    outline: 2px solid var(--color-semantic-border-focus);
    outline-offset: 2px;
  }
  
  &:disabled {
    cursor: not-allowed;
    opacity: 0.4;
  }
}

/* Variants */
.primary {
  background: var(--color-brand-600);
  color: white;
  
  &:hover:not(:disabled) { background: var(--color-brand-700); }
  &:active:not(:disabled) { background: var(--color-brand-800); }
}

.secondary {
  background: transparent;
  border-color: var(--color-semantic-border-default);
  color: var(--color-semantic-text-primary);
  
  &:hover:not(:disabled) { background: var(--color-neutral-50); }
}

.ghost {
  background: transparent;
  color: var(--color-semantic-text-action);
  
  &:hover:not(:disabled) { background: var(--color-brand-50); }
}

.danger {
  background: var(--color-red-600, #dc2626);
  color: white;
  
  &:hover:not(:disabled) { background: var(--color-red-700, #b91c1c); }
}

/* Sizes */
.sm { padding: var(--spacing-1) var(--spacing-3); font-size: 0.875rem; height: 32px; }
.md { padding: var(--spacing-2) var(--spacing-4); font-size: 0.9375rem; height: 40px; }
.lg { padding: var(--spacing-3) var(--spacing-6); font-size: 1rem; height: 48px; }

/* Icon only — square */
.iconOnly {
  &.sm { width: 32px; padding: 0; }
  &.md { width: 40px; padding: 0; }
  &.lg { width: 48px; padding: 0; }
}

Theming

Support light and dark mode, plus high-contrast for accessibility.
Theme should switch without flash.
// src/lib/theme.tsx
import { createContext, useContext, useEffect, useState } from 'react';

type Theme = 'light' | 'dark' | 'high-contrast' | 'system';

export function ThemeProvider({ children }: { children: React.ReactNode }) {
  const [theme, setThemeState] = useState<Theme>(() => {
    if (typeof window === 'undefined') return 'system';
    return (localStorage.getItem('theme') as Theme) ?? 'system';
  });
  
  const setTheme = (newTheme: Theme) => {
    localStorage.setItem('theme', newTheme);
    setThemeState(newTheme);
  };
  
  useEffect(() => {
    const root = document.documentElement;
    const resolvedTheme = theme === 'system'
      ? window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
      : theme;
    
    root.setAttribute('data-theme', resolvedTheme);
    
    // For high-contrast: also check OS preference
    if (window.matchMedia('(forced-colors: active)').matches) {
      root.setAttribute('data-theme', 'high-contrast');
    }
  }, [theme]);
  
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}
/* tokens.css — themed with data-theme attribute */
:root, [data-theme="light"] {
  --color-background: var(--color-neutral-50);
  --color-surface: var(--color-neutral-0);
  --color-text-primary: var(--color-neutral-900);
  --color-text-secondary: var(--color-neutral-500);
  --color-border: var(--color-neutral-200);
}

[data-theme="dark"] {
  --color-background: var(--color-neutral-900);
  --color-surface: var(--color-neutral-800);
  --color-text-primary: var(--color-neutral-50);
  --color-text-secondary: var(--color-neutral-400);
  --color-border: var(--color-neutral-700);
}

[data-theme="high-contrast"] {
  --color-background: #000000;
  --color-surface: #000000;
  --color-text-primary: #ffffff;
  --color-text-secondary: #ffffff;
  --color-border: #ffffff;
  --color-brand-600: #4db3ff;  /* WCAG AAA on black */
}

For testing design system components with Storybook including visual regression, see the Storybook guide. For accessibility requirements that design systems must meet, see the accessibility guide. The Claude Skills 360 bundle includes design system skill sets for token architecture and component APIs. Start with the free tier to generate a token system for your brand.

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free