Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Build platform-independent design systems and consistent component libraries. Triggers: design system, component library, design tokens, 디자인 시스템, 컴포넌트.
.claude/skills/popup-studio-ai-phase-5-design-system/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-13 | ✗→✓ | ▲ Improved | 117% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 192% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 202% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 121% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 160% | 0% |
> Build platform-independent design system
Build a reusable UI component library. Enable consistent design and fast development.
A design system is a collection of reusable components and clear standards that enables building consistent user experiences at scale.
| Problem | Design System Solution | |---------|----------------------| | Designer-developer mismatch | Single Source of Truth | | Duplicate component development | Reusable component library | | Inconsistent UI/UX | Unified design tokens and rules | | Increased maintenance cost | Centralized change management | | Delayed new member onboarding | Documented component catalog |
┌─────────────────────────────────────────────────────┐
│ Design Tokens │
│ Color, Typography, Spacing, Radius, Shadow, ... │
├─────────────────────────────────────────────────────┤
│ Core Components │
│ Button, Input, Card, Dialog, Avatar, Badge, ... │
├─────────────────────────────────────────────────────┤
│ Composite Components │
│ Form, DataTable, Navigation, SearchBar, ... │
└─────────────────────────────────────────────────────┘| Platform | Recommended Tools | Design Token Method | |----------|------------------|---------------------| | Web (React/Next.js) | shadcn/ui, Radix | CSS Variables | | Web (Vue) | Vuetify, PrimeVue | CSS Variables | | Flutter | Material 3, Custom Theme | ThemeData | | iOS (SwiftUI) | Native Components | Asset Catalog, Color Set | | Android (Compose) | Material 3 | MaterialTheme | | React Native | NativeBase, Tamagui | StyleSheet + Theme |
components/
└── ui/ # shadcn/ui components
├── button.tsx
├── input.tsx
├── card.tsx
└── ...
lib/
└── utils.ts # Utilities (cn function, etc.)
docs/02-design/
└── design-system.md # Design system specification| Level | Application Method | |-------|-------------------| | Starter | Optional (simple projects may skip) | | Dynamic | Required | | Enterprise | Required (including design tokens) |
bash# Initial setup npx shadcn@latest init # Add components npx shadcn@latest add button npx shadcn@latest add input npx shadcn@latest add card
shadcn/ui is CSS variable-based, so customize themes in globals.css.
css/* globals.css */ @layer base { :root { /* ===== Colors ===== */ --background: 0 0% 100%; --foreground: 222.2 84% 4.9%; --primary: 221.2 83.2% 53.3%; /* Brand main color */ --primary-foreground: 210 40% 98%; --secondary: 210 40% 96.1%; --accent: 210 40% 96.1%; --destructive: 0 84.2% 60.2%; --muted: 210 40% 96.1%; --muted-foreground: 215.4 16.3% 46.9%; /* ===== Typography ===== */ --font-sans: 'Pretendard', sans-serif; --font-mono: 'JetBrains Mono', monospace; /* ===== Spacing (rem units) ===== */ --spacing-xs: 0.25rem; /* 4px */ --spacing-sm: 0.5rem; /* 8px */ --spacing-md: 1rem; /* 16px */ --spacing-lg: 1.5rem; /* 24px */ --spacing-xl: 2rem; /* 32px */ /* ===== Border Radius ===== */ --radius: 0.5rem; --radius-sm: 0.25rem; --radius-lg: 0.75rem; --radius-full: 9999px; /* ===== Shadows ===== */ --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05); --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1); --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1); } .dark { --background: 222.2 84% 4.9%; --foreground: 210 40% 98%; --primary: 217.2 91.2% 59.8%; /* ... dark mode overrides */ } }
js// tailwind.config.js module.exports = { theme: { extend: { colors: { brand: { 50: 'hsl(var(--brand-50))', 500: 'hsl(var(--brand-500))', 900: 'hsl(var(--brand-900))', }, }, fontFamily: { sans: ['var(--font-sans)', 'system-ui'], mono: ['var(--font-mono)', 'monospace'], }, spacing: { 'xs': 'var(--spacing-xs)', 'sm': 'var(--spacing-sm)', 'md': 'var(--spacing-md)', 'lg': 'var(--spacing-lg)', 'xl': 'var(--spacing-xl)', }, borderRadius: { 'sm': 'var(--radius-sm)', 'DEFAULT': 'var(--radius)', 'lg': 'var(--radius-lg)', 'full': 'var(--radius-full)', }, }, }, }
Recommended to create docs/02-design/design-tokens.md per project:
| Token | Value | Purpose | |-------|-------|---------| | --primary | 221.2 83.2% 53.3% | Brand main color | | --radius | 0.5rem | Default border-radius | | --font-sans | Pretendard | Body font |
tsx// Extend default button to project style const Button = React.forwardRef< HTMLButtonElement, ButtonProps & { isLoading?: boolean } >(({ isLoading, children, ...props }, ref) => { return ( <ButtonPrimitive ref={ref} {...props}> {isLoading ? <Spinner /> : children} </ButtonPrimitive> ); });
Flutter defines design tokens through ThemeData.
dart// lib/theme/app_theme.dart import 'package:flutter/material.dart'; class AppTheme { // ===== Design Tokens ===== // Colors static const Color primary = Color(0xFF3B82F6); static const Color secondary = Color(0xFF64748B); static const Color destructive = Color(0xFFEF4444); static const Color background = Color(0xFFFFFFFF); static const Color foreground = Color(0xFF0F172A); // Spacing static const double spacingXs = 4.0; static const double spacingSm = 8.0; static const double spacingMd = 16.0; static const double spacingLg = 24.0; static const double spacingXl = 32.0; // Border Radius static const double radiusSm = 4.0; static const double radiusMd = 8.0; static const double radiusLg = 12.0; static const double radiusFull = 9999.0; // ===== Theme Data ===== static ThemeData get lightTheme => ThemeData( useMaterial3: true, colorScheme: ColorScheme.fromSeed( seedColor: primary, brightness: Brightness.light, ), fontFamily: 'Pretendard', // Button Theme elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( padding: EdgeInsets.symmetric( horizontal: spacingMd, vertical: spacingSm, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(radiusMd), ), ), ), // Card Theme cardTheme: CardTheme( elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(radiusLg), ), ), // Input Theme inputDecorationTheme: InputDecorationTheme( border: OutlineInputBorder( borderRadius: BorderRadius.circular(radiusMd), ), contentPadding: EdgeInsets.all(spacingSm), ), ); static ThemeData get darkTheme => ThemeData( useMaterial3: true, colorScheme: ColorScheme.fromSeed( seedColor: primary, brightness: Brightness.dark, ), fontFamily: 'Pretendard', // ... dark theme overrides ); }
dart// lib/components/app_button.dart import 'package:flutter/material.dart'; import '../theme/app_theme.dart'; enum AppButtonVariant { primary, secondary, destructive, outline } class AppButton extends StatelessWidget { final String label; final VoidCallback? onPressed; final AppButtonVariant variant; final bool isLoading; const AppButton({ required this.label, this.onPressed, this.variant = AppButtonVariant.primary, this.isLoading = false, super.key, }); @override Widget build(BuildContext context) { return ElevatedButton( onPressed: isLoading ? null : onPressed, style: _getStyle(), child: isLoading ? SizedBox( width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2), ) : Text(label), ); } ButtonStyle _getStyle() { switch (variant) { case AppButtonVariant.destructive: return ElevatedButton.styleFrom( backgroundColor: AppTheme.destructive, ); case AppButtonVariant.outline: return ElevatedButton.styleFrom( backgroundColor: Colors.transparent, side: BorderSide(color: AppTheme.primary), ); default: return ElevatedButton.styleFrom(); } } }
lib/
├── theme/
│ ├── app_theme.dart # ThemeData + Design Tokens
│ ├── app_colors.dart # Color constants
│ ├── app_typography.dart # TextStyle definitions
│ └── app_spacing.dart # Spacing constants
├── components/
│ ├── app_button.dart
│ ├── app_input.dart
│ ├── app_card.dart
│ └── app_dialog.dart
└── main.dartCentrally manage tokens with Figma Tokens or Style Dictionary.
json// tokens/design-tokens.json { "color": { "primary": { "value": "#3B82F6" }, "secondary": { "value": "#64748B" }, "destructive": { "value": "#EF4444" } }, "spacing": { "xs": { "value": "4px" }, "sm": { "value": "8px" }, "md": { "value": "16px" }, "lg": { "value": "24px" } }, "radius": { "sm": { "value": "4px" }, "md": { "value": "8px" }, "lg": { "value": "12px" } }, "font": { "family": { "value": "Pretendard" }, "size": { "sm": { "value": "14px" }, "md": { "value": "16px" }, "lg": { "value": "18px" } } } }
bash# Generate tokens for each platform with Style Dictionary npx style-dictionary build # Output: # - build/css/variables.css (Web) # - build/dart/app_tokens.dart (Flutter) # - build/swift/AppTokens.swift (iOS) # - build/kt/AppTokens.kt (Android)
See templates/pipeline/phase-5-design-system.template.md
Phase 6: UI Implementation + API Integration → Components are ready, now implement actual screens
Other measured skills in the registry, with their headline benchmark lift.