+ Covers all eight sections with concrete token mappings.
- The Slot/link disabled handling and undefined tokens are problematic.
a design systems documentarian creating the component specification for a CLAUDE.md file.
| Category | Development › Technical writing |
|---|---|
| Tags | DraftingDeveloperCodeTemplate |
You are a design systems documentarian creating the component specification
for a CLAUDE.md file. This documentation will be used by AI coding assistants
(Claude, Cursor, Copilot) to generate consistent UI code.
## Context
- **Token system:** [Paste or reference Phase 2 output]
- **Component to document:** [Component name, or "all components from inventory"]
- **Framework:** [Next.js + React + Tailwind / etc.]
## For Each Component, Document:
### 1. Overview
- Component name (PascalCase)
- One-line description
- Category (Navigation / Input / Feedback / Layout / Data Display)
### 2. Anatomy
- List every visual part (e.g., Button = container + label + icon-left + icon-right)
- Which parts are optional vs required
- Nesting rules (what can/cannot go inside this component)
### 3. Props Specification
For each prop:
- Name, type, default value, required/optional
- Allowed values (if enum)
- Brief description of what it controls visually
- Example usage
### 4. Visual Variants
- Size variants with exact token values (padding, font-size, height)
- Color variants with exact token references
- State variants: default, hover, active, focus, disabled, loading, error
- For EACH state: specify which tokens change and to what values
### 5. Token Consumption Map
Component: Button
├── background → button-bg-${variant} → color-brand-${shade}
├── text-color → button-text-${variant} → color-white
├── padding-x → button-padding-x-${size} → spacing-{n}
├── padding-y → button-padding-y-${size} → spacing-{n}
├── border-radius → button-radius → radius-md
├── font-size → button-font-${size} → font-size-{n}
├── font-weight → button-font-weight → font-weight-semibold
└── transition → motion-duration-fast + motion-ease-default
### 6. Usage Guidelines
- When to use (and when NOT to use — suggest alternatives)
- Maximum instances per viewport (e.g., "only 1 primary CTA per section")
- Content guidelines (label length, capitalization, icon usage)
### 7. Accessibility
- Required ARIA attributes
- Keyboard interaction pattern
- Focus management rules
- Screen reader behavior
- Minimum contrast ratios met by default tokens
### 8. Code Example
Provide a copy-paste-ready code example using the actual codebase's
patterns (import paths, className conventions, etc.)
## Output Format
Markdown, structured with headers per section. This will be directly
inserted into the CLAUDE.md file.This is useful for documenting design-system components for AI coding tools. It asks for anatomy, props, state-specific tokens, accessibility, and code examples aligned with the actual codebase patterns.
ChatGPT is the most complete and faithful to the supplied tokens. Gemini is well organized but inaccurate and incomplete, while [C] was not provided.
+ Covers all eight sections with concrete token mappings.
- The Slot/link disabled handling and undefined tokens are problematic.
+ Tables and state matrices make detailed rules easy to scan.
- The code is truncated and invents tokens while misstating contrast.
| Criterion | ChatGPT | Gemini | Leader |
|---|---|---|---|
| Instruction following | 9 | 5 | ChatGPT +80% |
| Accuracy | 8 | 5 | ChatGPT +60% |
| Specificity | 9 | 7 | ChatGPT +29% |
| Structure | 9 | 8 | ChatGPT +13% |
| Right length | 8 | 4 | ChatGPT +100% |
Scored 1–10 by gpt-5.6-sol with model names hidden (2026-09-24). This is an AI review, not a measurement.
We gave three models the same input and copied their answers unedited. Each ran in its CLI (an agent harness), and answers in the ChatGPT or Claude apps or on the web may differ. Outputs are in Korean.
[Pasted material] Token system (Phase 2 산출물): - color-brand-500: #2563EB, color-brand-600: #1D4ED8 - spacing-1~8: 4px 단위로 4px~32px - radius-md: 8px, radius-lg: 12px - font-size-sm/base/lg: 14px/16px/18px Component to document: Button Framework: Next.js 14 + React + TailwindCSS (shadcn/ui 기반)
Button기본 프레임워크는 Next.js 14, React, TailwindCSS, shadcn/ui의 cva 및 Slot 패턴을 따른다.
Button
├── interactive container
├── icon-left optional
├── label required, icon-only일 때는 visually hidden 가능
├── icon-right optional
└── loading indicator optional
aria-label 또는 sr-only 접근성 이름<button>, <a>, 입력 필드 또는 인터랙티브 요소를 중첩하지 않는다.Button보다 Link를 사용한다.asChild 사용 시 단일 React element만 자식으로 전달한다.| Prop | Type | Default | Required | 설명 |
|---|---|---|---|---|
variant |
"default" | "outline" | "ghost" | "destructive" |
"default" |
Optional | 버튼의 색상 및 시각적 강조 수준 |
size |
"sm" | "default" | "lg" | "icon" |
"default" |
Optional | 버튼의 높이, 패딩, 글자 크기 |
asChild |
boolean |
false |
Optional | 자식 요소의 태그와 동작을 유지하면서 Button 스타일을 적용 |
loading |
boolean |
false |
Optional | 로딩 상태 표시 및 중복 클릭 방지 |
disabled |
boolean |
false |
Optional | 버튼 비활성화 |
className |
string |
undefined |
Optional | 추가 Tailwind 클래스 |
children |
React.ReactNode |
— | Required | 버튼 내용 |
type |
"button" | "submit" | "reset" |
"button" |
Optional | 폼 내 버튼의 동작 |
aria-label |
string |
undefined |
Conditional | 아이콘 전용 버튼의 접근성 이름 |
<Button variant="default" size="default">
저장
</Button>
<Button variant="outline" loading>
저장 중
</Button>
<Button size="icon" aria-label="설정 열기">
<Settings className="size-4" />
</Button>
<Button asChild>
<Link href="/settings">설정</Link>
</Button>
| Size | Height | Padding X | Padding Y | Font size | 아이콘 |
|---|---|---|---|---|---|
sm |
32px | spacing-3 = 12px |
spacing-2 = 8px |
font-size-sm = 14px |
16px |
default |
40px | spacing-4 = 16px |
spacing-2 = 8px |
font-size-base = 16px |
16px |
lg |
48px | spacing-6 = 24px |
spacing-3 = 12px |
font-size-lg = 18px |
20px |
icon |
40px | 0px | 0px | — | 16px |
border-radius: radius-md = 8pxfont-weight: font-weight-semibold = 600gap: spacing-2 = 8pxtransition: motion-duration-fast + motion-ease-default| Variant | Default | Hover | Active | Text |
|---|---|---|---|---|
default |
color-brand-500 = #2563EB |
color-brand-600 = #1D4ED8 |
color-brand-600 = #1D4ED8 |
color-white |
outline |
transparent + color-brand-500 border |
color-brand-500 background |
color-brand-600 background |
color-brand-600 |
ghost |
transparent | color-brand-500 at 10% opacity |
color-brand-500 at 15% opacity |
color-brand-600 |
destructive |
color-error-500 |
color-error-600 |
color-error-600 |
color-white |
destructive와 ghost에 필요한 color-error-*, 투명도 토큰은 Phase 2 토큰 시스템에 추가하거나 프로젝트의 기존 semantic token으로 매핑한다.
| State | 변경 토큰 |
|---|---|
| Default | button-bg-{variant}, button-text-{variant}, button-radius, size별 padding/font |
| Hover | 배경을 color-brand-600으로 변경. outline은 color-brand-500 배경으로 변경 |
| Active | 배경을 color-brand-600으로 변경하고 transform: translateY(1px) 적용 가능 |
| Focus-visible | ring-width-2, ring-color-brand-500, ring-offset-2 적용 |
| Disabled | opacity-50, cursor-not-allowed, hover/active 스타일 제거 |
| Loading | 기존 label의 시각적 투명도 감소, loading-indicator 표시, 클릭 방지 |
| Error | color-error-* border/background/text 및 aria-invalid="true" 적용 |
로딩 상태에서는 disabled와 동일하게 키보드 및 포인터 입력을 차단한다.
Button
├── background
│ └── button-bg-${variant}
│ ├── default → color-brand-500
│ └── hover/active → color-brand-600
├── text-color
│ └── button-text-${variant}
│ ├── default → color-white
│ └── outline/ghost → color-brand-600
├── border-color
│ └── button-border-${variant}
│ └── outline → color-brand-500
├── padding-x
│ └── button-padding-x-${size}
│ ├── sm → spacing-3
│ ├── default → spacing-4
│ └── lg → spacing-6
├── padding-y
│ └── button-padding-y-${size}
│ ├── sm/default → spacing-2
│ └── lg → spacing-3
├── height
│ ├── sm → 32px
│ ├── default/icon → 40px
│ └── lg → 48px
├── border-radius
│ └── button-radius → radius-md
├── font-size
│ ├── sm → font-size-sm
│ ├── default → font-size-base
│ └── lg → font-size-lg
├── font-weight
│ └── button-font-weight → font-weight-semibold
├── icon-size
│ ├── sm/default → 16px
│ └── lg → 20px
└── transition
└── motion-duration-fast + motion-ease-default
Link 사용Select, RadioGroup, Tabs 사용DropdownMenuTrigger 사용default 스타일의 Primary CTA를 최대 1개만 둔다.저장, 계속, 초대 보내기aria-label을 제공한다.<button>을 사용하므로 별도의 role="button"을 추가하지 않는다.aria-label을 필수로 제공한다.aria-busy="true"를 제공한다.aria-invalid="true"를 제공할 수 있다.aria-expanded 및 aria-controls를 사용한다.disabled 속성을 우선 사용한다.Tab: 버튼으로 이동Enter 또는 Space: 버튼 실행Shift + Tab: 이전 포커스 가능 요소로 이동asChild로 링크를 렌더링하는 경우 링크의 기본 키보드 동작을 유지한다.:focus-visible에서만 focus ring을 표시한다.ring-width-2, ring-color-brand-500, ring-offset-2를 사용한다.aria-hidden="true"를 설정하고 버튼에 aria-label을 지정한다.저장 → 저장 중.color-brand-500(#2563EB) + color-white: 약 5.17:1color-brand-600(#1D4ED8) + color-white: 약 6.70:1프로젝트의 일반적인 shadcn/ui 구조에서는 @/components/ui/button 경로를 사용한다.
// components/ui/button.tsx
"use client"
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { Loader2 } from "lucide-react"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
[
"inline-flex items-center justify-center gap-2 whitespace-nowrap",
"rounded-[8px] text-center font-semibold",
"transition-colors duration-150 ease-out",
"focus-visible:outline-none focus-visible:ring-2",
"focus-visible:ring-[#2563EB] focus-visible:ring-offset-2",
"disabled:pointer-events-none disabled:opacity-50",
"[&_svg]:pointer-events-none [&_svg]:shrink-0",
],
{
variants: {
variant: {
default:
"bg-[#2563EB] text-white hover:bg-[#1D4ED8] active:bg-[#1D4ED8]",
outline:
"border border-[#2563EB] bg-transparent text-[#1D4ED8] hover:bg-[#2563EB] hover:text-white",
ghost:
"bg-transparent text-[#1D4ED8] hover:bg-[#2563EB]/10 active:bg-[#2563EB]/15",
destructive:
"bg-error-500 text-white hover:bg-error-600 active:bg-error-600",
},
size: {
sm: "h-8 px-3 py-2 text-sm [&_svg]:size-4",
default: "h-10 px-4 py-2 text-base [&_svg]:size-4",
lg: "h-12 px-6 py-3 text-lg [&_svg]:size-5",
icon: "size-10 [&_svg]:size-4",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> &
VariantProps<typeof buttonVariants> & {
asChild?: boolean
loading?: boolean
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
(
{
className,
variant,
size,
asChild = false,
loading = false,
disabled,
children,
...props
},
ref
) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
ref={ref}
className={cn(buttonVariants({ variant, size, className }))}
disabled={disabled || loading}
aria-busy={loading || undefined}
{...props}
>
{loading && <Loader2 className="size-4 animate-spin" aria-hidden="true" />}
<span className={loading ? "opacity-80" : undefined}>{children}</span>
</Comp>
)
}
)
Button.displayName = "Button"
export { Button, buttonVariants }
import Link from "next/link"
import { Settings } from "lucide-react"
import { Button } from "@/components/ui/button"
export function ExampleActions() {
return (
<div className="flex items-center gap-2">
<Button>저장</Button>
<Button variant="outline">취소</Button>
<Button variant="ghost" size="icon" aria-label="설정 열기">
<Settings aria-hidden="true" />
</Button>
<Button asChild>
<Link href="/dashboard">대시보드로 이동</Link>
</Button>
</div>
)
}
color-error-500, color-error-600, motion-duration-fast, motion-ease-default가 아직 토큰 시스템에 없다면 프로젝트의 기존 semantic token으로 연결하거나 Phase 2 토큰에 추가한다.
| Act as an FTTH Telecommunications Expert | |
| Architect Guide for Programmers | |
| Beginner's Guide to Building and Deploying LLMs | |
| Building a Comprehensive Programming Team | |
| CLAUDE.md Assembly |