SKILL.md
---
name: ui-design-system
description: Complete design system with principles + living style guide. Enforces precise, crafted UI inspired by Linear, Notion, and Stripe. Includes boilerplate style-guide page template for Next.js/React projects. Use when building any UI that needs Jony Ive-level precision.
---
# Design System
This skill provides a complete design system: the **philosophy** (how to think about design) and the **documentation** (a living style guide template). Every interface should look designed by a team that obsesses over 1-pixel differences.
## Workflow
1. **Start with Design Direction** — Read Part 1 and commit to a direction before writing code
2. **Apply Craft Principles** — Follow the core rules in Part 2 as you build
3. **Document in Style Guide** — Add every component to the style guide (Part 3)
---
# Part 1: Design Direction
**Before writing any code, commit to a design direction.** Don't default. Think about what this specific product needs to feel like.
## Think About Context
- **What does this product do?** A finance tool needs different energy than a creative tool.
- **Who uses it?** Power users want density. Occasional users want guidance.
- **What's the emotional job?** Trust? Efficiency? Delight? Focus?
- **What would make this memorable?** Every product has a chance to feel distinctive.
## Choose a Personality
Enterprise/SaaS UI has more range than you think. Consider these directions:
**Precision & Density** — Tight spacing, monochrome, information-forward. For power users who live in the tool. Think Linear, Raycast, terminal aesthetics.
**Warmth & Approachability** — Generous spacing, soft shadows, friendly colors. For products that want to feel human. Think Notion, Coda, collaborative tools.
**Sophistication & Trust** — Cool tones, layered depth, financial gravitas. For products handling money or sensitive data. Think Stripe, Mercury, enterprise B2B.
**Boldness & Clarity** — High contrast, dramatic negative space, confident typography. For products that want to feel modern and decisive. Think Vercel, minimal dashboards.
**Utility & Function** — Muted palette, functional density, clear hierarchy. For products where the work matters more than the chrome. Think GitHub, developer tools.
**Data & Analysis** — Chart-optimized, technical but accessible, numbers as first-class citizens. For analytics, metrics, business intelligence.
Pick one. Or blend two. But commit to a direction that fits the product.
## Choose a Color Foundation
**Don't default to warm neutrals.** Consider the product:
- **Warm foundations** (creams, warm grays) — approachable, comfortable, human
- **Cool foundations** (slate, blue-gray) — professional, trustworthy, serious
- **Pure neutrals** (true grays, black/white) — minimal, bold, technical
- **Tinted foundations** (slight color cast) — distinctive, memorable, branded
**Light or dark?** Dark modes aren't just light modes inverted. Dark feels technical, focused, premium. Light feels open, approachable, clean. Choose based on context.
**Accent color** — Pick ONE that means something. Blue for trust. Green for growth. Orange for energy. Violet for creativity. Don't just reach for the same accent every time.
## Choose a Layout Approach
The content should drive the layout:
- **Dense grids** for information-heavy interfaces where users scan and compare
- **Generous spacing** for focused tasks where users need to concentrate
- **Sidebar navigation** for multi-section apps with many destinations
- **Top navigation** for simpler tools with fewer sections
- **Split panels** for list-detail patterns where context matters
## Choose Typography
Typography sets tone. Don't always default:
- **System fonts** — fast, native, invisible (good for utility-focused products)
- **Geometric sans** (Geist, Inter) — modern, clean, technical
- **Humanist sans** (SF Pro, Satoshi) — warmer, more approachable
- **Monospace influence** — technical, developer-focused, data-heavy
---
# Part 2: Craft Principles
These apply regardless of design direction. This is the quality floor.
## The 4px Grid
All spacing uses a 4px base grid:
- `4px` - micro spacing (icon gaps)
- `8px` - tight spacing (within components)
- `12px` - standard spacing (between related elements)
- `16px` - comfortable spacing (section padding)
- `24px` - generous spacing (between sections)
- `32px` - major separation
## Symmetrical Padding
**TLBR must match.** If top padding is 16px, left/bottom/right must also be 16px. Exception: when content naturally creates visual balance.
```css
/* Good */
padding: 16px;
padding: 12px 16px; /* Only when horizontal needs more room */
/* Bad */
padding: 24px 16px 12px 16px;
```
## Border Radius Consistency
Stick to the 4px grid. Sharper corners feel technical, rounder corners feel friendly. Pick a system and commit:
- Sharp: 4px, 6px, 8px
- Soft: 8px, 12px
- Minimal: 2px, 4px, 6px
Don't mix systems. Consistency creates coherence.
## Depth & Elevation Strategy
**Match your depth approach to your design direction.** Depth is a tool, not a requirement:
**Borders-only (flat)** — Clean, technical, dense. Works for utility-focused tools where information density matters more than visual lift. Linear, Raycast, and many developer tools use almost no shadows — just subtle borders to define regions.
**Subtle single shadows** — Soft lift without complexity. A simple `0 1px 3px rgba(0,0,0,0.08)` can be enough.
**Layered shadows** — Rich, premium, dimensional. Multiple shadow layers create realistic depth. Stripe and Mercury use this approach.
**Surface color shifts** — Background tints establish hierarchy without any shadows. A card at `#fff` on a `#f8fafc` background already feels elevated.
Choose ONE approach and commit.
```css
/* Borders-only approach */
--border: rgba(0, 0, 0, 0.08);
border: 0.5px solid var(--border);
/* Single shadow approach */
--shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
/* Layered shadow approach */
--shadow-layered:
0 0 0 0.5px rgba(0, 0, 0, 0.05),
0 1px 2px rgba(0, 0, 0, 0.04),
0 2px 4px rgba(0, 0, 0, 0.03),
0 4px 8px rgba(0, 0, 0, 0.02);
```
## Card Layouts
Monotonous card layouts are lazy design. Design each card's internal structure for its specific content — but keep the surface treatment consistent: same border weight, shadow depth, corner radius, padding scale, typography.
## Isolated Controls
UI controls deserve container treatment. Date pickers, filters, dropdowns should feel like crafted objects.
**Never use native form elements for styled UI.** Build custom components instead:
- Custom select: trigger button + positioned dropdown menu
- Custom date picker: input + calendar popover
- Custom checkbox/radio: styled div with state management
## Typography Hierarchy
- Headlines: 600 weight, tight letter-spacing (-0.02em)
- Body: 400-500 weight, standard tracking
- Labels: 500 weight, slight positive tracking for uppercase
- Scale: 11px, 12px, 13px, 14px (base), 16px, 18px, 24px, 32px
## Monospace for Data
Numbers, IDs, codes, timestamps belong in monospace. Use `tabular-nums` for columnar alignment.
## Iconography
Use **Phosphor Icons** (`@phosphor-icons/react`) or **Lucide** (`lucide-react`). Icons clarify, not decorate — if removing an icon loses no meaning, remove it.
## Animation
- 150ms for micro-interactions, 200-250ms for larger transitions
- Easing: `cubic-bezier(0.25, 1, 0.5, 1)`
- No spring/bouncy effects in enterprise UI
## Contrast Hierarchy
Build a four-level system: foreground (primary) → secondary → muted → faint. Use all four consistently.
## Color for Meaning Only
Gray builds structure. Color only appears when it communicates: status, action, error, success. Decorative color is noise.
## Navigation Context
Screens need grounding:
- **Navigation** — sidebar or top nav showing where you are
- **Location indicator** — breadcrumbs, page title, or active nav state
- **User context** — who's logged in, what workspace/org
## Dark Mode Considerations
**Borders over shadows** — Shadows are less visible on dark backgrounds. Lean more on borders.
**Adjust semantic colors** — Status colors often need to be slightly desaturated for dark backgrounds.
---
# Part 3: Anti-Patterns
## Never Do This
- Dramatic drop shadows (`box-shadow: 0 25px 50px...`)
- Large border radius (16px+) on small elements
- Asymmetric padding without clear reason
- Pure white cards on colored backgrounds
- Thick borders (2px+) for decoration
- Excessive spacing (margins > 48px between sections)
- Spring/bouncy animations
- Gradients for decoration
- Multiple accent colors in one interface
## Always Question
- "Did I think about what this product needs, or did I default?"
- "Does this direction fit the context and users?"
- "Does this element feel crafted?"
- "Is my depth strategy consistent and intentional?"
- "Are all elements on the grid?"
---
# Part 4: Style Guide Template
**The style guide is living documentation of your design system.** Every component you build should be documented here.
## What the Template Creates
A `/style-guide` page with:
- **Left navigation** — Sticky sidebar with hierarchical section navigation
- **Colors** — Brand palette, category colors, system colors
- **Typography** — Font families, sizes, weights
- **Buttons** — All variants, sizes, states
- **Inputs** — Default, focus, error, disabled states
- **Cards** — Layout variations
- **Avatars** — Sizes and fallback states
- **Dialogs** — Modal patterns
- **Loading/Thinking** — Spinners, indicators, animated states
- **Design Tokens** — Spacing scale, border radius, shadows
## Using the Template
### 1. Copy to Your Project
```bash
cp templates/style-guide-page.tsx your-app/app/style-guide/page.tsx
```
### 2. Customize
1. **Update the header title** — Replace "STYLE GUIDE" with your project name
2. **Add your brand colors** — Update the Colors section with your palette
3. **Add your fonts** — Update the Typography section
4. **Import your components** — Replace placeholder imports with your actual components
5. **Add project-specific sections** — Add sections for custom components
### 3. Maintain
**The style guide is the canonical source for all UI components.**
When building or modifying components:
1. **Check the style guide first** — See if a component exists before creating
2. **Add new components** — Every reusable component gets a section
3. **Show all states** — Normal, hover, focus, disabled, error, loading
4. **Update on changes** — Modify a component? Update its style guide entry
## Template Structure
Navigation items define the sidebar structure:
```typescript
const NAV_ITEMS = [
{ id: 'colors', label: 'Colors', icon: Palette, children: [
{ id: 'brand', label: 'Brand' },
{ id: 'system', label: 'System' },
]},
{ id: 'typography', label: 'Typography', icon: Type },
{ id: 'buttons', label: 'Buttons', icon: MousePointer },
// ... more sections
]
```
Each section follows this pattern:
```tsx
<section id="buttons">
<SectionHeader title="Buttons" />
<div>
<h4 className="text-xs font-medium text-muted-foreground mb-3 uppercase tracking-wide">
Variants
</h4>
<div className="flex flex-wrap gap-3">
<Button variant="default">Default</Button>
<Button variant="secondary">Secondary</Button>
</div>
</div>
</section>
```
## Adding Custom Sections
1. Add to `NAV_ITEMS`:
```typescript
{ id: 'my-component', label: 'My Component', icon: Box },
```
2. Add the section:
```tsx
<section id="my-component">
<SectionHeader title="My Component" />
{/* Component variations */}
</section>
```
## Maintenance Checklist
For every UI change:
- [ ] Component added to style guide (if new)
- [ ] All states shown (normal, hover, disabled, loading, error)
- [ ] Uses design tokens (colors, spacing, radius from theme)
- [ ] Properly exported from component index
## Best Practices
1. **Keep it current** — Outdated style guides are worse than none
2. **Show real data** — Use realistic mock data, not "Lorem ipsum"
3. **Test interactions** — Dialogs should open, buttons should click
4. **Document edge cases** — Long text, empty states, loading
5. **Mobile considerations** — Show responsive variants if applicable
---
# The Standard
Every interface should look designed by a team that obsesses over 1-pixel differences. Not stripped — *crafted*. And designed for its specific context.
The goal: intricate minimalism with appropriate personality. Same quality bar, context-driven execution.
templates/style-guide-page.tsx
'use client'
import { useState } from 'react'
import {
Palette,
Type,
MousePointer,
TextCursor,
LayoutGrid,
CircleUser,
MessageSquare,
Ruler,
Loader2,
Mail,
Search,
Plus,
Settings,
ArrowRight,
Check,
X
} from 'lucide-react'
// ============================================
// IMPORTS: Update these with your actual components
// ============================================
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import {
Card,
CardHeader,
CardTitle,
CardDescription,
CardContent,
CardFooter
} from '@/components/ui/card'
import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar'
import {
Dialog,
DialogTrigger,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
DialogFooter,
} from '@/components/ui/dialog'
import { cn } from '@/lib/utils'
// ============================================
// NAVIGATION: Customize sections for your project
// ============================================
const NAV_ITEMS = [
{ id: 'colors', label: 'Colors', icon: Palette, children: [
{ id: 'brand', label: 'Brand' },
{ id: 'system', label: 'System' },
]},
{ id: 'typography', label: 'Typography', icon: Type },
{ id: 'buttons', label: 'Buttons', icon: MousePointer },
{ id: 'inputs', label: 'Inputs', icon: TextCursor },
{ id: 'cards', label: 'Cards', icon: LayoutGrid },
{ id: 'avatars', label: 'Avatars', icon: CircleUser },
{ id: 'dialogs', label: 'Dialogs', icon: MessageSquare },
{ id: 'loading', label: 'Loading', icon: Loader2 },
{ id: 'tokens', label: 'Tokens', icon: Ruler, children: [
{ id: 'spacing', label: 'Spacing' },
{ id: 'radius', label: 'Radius' },
{ id: 'shadows', label: 'Shadows' },
]},
]
// ============================================
// MAIN PAGE
// ============================================
export default function StyleGuidePage() {
const [dialogOpen, setDialogOpen] = useState(false)
const [activeSection, setActiveSection] = useState('colors')
const scrollToSection = (id: string) => {
setActiveSection(id)
document.getElementById(id)?.scrollIntoView({ behavior: 'smooth' })
}
return (
<div className="min-h-screen bg-background">
{/* Header */}
<header className="sticky top-0 z-50 border-b border-border bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<div className="px-6 py-4">
{/* TODO: Update with your project name */}
<h1 className="text-2xl font-bold text-foreground">STYLE GUIDE</h1>
</div>
</header>
<div className="flex">
{/* Left Navigation */}
<nav className="sticky top-[57px] h-[calc(100vh-57px)] w-56 shrink-0 overflow-y-auto border-r border-border bg-muted/30 p-4">
<ul className="space-y-1">
{NAV_ITEMS.map((item) => (
<li key={item.id}>
<button
onClick={() => scrollToSection(item.id)}
className={cn(
"flex w-full items-center gap-2 rounded-md px-3 py-2 text-sm font-medium transition-colors",
activeSection === item.id || item.children?.some(c => activeSection === c.id)
? "bg-primary/10 text-primary"
: "text-muted-foreground hover:bg-muted hover:text-foreground"
)}
>
<item.icon className="h-4 w-4" />
{item.label}
</button>
{item.children && (
<ul className="ml-6 mt-1 space-y-1">
{item.children.map((child) => (
<li key={child.id}>
<button
onClick={() => scrollToSection(child.id)}
className={cn(
"w-full rounded-md px-3 py-1.5 text-left text-xs transition-colors",
activeSection === child.id
? "text-primary font-medium"
: "text-muted-foreground hover:text-foreground"
)}
>
{child.label}
</button>
</li>
))}
</ul>
)}
</li>
))}
</ul>
</nav>
{/* Main Content */}
<main className="flex-1 p-8 space-y-16 max-w-5xl">
{/* ==========================================
COLORS
Update with your brand colors
========================================== */}
<section id="colors">
<SectionHeader title="Colors" />
{/* Brand Colors */}
<div id="brand" className="mb-8">
<h4 className="text-sm font-medium text-muted-foreground mb-3">Brand</h4>
<div className="flex flex-wrap gap-2">
{/* TODO: Replace with your brand colors */}
<ColorChip color="bg-primary" label="Primary" hex="#3B82F6" />
<ColorChip color="bg-secondary" label="Secondary" hex="#6B7280" />
<ColorChip color="bg-accent" label="Accent" hex="#F59E0B" />
</div>
</div>
{/* System Colors */}
<div id="system">
<h4 className="text-sm font-medium text-muted-foreground mb-3">System</h4>
<div className="flex flex-wrap gap-2">
<ColorChip color="bg-background" label="Background" hex="#FFF" border />
<ColorChip color="bg-foreground" label="Foreground" hex="#1F2937" light />
<ColorChip color="bg-muted" label="Muted" hex="#F3F4F6" />
<ColorChip color="bg-destructive" label="Destructive" hex="#EF4444" light />
<ColorChip color="bg-border" label="Border" hex="#E5E7EB" />
</div>
</div>
</section>
{/* ==========================================
TYPOGRAPHY
Update with your fonts
========================================== */}
<section id="typography">
<SectionHeader title="Typography" />
<div className="grid md:grid-cols-2 gap-8">
<div>
{/* TODO: Update with your display font */}
<h4 className="text-sm font-medium text-muted-foreground mb-3">Headings</h4>
<div className="space-y-2">
<p className="text-4xl font-bold">Heading 1</p>
<p className="text-3xl font-bold">Heading 2</p>
<p className="text-2xl font-semibold">Heading 3</p>
<p className="text-xl font-semibold">Heading 4</p>
</div>
</div>
<div>
<h4 className="text-sm font-medium text-muted-foreground mb-3">Body</h4>
<div className="space-y-2">
<p className="text-lg">Large body text</p>
<p className="text-base">Base body text</p>
<p className="text-sm text-muted-foreground">Small muted text</p>
<p className="text-xs text-muted-foreground">Extra small label</p>
</div>
</div>
</div>
</section>
{/* ==========================================
BUTTONS
========================================== */}
<section id="buttons">
<SectionHeader title="Buttons" />
<div className="space-y-6">
<div>
<h4 className="text-xs font-medium text-muted-foreground mb-3 uppercase tracking-wide">Variants</h4>
<div className="flex flex-wrap gap-3">
<Button variant="default">Default</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="link">Link</Button>
<Button variant="destructive">Destructive</Button>
</div>
</div>
<div>
<h4 className="text-xs font-medium text-muted-foreground mb-3 uppercase tracking-wide">Sizes</h4>
<div className="flex flex-wrap items-center gap-3">
<Button size="sm">Small</Button>
<Button size="default">Default</Button>
<Button size="lg">Large</Button>
<Button size="icon"><Plus /></Button>
</div>
</div>
<div>
<h4 className="text-xs font-medium text-muted-foreground mb-3 uppercase tracking-wide">With Icons</h4>
<div className="flex flex-wrap gap-3">
<Button><Mail className="mr-2 h-4 w-4" />Email</Button>
<Button variant="outline">Next <ArrowRight className="ml-2 h-4 w-4" /></Button>
<Button variant="secondary"><Settings className="mr-2 h-4 w-4" />Settings</Button>
</div>
</div>
<div>
<h4 className="text-xs font-medium text-muted-foreground mb-3 uppercase tracking-wide">States</h4>
<div className="flex flex-wrap gap-3">
<Button>Normal</Button>
<Button disabled>Disabled</Button>
<Button disabled>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Loading
</Button>
</div>
</div>
</div>
</section>
{/* ==========================================
INPUTS
========================================== */}
<section id="inputs">
<SectionHeader title="Inputs" />
<div className="max-w-sm space-y-4">
<div>
<label className="text-sm font-medium mb-1 block">Default</label>
<Input placeholder="Placeholder text..." />
</div>
<div>
<label className="text-sm font-medium mb-1 block">With Value</label>
<Input defaultValue="Entered value" />
</div>
<div>
<label className="text-sm font-medium mb-1 block">Disabled</label>
<Input placeholder="Disabled" disabled />
</div>
<div>
<label className="text-sm font-medium mb-1 block">Error</label>
<Input placeholder="Error state" aria-invalid="true" />
<p className="text-xs text-destructive mt-1">Validation error message</p>
</div>
<div>
<label className="text-sm font-medium mb-1 block">With Icon</label>
<div className="relative">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
<Input placeholder="Search..." className="pl-10" />
</div>
</div>
</div>
</section>
{/* ==========================================
CARDS
========================================== */}
<section id="cards">
<SectionHeader title="Cards" />
<div className="grid md:grid-cols-2 gap-4">
<Card>
<CardHeader>
<CardTitle>Basic Card</CardTitle>
<CardDescription>Card with header and content</CardDescription>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground">Card body content goes here.</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>With Footer</CardTitle>
<CardDescription>Card with action buttons</CardDescription>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground">Card with footer actions.</p>
</CardContent>
<CardFooter className="gap-2">
<Button variant="outline" size="sm">Cancel</Button>
<Button size="sm">Save</Button>
</CardFooter>
</Card>
<Card>
<CardHeader>
<CardTitle>Interactive Card</CardTitle>
<CardDescription>With form elements</CardDescription>
</CardHeader>
<CardContent className="space-y-3">
<Input placeholder="Enter name..." />
<Input placeholder="Enter email..." />
</CardContent>
<CardFooter>
<Button className="w-full">Submit</Button>
</CardFooter>
</Card>
<Card className="flex items-center justify-center p-8">
<div className="text-center text-muted-foreground">
<Plus className="h-8 w-8 mx-auto mb-2 opacity-50" />
<p className="text-sm">Empty state placeholder</p>
</div>
</Card>
</div>
</section>
{/* ==========================================
AVATARS
========================================== */}
<section id="avatars">
<SectionHeader title="Avatars" />
<div className="flex items-end gap-6">
<div className="text-center">
<Avatar className="h-8 w-8">
<AvatarFallback>S</AvatarFallback>
</Avatar>
<p className="text-xs text-muted-foreground mt-2">sm</p>
</div>
<div className="text-center">
<Avatar className="h-10 w-10">
<AvatarImage src="https://github.com/shadcn.png" />
<AvatarFallback>M</AvatarFallback>
</Avatar>
<p className="text-xs text-muted-foreground mt-2">md</p>
</div>
<div className="text-center">
<Avatar className="h-14 w-14">
<AvatarFallback className="bg-primary text-primary-foreground text-lg">LG</AvatarFallback>
</Avatar>
<p className="text-xs text-muted-foreground mt-2">lg</p>
</div>
<div className="text-center">
<Avatar className="h-14 w-14">
<AvatarFallback className="bg-accent text-white text-xl">🎨</AvatarFallback>
</Avatar>
<p className="text-xs text-muted-foreground mt-2">emoji</p>
</div>
</div>
</section>
{/* ==========================================
DIALOGS
========================================== */}
<section id="dialogs">
<SectionHeader title="Dialogs" />
<div className="flex flex-wrap gap-4">
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
<DialogTrigger asChild>
<Button variant="outline">Open Dialog</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Dialog Title</DialogTitle>
<DialogDescription>
Dialog description text that explains what this dialog is for.
</DialogDescription>
</DialogHeader>
<div className="py-4">
<p className="text-sm text-muted-foreground">Dialog content goes here.</p>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setDialogOpen(false)}>Cancel</Button>
<Button onClick={() => setDialogOpen(false)}>Confirm</Button>
</DialogFooter>
</DialogContent>
</Dialog>
{/* TODO: Add your custom dialogs here */}
</div>
</section>
{/* ==========================================
LOADING STATES
TODO: Add your loading/spinner components
========================================== */}
<section id="loading">
<SectionHeader title="Loading States" />
<div className="space-y-6">
{/* Spinner */}
<div>
<h4 className="text-xs font-medium text-muted-foreground mb-3 uppercase tracking-wide">Spinners</h4>
<div className="flex items-center gap-6">
<div className="flex items-center gap-2">
<Loader2 className="h-4 w-4 animate-spin text-muted-foreground" />
<span className="text-xs text-muted-foreground">sm</span>
</div>
<div className="flex items-center gap-2">
<Loader2 className="h-6 w-6 animate-spin text-primary" />
<span className="text-xs text-muted-foreground">md</span>
</div>
<div className="flex items-center gap-2">
<Loader2 className="h-8 w-8 animate-spin text-primary" />
<span className="text-xs text-muted-foreground">lg</span>
</div>
</div>
</div>
{/* Skeleton */}
<div>
<h4 className="text-xs font-medium text-muted-foreground mb-3 uppercase tracking-wide">Skeleton Loading</h4>
<div className="max-w-md space-y-3">
<div className="h-4 bg-muted rounded animate-pulse" />
<div className="h-4 bg-muted rounded animate-pulse w-3/4" />
<div className="h-4 bg-muted rounded animate-pulse w-1/2" />
</div>
</div>
{/* Status indicators */}
<div>
<h4 className="text-xs font-medium text-muted-foreground mb-3 uppercase tracking-wide">Status</h4>
<div className="flex flex-wrap gap-4">
<div className="flex items-center gap-2">
<div className="h-2 w-2 rounded-full bg-green-500" />
<span className="text-sm">Connected</span>
</div>
<div className="flex items-center gap-2">
<div className="h-2 w-2 rounded-full bg-yellow-500 animate-pulse" />
<span className="text-sm">Syncing</span>
</div>
<div className="flex items-center gap-2">
<div className="h-2 w-2 rounded-full bg-muted-foreground/50" />
<span className="text-sm">Offline</span>
</div>
<div className="flex items-center gap-2">
<div className="h-2 w-2 rounded-full bg-destructive" />
<span className="text-sm">Error</span>
</div>
</div>
</div>
{/* TODO: Add thinking/AI indicators here */}
</div>
</section>
{/* ==========================================
DESIGN TOKENS
========================================== */}
<section id="tokens">
<SectionHeader title="Design Tokens" />
{/* Spacing */}
<div id="spacing" className="mb-8">
<h4 className="text-xs font-medium text-muted-foreground mb-3 uppercase tracking-wide">Spacing</h4>
<div className="flex items-end gap-1">
{[1, 2, 3, 4, 6, 8, 12, 16].map(s => (
<div key={s} className="text-center">
<div className="bg-primary rounded-sm" style={{ width: s * 4, height: 32 }} />
<span className="text-[10px] text-muted-foreground">{s}</span>
</div>
))}
</div>
<p className="text-xs text-muted-foreground mt-2">Values shown in 4px units (1 = 4px, 2 = 8px, etc.)</p>
</div>
{/* Radius */}
<div id="radius" className="mb-8">
<h4 className="text-xs font-medium text-muted-foreground mb-3 uppercase tracking-wide">Border Radius</h4>
<div className="flex gap-4">
{[
{ name: 'sm', class: 'rounded-sm' },
{ name: 'md', class: 'rounded-md' },
{ name: 'lg', class: 'rounded-lg' },
{ name: 'xl', class: 'rounded-xl' },
{ name: 'full', class: 'rounded-full' },
].map(r => (
<div key={r.name} className="text-center">
<div className={`h-12 w-12 bg-primary ${r.class}`} />
<span className="text-[10px] text-muted-foreground">{r.name}</span>
</div>
))}
</div>
</div>
{/* Shadows */}
<div id="shadows">
<h4 className="text-xs font-medium text-muted-foreground mb-3 uppercase tracking-wide">Shadows</h4>
<div className="flex gap-6">
{[
{ name: 'sm', class: 'shadow-sm' },
{ name: 'md', class: 'shadow-md' },
{ name: 'lg', class: 'shadow-lg' },
{ name: 'xl', class: 'shadow-xl' },
].map(s => (
<div key={s.name} className="text-center">
<div className={`h-12 w-16 bg-card rounded-lg ${s.class}`} />
<span className="text-[10px] text-muted-foreground mt-2 block">{s.name}</span>
</div>
))}
</div>
</div>
</section>
</main>
</div>
</div>
)
}
// ============================================
// HELPER COMPONENTS
// ============================================
function SectionHeader({ title }: { title: string }) {
return (
<h2 className="text-xl font-semibold text-foreground mb-6 pb-2 border-b border-border">
{title}
</h2>
)
}
interface ColorChipProps {
color: string
label: string
hex: string
border?: boolean
light?: boolean
}
function ColorChip({ color, label, hex, border, light }: ColorChipProps) {
return (
<div
className={cn(
"flex items-center gap-2 rounded-full pl-1 pr-3 py-1",
border && "border border-border"
)}
>
<div className={cn("h-6 w-6 rounded-full", color)} />
<div className="flex items-baseline gap-1.5">
<span className="text-xs font-medium">{label}</span>
<span className="text-[10px] text-muted-foreground font-mono">{hex}</span>
</div>
</div>
)
}