moai-design-systems
About
This skill helps developers implement production-ready design systems using W3C DTCG 2025.10 token architecture and WCAG 2.2 accessibility standards. It provides guidance for creating accessible component libraries, automating Figma-to-code workflows, and ensuring color contrast compliance. Use it when setting up design token architecture or building maintainable, accessible UI systems.
Documentation
Design Systems Development Skill
Purpose: Guide implementation of production-ready design systems using W3C DTCG 2025.10 token standards, WCAG 2.2 accessibility compliance, and Figma MCP automation workflows.
When to use this Skill:
- Setting up design token architecture for multi-platform projects
- Implementing accessible component libraries (WCAG 2.2 AA/AAA)
- Automating design-to-code workflows with Figma MCP
- Building maintainable design systems with Storybook
- Ensuring color contrast compliance and semantic token naming
Latest Standards (as of November 2025):
- DTCG Specification: 2025.10 (first stable version)
- WCAG Guidelines: 2.2 (AA: 4.5:1 text, AAA: 7:1 text)
- Figma MCP: Desktop + Remote server support
- Style Dictionary: 4.0 (DTCG-compatible)
- Storybook: 8.x (with Docs addon)
Progressive Disclosure Structure
Level 1: Quick Start Overview (Read This First)
Design System Foundation - Three Pillars:
-
Design Tokens (Single Source of Truth)
- Color, typography, spacing, borders, shadows
- Semantic naming:
color.primary.500,spacing.md,font.heading.lg - Multi-theme support (light/dark modes)
- Format: W3C DTCG 2025.10 JSON or Style Dictionary 4.0
-
Component Library (Atomic Design Pattern)
- Atoms → Molecules → Organisms → Templates → Pages
- Props API for reusability and composition
- Variant states: default, hover, active, disabled, error, loading
- Documentation: Storybook with auto-generated props/usage
-
Accessibility Standards (WCAG 2.2 Compliance)
- Color contrast: 4.5:1 (AA), 7:1 (AAA) for text
- Keyboard navigation: Tab order, focus management
- Screen readers: ARIA roles, labels, live regions
- Motion:
prefers-reduced-motionsupport
Tool Ecosystem Quick Reference:
| Tool | Version | Purpose | Official Link |
|---|---|---|---|
| W3C DTCG | 2025.10 | Design token specification | https://designtokens.org |
| Style Dictionary | 4.0+ | Token transformation engine | https://styledictionary.com |
| Figma MCP | Latest | Design-to-code automation | https://help.figma.com/hc/en-us/articles/32132100833559 |
| Storybook | 8.x | Component documentation | https://storybook.js.org |
| axe DevTools | Latest | Accessibility testing | https://www.deque.com/axe/devtools/ |
| Chromatic | Latest | Visual regression testing | https://chromatic.com |
Decision Points Checklist:
- Choose token format: DTCG 2025.10 or Style Dictionary 4.0 (both compatible)
- Target WCAG level: AA (4.5:1) or AAA (7:1) contrast
- Component pattern: Atomic Design or alternative structure
- Documentation tool: Storybook, zeroheight, or custom
- Figma integration: MCP server (desktop vs remote)
- Testing strategy: Visual regression + accessibility + interaction
Level 2: Implementation Patterns (How to Build)
Pattern 1: Design Token Architecture (DTCG 2025.10)
Token Structure - Semantic Naming Convention:
{
"$schema": "https://tr.designtokens.org/format/",
"$tokens": {
"color": {
"$type": "color",
"primary": {
"50": { "$value": "#eff6ff" },
"100": { "$value": "#dbeafe" },
"500": { "$value": "#3b82f6" },
"900": { "$value": "#1e3a8a" }
},
"semantic": {
"text": {
"primary": { "$value": "{color.gray.900}" },
"secondary": { "$value": "{color.gray.600}" },
"disabled": { "$value": "{color.gray.400}" }
},
"background": {
"default": { "$value": "{color.white}" },
"elevated": { "$value": "{color.gray.50}" }
}
}
},
"spacing": {
"$type": "dimension",
"xs": { "$value": "0.25rem" },
"sm": { "$value": "0.5rem" },
"md": { "$value": "1rem" },
"lg": { "$value": "1.5rem" },
"xl": { "$value": "2rem" }
},
"typography": {
"$type": "fontFamily",
"sans": { "$value": ["Inter", "system-ui", "sans-serif"] },
"mono": { "$value": ["JetBrains Mono", "monospace"] }
},
"fontSize": {
"$type": "dimension",
"sm": { "$value": "0.875rem" },
"base": { "$value": "1rem" },
"lg": { "$value": "1.125rem" },
"xl": { "$value": "1.25rem" }
}
}
}
Multi-Theme Support (Light/Dark Mode):
{
"color": {
"semantic": {
"background": {
"$type": "color",
"default": {
"$value": "{color.white}",
"$extensions": {
"mode": {
"dark": "{color.gray.900}"
}
}
}
}
}
}
}
Style Dictionary Configuration (v4.0+):
// style-dictionary.config.js
export default {
source: ['tokens/**/*.json'],
platforms: {
css: {
transformGroup: 'css',
buildPath: 'build/css/',
files: [{
destination: 'variables.css',
format: 'css/variables'
}]
},
js: {
transformGroup: 'js',
buildPath: 'build/js/',
files: [{
destination: 'tokens.js',
format: 'javascript/es6'
}]
}
}
};
Pattern 2: Atomic Design Component Structure
Folder Hierarchy:
src/design-system/
├── tokens/ # Design tokens (DTCG format)
│ ├── color.json
│ ├── typography.json
│ └── spacing.json
├── components/
│ ├── atoms/ # Basic building blocks
│ │ ├── Button/
│ │ │ ├── Button.tsx
│ │ │ ├── Button.stories.tsx
│ │ │ ├── Button.test.tsx
│ │ │ └── index.ts
│ │ ├── Input/
│ │ └── Icon/
│ ├── molecules/ # Simple combinations
│ │ ├── FormField/
│ │ ├── SearchBar/
│ │ └── Card/
│ ├── organisms/ # Complex sections
│ │ ├── Header/
│ │ ├── Footer/
│ │ └── DataTable/
│ └── templates/ # Page layouts
│ ├── DashboardLayout/
│ └── AuthLayout/
└── styles/
├── global.css
└── theme.css
Component Props API (Reusability Pattern):
// atoms/Button/Button.tsx
import { forwardRef } from 'react';
import { cva, type VariantProps } from 'class-variance-authority';
const buttonVariants = cva(
'inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
primary: 'bg-primary-500 text-white hover:bg-primary-600',
secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300',
outline: 'border border-gray-300 bg-transparent hover:bg-gray-100',
ghost: 'hover:bg-gray-100',
danger: 'bg-red-500 text-white hover:bg-red-600'
},
size: {
sm: 'h-8 px-3 text-sm',
md: 'h-10 px-4 text-base',
lg: 'h-12 px-6 text-lg'
}
},
defaultVariants: {
variant: 'primary',
size: 'md'
}
}
);
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
isLoading?: boolean;
}
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, isLoading, children, disabled, ...props }, ref) => {
return (
<button
ref={ref}
className={buttonVariants({ variant, size, className })}
disabled={disabled || isLoading}
aria-busy={isLoading}
{...props}
>
{isLoading ? <Spinner /> : children}
</button>
);
}
);
Button.displayName = 'Button';
Pattern 3: WCAG 2.2 Accessibility Implementation
Color Contrast Validation (Automated Check):
// utils/a11y/contrast.ts
/**
* Calculate relative luminance for WCAG compliance
* @see https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html
*/
function getLuminance(rgb: [number, number, number]): number {
const [r, g, b] = rgb.map(val => {
const sRGB = val / 255;
return sRGB <= 0.03928
? sRGB / 12.92
: Math.pow((sRGB + 0.055) / 1.055, 2.4);
});
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
/**
* Calculate contrast ratio between two colors
* WCAG AA: 4.5:1 (normal text), 3:1 (large text)
* WCAG AAA: 7:1 (normal text), 4.5:1 (large text)
*/
export function getContrastRatio(
foreground: string,
background: string
): number {
const fgLum = getLuminance(hexToRgb(foreground));
const bgLum = getLuminance(hexToRgb(background));
const lighter = Math.max(fgLum, bgLum);
const darker = Math.min(fgLum, bgLum);
return (lighter + 0.05) / (darker + 0.05);
}
/**
* Check if color pair meets WCAG AA/AAA requirements
*/
export function meetsWCAG(
foreground: string,
background: string,
level: 'AA' | 'AAA' = 'AA',
isLargeText: boolean = false
): boolean {
const ratio = getContrastRatio(foreground, background);
if (level === 'AAA') {
return isLargeText ? ratio >= 4.5 : ratio >= 7;
}
// AA level
return isLargeText ? ratio >= 3 : ratio >= 4.5;
}
Keyboard Navigation (Focus Management):
// hooks/useKeyboardNavigation.ts
import { useEffect, useRef } from 'react';
export function useKeyboardNavigation<T extends HTMLElement>(
options: {
onEscape?: () => void;
onEnter?: () => void;
trapFocus?: boolean;
} = {}
) {
const elementRef = useRef<T>(null);
useEffect(() => {
const element = elementRef.current;
if (!element) return;
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
options.onEscape?.();
} else if (e.key === 'Enter') {
options.onEnter?.();
} else if (e.key === 'Tab' && options.trapFocus) {
// Focus trap implementation
const focusableElements = element.querySelectorAll<HTMLElement>(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
if (e.shiftKey && document.activeElement === firstElement) {
lastElement.focus();
e.preventDefault();
} else if (!e.shiftKey && document.activeElement === lastElement) {
firstElement.focus();
e.preventDefault();
}
}
};
element.addEventListener('keydown', handleKeyDown);
return () => element.removeEventListener('keydown', handleKeyDown);
}, [options]);
return elementRef;
}
ARIA Labels & Roles (Screen Reader Support):
// components/atoms/Input/Input.tsx
export const Input = forwardRef<HTMLInputElement, InputProps>(
({ label, error, required, ...props }, ref) => {
const inputId = useId();
const errorId = `${inputId}-error`;
return (
<div className="form-field">
<label htmlFor={inputId} className="form-label">
{label}
{required && <span aria-label="required">*</span>}
</label>
<input
ref={ref}
id={inputId}
aria-invalid={!!error}
aria-describedby={error ? errorId : undefined}
aria-required={required}
{...props}
/>
{error && (
<span id={errorId} role="alert" className="error-message">
{error}
</span>
)}
</div>
);
}
);
Motion Accessibility (Reduced Motion Support):
/* styles/motion.css */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
/* Safe animations for reduced motion users */
.fade-enter {
opacity: 0;
}
.fade-enter-active {
opacity: 1;
transition: opacity 200ms ease-in;
}
@media (prefers-reduced-motion: reduce) {
.fade-enter-active {
transition: none;
opacity: 1;
}
}
Pattern 4: Figma MCP Integration Workflow
Setup (Desktop Server):
# Install Figma desktop app
# Enable MCP server in settings
# Desktop server runs at http://127.0.0.1:3845/mcp
MCP Configuration (Claude Desktop):
{
"mcpServers": {
"figma": {
"command": "figma-mcp",
"args": [],
"env": {
"FIGMA_ACCESS_TOKEN": "your-figma-token"
}
}
}
}
Extracting Design Tokens from Figma:
- Create Figma Variables (Color, Typography, Spacing)
- Use MCP Server to extract variables:
- Select frame in Figma
- Prompt: "Extract all design tokens from this frame"
- MCP returns DTCG-compatible JSON
- Transform to Code using Style Dictionary
Component Code Generation:
User Workflow:
1. Select component frame in Figma
2. Prompt: "Generate React component from this design"
3. MCP extracts:
- Component structure
- Applied design tokens
- Layout properties (flex, grid)
- Typography and spacing
4. Output: TypeScript React component with props
Automation Pattern (Link-based workflow):
// scripts/sync-figma-tokens.ts
import { exec } from 'child_process';
import { promisify } from 'util';
const execAsync = promisify(exec);
async function syncFigmaTokens(figmaFileUrl: string) {
// Use Figma MCP to extract tokens
const { stdout } = await execAsync(
`figma-mcp extract-tokens --url="${figmaFileUrl}"`
);
const tokens = JSON.parse(stdout);
// Write to tokens directory
await writeFile('tokens/color.json', JSON.stringify(tokens.color, null, 2));
await writeFile('tokens/spacing.json', JSON.stringify(tokens.spacing, null, 2));
// Run Style Dictionary build
await execAsync('npm run tokens:build');
console.log('✅ Design tokens synchronized from Figma');
}
Pattern 5: Storybook Documentation Setup
Installation & Configuration:
npx storybook@latest init
Storybook Configuration (v8.x):
// .storybook/main.ts
import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-a11y', // Accessibility testing
],
framework: {
name: '@storybook/react-vite',
options: {},
},
docs: {
autodocs: 'tag', // Auto-generate docs
},
};
export default config;
Component Story (with accessibility tests):
// components/atoms/Button/Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';
const meta: Meta<typeof Button> = {
title: 'Atoms/Button',
component: Button,
tags: ['autodocs'],
argTypes: {
variant: {
control: 'select',
options: ['primary', 'secondary', 'outline', 'ghost', 'danger'],
},
size: {
control: 'select',
options: ['sm', 'md', 'lg'],
},
},
};
export default meta;
type Story = StoryObj<typeof Button>;
export const Primary: Story = {
args: {
children: 'Primary Button',
variant: 'primary',
},
};
export const AllVariants: Story = {
render: () => (
<div className="flex gap-4">
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="danger">Danger</Button>
</div>
),
};
export const Disabled: Story = {
args: {
children: 'Disabled Button',
disabled: true,
},
};
export const Loading: Story = {
args: {
children: 'Loading Button',
isLoading: true,
},
};
Level 3: Advanced Topics (Deep Expertise)
Advanced 1: Type-Safe Design Tokens (TypeScript)
Generate TypeScript Types from DTCG Tokens:
// scripts/generate-token-types.ts
import { readFileSync, writeFileSync } from 'fs';
interface DTCGToken {
$value: string | number | string[];
$type?: string;
[key: string]: any;
}
function generateTypes(tokens: Record<string, any>, prefix = ''): string {
let types = '';
for (const [key, value] of Object.entries(tokens)) {
if (value.$value !== undefined) {
const tokenPath = `${prefix}${key}`.replace(/\./g, '-');
types += `export const ${tokenPath} = '${value.$value}';\n`;
} else {
types += generateTypes(value, `${prefix}${key}.`);
}
}
return types;
}
const colorTokens = JSON.parse(readFileSync('tokens/color.json', 'utf-8'));
const types = generateTypes(colorTokens.$tokens);
writeFileSync('src/tokens/colors.ts', types);
Advanced 2: Visual Regression Testing (Chromatic)
# Install Chromatic
npm install --save-dev chromatic
# Configure in package.json
{
"scripts": {
"chromatic": "chromatic --project-token=<your-token>"
}
}
CI/CD Integration:
# .github/workflows/chromatic.yml
name: Chromatic
on: [push]
jobs:
chromatic:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: npm ci
- name: Run Chromatic
uses: chromaui/action@v1
with:
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
Advanced 3: Accessibility Testing Automation
Jest + jest-axe Configuration:
// tests/setup.ts
import '@testing-library/jest-dom';
import { toHaveNoViolations } from 'jest-axe';
expect.extend(toHaveNoViolations);
Component Accessibility Tests:
// components/atoms/Button/Button.test.tsx
import { render } from '@testing-library/react';
import { axe } from 'jest-axe';
import { Button } from './Button';
describe('Button Accessibility', () => {
it('should have no accessibility violations', async () => {
const { container } = render(<Button>Click me</Button>);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
it('should have correct ARIA attributes when disabled', () => {
const { getByRole } = render(<Button disabled>Disabled</Button>);
const button = getByRole('button');
expect(button).toHaveAttribute('aria-disabled', 'true');
});
it('should indicate loading state to screen readers', () => {
const { getByRole } = render(<Button isLoading>Loading</Button>);
const button = getByRole('button');
expect(button).toHaveAttribute('aria-busy', 'true');
});
});
Best Practices Checklist
Design Token Architecture:
- Use semantic naming (
color.primary.500notcolor.blue) - Implement aliasing for themes (
{color.white}references) - Validate DTCG 2025.10 spec compliance
- Version tokens with semantic versioning
- Document token usage in Storybook
Component Development:
- Follow Atomic Design hierarchy (Atoms → Molecules → Organisms)
- Create variant-based props APIs (not separate components)
- Document all props with TypeScript types
- Write Storybook stories for all variants
- Test component accessibility with jest-axe
Accessibility:
- Verify 4.5:1 contrast for all text (WCAG AA)
- Implement keyboard navigation for all interactive elements
- Add ARIA labels to form fields and buttons
- Test with screen readers (NVDA, JAWS, VoiceOver)
- Support
prefers-reduced-motion
Testing:
- Visual regression tests for all components (Chromatic)
- Accessibility tests with axe-core
- Interaction tests with Testing Library
- Cross-browser compatibility checks
Figma Integration:
- Set up Figma MCP server (desktop or remote)
- Extract design tokens from Figma variables
- Automate component code generation
- Sync design changes with codebase
When NOT to Use This Skill
- Simple static sites: Overkill for projects without complex UI requirements
- Rapid prototyping: Design systems add overhead during early exploration
- Single-use projects: Token architecture benefits long-term maintenance
- Non-web platforms: This Skill focuses on web (React/Vue/TypeScript)
For these cases, consider:
- Plain CSS/Tailwind for static sites
- Component libraries (Material-UI, shadcn/ui) for rapid development
- Platform-specific design systems (iOS HIG, Material Design for Android)
Related Skills:
moai-domain-frontend- Frontend architecture patternsmoai-lang-typescript- TypeScript best practicesmoai-foundation-testing- Testing strategies
Official Resources:
- W3C DTCG: https://designtokens.org
- WCAG 2.2: https://www.w3.org/WAI/WCAG22/quickref/
- Figma MCP: https://help.figma.com/hc/en-us/articles/32132100833559
- Style Dictionary: https://styledictionary.com
- Storybook: https://storybook.js.org
Quick Install
/plugin add https://github.com/modu-ai/moai-adk/tree/main/moai-design-systemsCopy and paste this command in Claude Code to install this skill
GitHub 仓库
Related Skills
accessibility-testing
OtherThis skill performs automated accessibility testing for WCAG 2.2 compliance, including screen reader validation and inclusive design checks. Use it to ensure legal standards (ADA, Section 508) are met and to build applications accessible to disabled users. It applies POUR principles, tests keyboard navigation, validates with screen readers, and checks color contrast.
moai-design-systems
DesignThis Claude Skill helps developers implement production-ready design systems using W3C DTCG 2025.10 token standards and WCAG 2.2 accessibility compliance. It provides guidance for setting up design token architecture, building accessible component libraries, and automating design-to-code workflows with Figma MCP. Use it when establishing multi-platform design systems that require maintainable, accessible UI development.
design-system-starter
MetaThis Claude skill helps developers create or evolve design systems by providing design token structures, component architecture patterns, and accessibility guidelines. It is designed for establishing consistent, scalable, and accessible UI design across products. Use it when starting a new design system or refactoring an existing one.
web-cli-teleport
DesignThis skill helps developers choose between Claude Code Web and CLI interfaces based on task complexity and workflow needs. It enables seamless teleportation of sessions between environments to maintain context and optimize productivity. Use it for session management and to determine the best interface for coding tasks requiring different levels of iteration or back-and-forth interaction.
