@kood/claude-code 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/dist/index.js +62 -18
  2. package/package.json +1 -1
  3. package/templates/.claude/agents/code-reviewer.md +31 -0
  4. package/templates/.claude/agents/debug-detective.md +37 -0
  5. package/templates/.claude/agents/refactor-advisor.md +44 -0
  6. package/templates/.claude/agents/test-writer.md +41 -0
  7. package/templates/.claude/skills/frontend-design/SKILL.md +310 -0
  8. package/templates/.claude/skills/frontend-design/references/animation-patterns.md +446 -0
  9. package/templates/.claude/skills/frontend-design/references/colors-2026.md +244 -0
  10. package/templates/.claude/skills/frontend-design/references/typography-2026.md +302 -0
  11. package/templates/.claude/skills/gemini-review/SKILL.md +1 -1
  12. package/templates/hono/docs/library/drizzle/cloudflare-d1.md +247 -0
  13. package/templates/hono/docs/library/drizzle/config.md +167 -0
  14. package/templates/hono/docs/library/drizzle/index.md +259 -0
  15. package/templates/tanstack-start/docs/library/drizzle/cloudflare-d1.md +146 -0
  16. package/templates/tanstack-start/docs/library/drizzle/config.md +118 -0
  17. package/templates/tanstack-start/docs/library/drizzle/crud.md +205 -0
  18. package/templates/tanstack-start/docs/library/drizzle/index.md +79 -0
  19. package/templates/tanstack-start/docs/library/drizzle/relations.md +202 -0
  20. package/templates/tanstack-start/docs/library/drizzle/schema.md +154 -0
  21. package/templates/tanstack-start/docs/library/drizzle/setup.md +95 -0
  22. package/templates/tanstack-start/docs/library/drizzle/transactions.md +127 -0
  23. package/templates/tanstack-start/docs/library/tanstack-router/error-handling.md +204 -0
  24. package/templates/tanstack-start/docs/library/tanstack-router/hooks.md +195 -0
  25. package/templates/tanstack-start/docs/library/tanstack-router/index.md +150 -0
  26. package/templates/tanstack-start/docs/library/tanstack-router/navigation.md +150 -0
  27. package/templates/tanstack-start/docs/library/tanstack-router/route-context.md +203 -0
  28. package/templates/tanstack-start/docs/library/tanstack-router/search-params.md +213 -0
package/dist/index.js CHANGED
@@ -159,26 +159,42 @@ var copyCommands = async (_templates, targetDir) => {
159
159
  }
160
160
  return counter;
161
161
  };
162
- var checkSkillsAndCommandsExist = async (_templates) => {
163
- const claudeDir = path.join(getTemplatesDir(), ".claude");
164
- const skillsSrc = path.join(claudeDir, "skills");
165
- const commandsSrc = path.join(claudeDir, "commands");
166
- const hasSkills = await fs.pathExists(skillsSrc);
167
- const hasCommands = await fs.pathExists(commandsSrc);
168
- return { hasSkills, hasCommands };
169
- };
170
162
  var checkExistingClaudeFiles = async (targetDir) => {
171
163
  const existingFiles = [];
172
164
  const skillsDir = path.join(targetDir, ".claude", "skills");
173
165
  const commandsDir = path.join(targetDir, ".claude", "commands");
166
+ const agentsDir = path.join(targetDir, ".claude", "agents");
174
167
  if (await fs.pathExists(skillsDir)) {
175
168
  existingFiles.push(".claude/skills/");
176
169
  }
177
170
  if (await fs.pathExists(commandsDir)) {
178
171
  existingFiles.push(".claude/commands/");
179
172
  }
173
+ if (await fs.pathExists(agentsDir)) {
174
+ existingFiles.push(".claude/agents/");
175
+ }
180
176
  return existingFiles;
181
177
  };
178
+ var copyAgents = async (_templates, targetDir) => {
179
+ const counter = { files: 0, directories: 0 };
180
+ const targetAgentsDir = path.join(targetDir, ".claude", "agents");
181
+ const agentsSrc = path.join(getTemplatesDir(), ".claude", "agents");
182
+ if (await fs.pathExists(agentsSrc)) {
183
+ await fs.ensureDir(targetAgentsDir);
184
+ await copyRecursive(agentsSrc, targetAgentsDir, counter);
185
+ }
186
+ return counter;
187
+ };
188
+ var checkAllExtrasExist = async (_templates) => {
189
+ const claudeDir = path.join(getTemplatesDir(), ".claude");
190
+ const skillsSrc = path.join(claudeDir, "skills");
191
+ const commandsSrc = path.join(claudeDir, "commands");
192
+ const agentsSrc = path.join(claudeDir, "agents");
193
+ const hasSkills = await fs.pathExists(skillsSrc);
194
+ const hasCommands = await fs.pathExists(commandsSrc);
195
+ const hasAgents = await fs.pathExists(agentsSrc);
196
+ return { hasSkills, hasCommands, hasAgents };
197
+ };
182
198
 
183
199
  // src/commands/init.ts
184
200
  var TEMPLATE_DESCRIPTIONS = {
@@ -270,17 +286,19 @@ var init = async (options) => {
270
286
  }
271
287
  logger.blank();
272
288
  logger.success(`Total: ${totalFiles} files, ${totalDirectories} directories`);
273
- const { hasSkills, hasCommands } = await checkSkillsAndCommandsExist(templates);
289
+ const { hasSkills, hasCommands, hasAgents } = await checkAllExtrasExist(templates);
274
290
  let installSkills = options.skills ?? false;
275
291
  let installCommands = options.commands ?? false;
276
- if (!options.skills && !options.commands && (hasSkills || hasCommands)) {
292
+ let installAgents = options.agents ?? false;
293
+ const noOptionsProvided = options.skills === void 0 && options.commands === void 0 && options.agents === void 0;
294
+ if (noOptionsProvided && (hasSkills || hasCommands || hasAgents)) {
277
295
  logger.blank();
278
296
  if (hasSkills) {
279
297
  const skillsResponse = await prompts({
280
298
  type: "confirm",
281
299
  name: "install",
282
300
  message: "Install skills to .claude/skills/?",
283
- initial: true
301
+ initial: false
284
302
  });
285
303
  installSkills = skillsResponse.install ?? false;
286
304
  }
@@ -289,12 +307,21 @@ var init = async (options) => {
289
307
  type: "confirm",
290
308
  name: "install",
291
309
  message: "Install commands to .claude/commands/?",
292
- initial: true
310
+ initial: false
293
311
  });
294
312
  installCommands = commandsResponse.install ?? false;
295
313
  }
314
+ if (hasAgents) {
315
+ const agentsResponse = await prompts({
316
+ type: "confirm",
317
+ name: "install",
318
+ message: "Install agents to .claude/agents/?",
319
+ initial: false
320
+ });
321
+ installAgents = agentsResponse.install ?? false;
322
+ }
296
323
  }
297
- if (installSkills || installCommands) {
324
+ if (installSkills || installCommands || installAgents) {
298
325
  const existingClaudeFiles = await checkExistingClaudeFiles(targetDir);
299
326
  if (existingClaudeFiles.length > 0 && !options.force) {
300
327
  logger.warn("The following .claude files/folders already exist:");
@@ -307,9 +334,10 @@ var init = async (options) => {
307
334
  initial: false
308
335
  });
309
336
  if (!response.overwrite) {
310
- logger.info("Skipping skills/commands installation.");
337
+ logger.info("Skipping extras installation.");
311
338
  installSkills = false;
312
339
  installCommands = false;
340
+ installAgents = false;
313
341
  }
314
342
  }
315
343
  if (installSkills && hasSkills) {
@@ -336,13 +364,25 @@ var init = async (options) => {
336
364
  } else if (installCommands && !hasCommands) {
337
365
  logger.warn("No commands found in selected templates.");
338
366
  }
367
+ if (installAgents && hasAgents) {
368
+ logger.blank();
369
+ logger.info("Installing agents...");
370
+ const agentsResult = await copyAgents(templates, targetDir);
371
+ totalFiles += agentsResult.files;
372
+ totalDirectories += agentsResult.directories;
373
+ logger.success(
374
+ `Agents: ${agentsResult.files} files, ${agentsResult.directories} directories`
375
+ );
376
+ } else if (installAgents && !hasAgents) {
377
+ logger.warn("No agents found in selected templates.");
378
+ }
339
379
  }
340
380
  logger.blank();
341
381
  logger.success("Claude Code documentation installed!");
342
382
  logger.blank();
343
383
  logger.info("Installed templates:");
344
384
  templates.forEach((t) => logger.step(t));
345
- if (installSkills || installCommands) {
385
+ if (installSkills || installCommands || installAgents) {
346
386
  logger.blank();
347
387
  logger.info("Installed extras:");
348
388
  if (installSkills) {
@@ -351,6 +391,9 @@ var init = async (options) => {
351
391
  if (installCommands) {
352
392
  logger.step("Commands \u2192 .claude/commands/");
353
393
  }
394
+ if (installAgents) {
395
+ logger.step("Agents \u2192 .claude/agents/");
396
+ }
354
397
  }
355
398
  logger.blank();
356
399
  logger.info("Next steps:");
@@ -361,11 +404,11 @@ var init = async (options) => {
361
404
 
362
405
  // src/index.ts
363
406
  var program = new Command();
364
- program.name("claude-code").description("Claude Code documentation installer for projects").version("0.2.0");
407
+ program.name("claude-code").description("Claude Code documentation installer for projects").version("0.2.2");
365
408
  program.option(
366
409
  "-t, --template <names>",
367
410
  "template names (comma-separated: tanstack-start,hono)"
368
- ).option("-f, --force", "overwrite existing files without prompting").option("--cwd <path>", "target directory (default: current directory)").option("--list", "list available templates").option("-s, --skills", "install skills to .claude/skills/").option("-c, --commands", "install commands to .claude/commands/").action(async (options) => {
411
+ ).option("-f, --force", "overwrite existing files without prompting").option("--cwd <path>", "target directory (default: current directory)").option("--list", "list available templates").option("-s, --skills", "install skills to .claude/skills/").option("-c, --commands", "install commands to .claude/commands/").option("-a, --agents", "install agents to .claude/agents/").action(async (options) => {
369
412
  banner();
370
413
  if (options.list) {
371
414
  const templates = await listAvailableTemplates();
@@ -379,7 +422,8 @@ program.option(
379
422
  force: options.force,
380
423
  cwd: options.cwd,
381
424
  skills: options.skills,
382
- commands: options.commands
425
+ commands: options.commands,
426
+ agents: options.agents
383
427
  });
384
428
  });
385
429
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kood/claude-code",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Claude Code documentation installer for projects",
5
5
  "type": "module",
6
6
  "bin": "./dist/index.js",
@@ -0,0 +1,31 @@
1
+ ---
2
+ name: code-reviewer
3
+ description: 코드 리뷰. git diff 분석, 버그/보안/품질 이슈 탐지. "리뷰해줘", "코드 봐줘" 요청 시 사용.
4
+ model: inherit
5
+ color: red
6
+ tools: ["Read", "Grep", "Bash"]
7
+ ---
8
+
9
+ You are an expert code reviewer.
10
+
11
+ ## Responsibilities
12
+
13
+ 1. 버그 탐지 - 로직 오류, null/undefined, 레이스 컨디션
14
+ 2. 보안 검토 - 인젝션, XSS, 민감 데이터 노출
15
+ 3. 품질 확인 - 중복, 에러 핸들링, CLAUDE.md 규칙 준수
16
+
17
+ ## Process
18
+
19
+ 1. `git diff` 또는 지정 파일 분석
20
+ 2. 이슈 심각도 분류 (Critical/Important)
21
+ 3. 구체적 수정안 제시
22
+
23
+ ## Confidence
24
+
25
+ 80점 이상만 보고:
26
+ - 75+ : 확실한 이슈
27
+ - 100 : 반드시 수정
28
+
29
+ ## Output
30
+
31
+ Critical → Important 순으로 `[파일:라인]` + 이슈 + 수정안 제시.
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: debug-detective
3
+ description: 디버깅 전문가. 버그 원인 분석 및 수정안 제시. "에러", "버그", "안돼" 요청 시 사용.
4
+ model: inherit
5
+ color: yellow
6
+ tools: ["Read", "Grep", "Glob", "Bash"]
7
+ ---
8
+
9
+ You are a debugging specialist.
10
+
11
+ ## Responsibilities
12
+
13
+ 1. 에러 분석 - 스택 트레이스, 로그, 재현 조건
14
+ 2. 원인 추적 - 코드 흐름 따라 근본 원인 탐지
15
+ 3. 수정안 제시 - 최소 변경으로 해결
16
+
17
+ ## Process
18
+
19
+ 1. 증상 파악 (에러 메시지, 재현 단계)
20
+ 2. 가설 수립 (가능한 원인 목록)
21
+ 3. 코드 분석으로 가설 검증
22
+ 4. 근본 원인 수정
23
+
24
+ ## Common Patterns
25
+
26
+ | 패턴 | 증상 | 확인 |
27
+ |------|------|------|
28
+ | Null Reference | TypeError | 옵셔널 체이닝 |
29
+ | Race Condition | 간헐적 실패 | async/await |
30
+ | Off-by-One | 잘못된 인덱스 | 반복문 경계 |
31
+ | State Mutation | 예상외 변경 | 불변성 |
32
+
33
+ ## Rules
34
+
35
+ - 추측 금지, 코드 근거 필수
36
+ - 최소 변경 원칙
37
+ - Before/After 코드 제시
@@ -0,0 +1,44 @@
1
+ ---
2
+ name: refactor-advisor
3
+ description: 리팩토링 조언. 코드 개선점 분석 및 계획 수립. "리팩토링", "정리", "개선" 요청 시 사용.
4
+ model: inherit
5
+ color: cyan
6
+ tools: ["Read", "Grep", "Glob"]
7
+ ---
8
+
9
+ You are a refactoring specialist.
10
+
11
+ ## Responsibilities
12
+
13
+ 1. 코드 스멜 탐지 - 중복, 긴 함수, 복잡한 조건문
14
+ 2. 구조 개선 - 모듈화, 관심사 분리
15
+ 3. 안전한 계획 - 단계별 실행, 테스트 보장
16
+
17
+ ## Process
18
+
19
+ 1. 현재 코드 구조 파악
20
+ 2. 문제점/개선점 식별
21
+ 3. 우선순위 결정
22
+ 4. 단계별 계획 수립
23
+
24
+ ## Code Smells
25
+
26
+ | 카테고리 | 예시 |
27
+ |----------|------|
28
+ | Bloaters | 긴 메서드, 큰 클래스 |
29
+ | Couplers | 과도한 의존성 |
30
+ | Dispensables | 중복, 죽은 코드 |
31
+
32
+ ## Techniques
33
+
34
+ | 기법 | 상황 |
35
+ |------|------|
36
+ | Extract Function | 긴 함수, 중복 |
37
+ | Extract Component | 복잡한 UI |
38
+ | Replace Conditional | 복잡한 if/switch |
39
+
40
+ ## Rules
41
+
42
+ - 한 번에 하나의 리팩토링
43
+ - 각 단계 후 테스트
44
+ - 기능 변경 없이 구조만 개선
@@ -0,0 +1,41 @@
1
+ ---
2
+ name: test-writer
3
+ description: 테스트 작성. 함수/컴포넌트 테스트 생성. "테스트 추가", "테스트 작성" 요청 시 사용.
4
+ model: inherit
5
+ color: green
6
+ tools: ["Read", "Write", "Grep", "Glob"]
7
+ ---
8
+
9
+ You are a testing specialist.
10
+
11
+ ## Responsibilities
12
+
13
+ 1. 유닛 테스트 - 함수, 클래스 단위
14
+ 2. 통합 테스트 - API, 서비스 상호작용
15
+ 3. 엣지 케이스 - 경계값, 에러, 빈 입력
16
+
17
+ ## Process
18
+
19
+ 1. 대상 코드 분석 (입력/출력, 의존성)
20
+ 2. 테스트 케이스 도출 (정상, 엣지, 에러)
21
+ 3. 프로젝트 테스트 패턴 확인
22
+ 4. 테스트 코드 작성
23
+
24
+ ## Coverage Priority
25
+
26
+ | 우선순위 | 케이스 |
27
+ |----------|--------|
28
+ | 필수 | happy path |
29
+ | 필수 | 에러 핸들링 |
30
+ | 권장 | 경계값 |
31
+ | 권장 | null/undefined |
32
+
33
+ ## Structure
34
+
35
+ ```typescript
36
+ describe('[대상]', () => {
37
+ it('should [동작] when [조건]', () => {
38
+ // Arrange → Act → Assert
39
+ });
40
+ });
41
+ ```
@@ -0,0 +1,310 @@
1
+ ---
2
+ name: frontend-design
3
+ description: 프론트엔드 UI 구현 스킬. 컴포넌트, 페이지, 애플리케이션 구축 시 사용. 2026 트렌드 기반 고품질 디자인 생성.
4
+ license: Complete terms in LICENSE.txt
5
+ ---
6
+
7
+ # Frontend Design Skill
8
+
9
+ 프로덕션급 프론트엔드 인터페이스 구현. 독창적이고 기억에 남는 디자인.
10
+
11
+ ## 사용 시점
12
+
13
+ | 트리거 | 예시 |
14
+ |--------|------|
15
+ | UI 컴포넌트 요청 | "버튼 컴포넌트 만들어줘" |
16
+ | 페이지 구현 | "랜딩 페이지 만들어줘" |
17
+ | 스타일링 작업 | "다크 테마 적용해줘" |
18
+ | 애니메이션 추가 | "hover 효과 넣어줘" |
19
+
20
+ ## 디자인 프로세스
21
+
22
+ ### 1단계: 컨텍스트 파악
23
+
24
+ ```
25
+ - 목적: 무슨 문제 해결?
26
+ - 대상: 누가 사용?
27
+ - 제약: 프레임워크, 성능, 접근성 요구사항
28
+ ```
29
+
30
+ ### 2단계: 미적 방향 결정
31
+
32
+ **반드시 하나 선택하고 일관성 유지:**
33
+
34
+ | 방향 | 특징 | 적합한 상황 |
35
+ |------|------|-------------|
36
+ | Liquid Glass | 투명도, 깊이, 유동적 표면 | 모던 앱, 대시보드 |
37
+ | Calm Minimal | 여백, 선명한 타이포, 절제 | 콘텐츠 중심, 포트폴리오 |
38
+ | Bold Maximalist | 큰 타이포, 강렬한 색, 레이어 | 크리에이티브, 브랜드 |
39
+ | Organic Natural | 부드러운 곡선, 어시 톤, 텍스처 | 웰빙, 라이프스타일 |
40
+ | Editorial | 매거진 레이아웃, 그리드 플레이 | 미디어, 블로그 |
41
+ | Retro Futuristic | 네온, 그라디언트, 글리치 | 테크, 게임 |
42
+
43
+ ### 3단계: 구현
44
+
45
+ ```
46
+ 1. CSS 변수로 디자인 토큰 정의
47
+ 2. 타이포그래피 시스템 설정
48
+ 3. 컬러 팔레트 적용
49
+ 4. 레이아웃 구조화
50
+ 5. 애니메이션/인터랙션 추가
51
+ 6. 접근성 검증
52
+ ```
53
+
54
+ ## 핵심 규칙
55
+
56
+ ### 타이포그래피
57
+
58
+ **DO:**
59
+ ```css
60
+ /* Variable fonts 사용 */
61
+ @font-face {
62
+ font-family: 'Display';
63
+ src: url('font.woff2') format('woff2');
64
+ font-weight: 100 900;
65
+ font-display: swap;
66
+ }
67
+
68
+ /* 명확한 위계 */
69
+ --font-display: 'Playfair Display', serif; /* 헤드라인 */
70
+ --font-body: 'Source Sans 3', sans-serif; /* 본문 */
71
+ --font-mono: 'JetBrains Mono', monospace; /* 코드 */
72
+ ```
73
+
74
+ **DON'T:**
75
+ ```css
76
+ /* ❌ 금지 - 일반적인 시스템 폰트 */
77
+ font-family: Arial, Helvetica, sans-serif;
78
+ font-family: -apple-system, BlinkMacSystemFont, sans-serif;
79
+
80
+ /* ❌ 금지 - AI 슬롭 폰트 */
81
+ font-family: 'Inter', sans-serif;
82
+ font-family: 'Roboto', sans-serif;
83
+ font-family: 'Space Grotesk', sans-serif;
84
+ ```
85
+
86
+ **추천 폰트 페어링:**
87
+
88
+ | 헤드라인 | 본문 | 무드 |
89
+ |----------|------|------|
90
+ | Playfair Display | Source Sans 3 | 클래식, 에디토리얼 |
91
+ | Clash Display | Satoshi | 모던, 볼드 |
92
+ | Fraunces | Work Sans | 따뜻한, 친근한 |
93
+ | Space Mono | DM Sans | 테크, 미니멀 |
94
+ | Syne | Inter (예외적 허용) | 크리에이티브 |
95
+
96
+ **상세**: [references/typography-2026.md](references/typography-2026.md)
97
+
98
+ ### 컬러
99
+
100
+ **DO:**
101
+ ```css
102
+ /* OKLCH 컬러 시스템 (2026 표준) */
103
+ :root {
104
+ /* 베이스 - 소프트 뉴트럴 (순백색 X) */
105
+ --bg-primary: oklch(98% 0.005 90); /* 웜 오프화이트 */
106
+ --bg-secondary: oklch(95% 0.01 90); /* 린넨 */
107
+
108
+ /* 텍스트 */
109
+ --text-primary: oklch(20% 0.01 90); /* 소프트 블랙 */
110
+ --text-secondary: oklch(45% 0.02 90); /* 뮤트 그레이 */
111
+
112
+ /* 액센트 - 하나만 강하게 */
113
+ --accent: oklch(65% 0.25 180); /* Transformative Teal */
114
+ --accent-hover: oklch(60% 0.28 180);
115
+
116
+ /* 상태 */
117
+ --success: oklch(70% 0.2 145);
118
+ --error: oklch(65% 0.25 25);
119
+ }
120
+
121
+ /* 다크 모드 */
122
+ @media (prefers-color-scheme: dark) {
123
+ :root {
124
+ --bg-primary: oklch(15% 0.01 90);
125
+ --bg-secondary: oklch(20% 0.015 90);
126
+ --text-primary: oklch(90% 0.01 90);
127
+ }
128
+ }
129
+ ```
130
+
131
+ **DON'T:**
132
+ ```css
133
+ /* ❌ 금지 - 순백색 배경 */
134
+ background: #ffffff;
135
+ background: white;
136
+
137
+ /* ❌ 금지 - AI 슬롭 퍼플 그라디언트 */
138
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
139
+ background: linear-gradient(to right, #8b5cf6, #a855f7);
140
+ ```
141
+
142
+ **2026 트렌드 컬러:**
143
+
144
+ | 이름 | OKLCH | HEX (참고) | 용도 |
145
+ |------|-------|-----------|------|
146
+ | Cloud Dancer | oklch(97% 0.005 90) | #F5F5F5 | 배경, 여백 |
147
+ | Mocha Mousse | oklch(55% 0.08 55) | #A47764 | 어시, 따뜻함 |
148
+ | Transformative Teal | oklch(65% 0.15 180) | #2D9CCA | 액센트 |
149
+ | Neo Mint | oklch(85% 0.1 160) | #AAF0D1 | 프레시, 테크 |
150
+ | Soft Coral | oklch(75% 0.12 30) | #FFB5A7 | 따뜻한 액센트 |
151
+
152
+ **상세**: [references/colors-2026.md](references/colors-2026.md)
153
+
154
+ ### 애니메이션
155
+
156
+ **원칙:**
157
+ 1. **목적 있는 움직임만** - 장식적 애니메이션 금지
158
+ 2. **300ms 이하** - 트랜지션은 빠르게
159
+ 3. **GPU 가속** - transform, opacity만 애니메이트
160
+ 4. **접근성** - prefers-reduced-motion 존중
161
+
162
+ **DO:**
163
+ ```css
164
+ /* 기본 트랜지션 */
165
+ .button {
166
+ transition: transform 200ms ease-out,
167
+ background-color 150ms ease;
168
+ }
169
+ .button:hover {
170
+ transform: translateY(-2px);
171
+ }
172
+
173
+ /* 스태거 애니메이션 */
174
+ .list-item {
175
+ animation: fadeInUp 400ms ease-out backwards;
176
+ }
177
+ .list-item:nth-child(1) { animation-delay: 0ms; }
178
+ .list-item:nth-child(2) { animation-delay: 50ms; }
179
+ .list-item:nth-child(3) { animation-delay: 100ms; }
180
+
181
+ @keyframes fadeInUp {
182
+ from {
183
+ opacity: 0;
184
+ transform: translateY(20px);
185
+ }
186
+ to {
187
+ opacity: 1;
188
+ transform: translateY(0);
189
+ }
190
+ }
191
+
192
+ /* 접근성 */
193
+ @media (prefers-reduced-motion: reduce) {
194
+ *, *::before, *::after {
195
+ animation-duration: 0.01ms !important;
196
+ transition-duration: 0.01ms !important;
197
+ }
198
+ }
199
+ ```
200
+
201
+ **DON'T:**
202
+ ```css
203
+ /* ❌ 금지 - 레이아웃 속성 애니메이트 */
204
+ transition: width 300ms, height 300ms, margin 300ms;
205
+
206
+ /* ❌ 금지 - 너무 느린 트랜지션 */
207
+ transition: all 1s ease;
208
+
209
+ /* ❌ 금지 - 무한 회전 (목적 없는 장식) */
210
+ animation: spin 2s infinite linear;
211
+ ```
212
+
213
+ **React (Motion 라이브러리):**
214
+ ```tsx
215
+ import { motion } from 'motion/react';
216
+
217
+ // 페이지 진입
218
+ <motion.div
219
+ initial={{ opacity: 0, y: 20 }}
220
+ animate={{ opacity: 1, y: 0 }}
221
+ transition={{ duration: 0.3, ease: 'easeOut' }}
222
+ >
223
+ {content}
224
+ </motion.div>
225
+
226
+ // 리스트 스태거
227
+ <motion.ul>
228
+ {items.map((item, i) => (
229
+ <motion.li
230
+ key={item.id}
231
+ initial={{ opacity: 0, x: -20 }}
232
+ animate={{ opacity: 1, x: 0 }}
233
+ transition={{ delay: i * 0.05 }}
234
+ >
235
+ {item.name}
236
+ </motion.li>
237
+ ))}
238
+ </motion.ul>
239
+ ```
240
+
241
+ **상세**: [references/animation-patterns.md](references/animation-patterns.md)
242
+
243
+ ### 레이아웃
244
+
245
+ **DO:**
246
+ ```css
247
+ /* 비대칭, 그리드 브레이킹 */
248
+ .hero {
249
+ display: grid;
250
+ grid-template-columns: 1fr 1.5fr;
251
+ gap: clamp(2rem, 5vw, 4rem);
252
+ }
253
+
254
+ /* 여백은 넉넉하게 */
255
+ section {
256
+ padding: clamp(4rem, 10vh, 8rem) 0;
257
+ }
258
+
259
+ /* Container queries (2026 표준) */
260
+ @container (min-width: 400px) {
261
+ .card { flex-direction: row; }
262
+ }
263
+ ```
264
+
265
+ **DON'T:**
266
+ ```css
267
+ /* ❌ 금지 - 완벽한 대칭 (지루함) */
268
+ grid-template-columns: 1fr 1fr;
269
+
270
+ /* ❌ 금지 - 빽빽한 레이아웃 */
271
+ padding: 10px;
272
+ gap: 8px;
273
+ ```
274
+
275
+ ## 접근성 체크리스트
276
+
277
+ | 항목 | 기준 | 확인 |
278
+ |------|------|------|
279
+ | 색상 대비 | WCAG AA (4.5:1 텍스트, 3:1 UI) | [ ] |
280
+ | 키보드 네비게이션 | 모든 인터랙티브 요소 접근 가능 | [ ] |
281
+ | 포커스 표시 | 명확한 포커스 링 | [ ] |
282
+ | 모션 감소 | prefers-reduced-motion 처리 | [ ] |
283
+ | 스크린 리더 | 시맨틱 HTML, aria-label | [ ] |
284
+ | 텍스트 크기 | 16px 이상 본문 | [ ] |
285
+ | 터치 타겟 | 44x44px 이상 | [ ] |
286
+
287
+ ## 금지 사항 (Anti-patterns)
288
+
289
+ | 카테고리 | 금지 항목 |
290
+ |----------|----------|
291
+ | 폰트 | Inter, Roboto, Arial, system fonts 단독 사용 |
292
+ | 컬러 | 퍼플 그라디언트 on 화이트, 순백색 배경 |
293
+ | 레이아웃 | 완벽 대칭, 좁은 여백, 쿠키커터 카드 |
294
+ | 애니메이션 | 목적 없는 회전, 1초+ 트랜지션, layout 애니메이트 |
295
+ | 패턴 | 동일한 그림자, 동일한 border-radius, 복사된 UI |
296
+
297
+ ## 참조 문서
298
+
299
+ - [references/typography-2026.md](references/typography-2026.md) - 폰트 선택, 스케일, 페어링
300
+ - [references/colors-2026.md](references/colors-2026.md) - OKLCH, 팔레트, 다크모드
301
+ - [references/animation-patterns.md](references/animation-patterns.md) - CSS/JS 패턴, 라이브러리
302
+
303
+ ## Sources
304
+
305
+ - [UI Trends 2026 - UX Studio Team](https://www.uxstudioteam.com/ux-blog/ui-trends-2019)
306
+ - [12 UI/UX Design Trends - Index.dev](https://www.index.dev/blog/ui-ux-design-trends)
307
+ - [Typography Trends 2026 - Wannathis](https://wannathis.one/blog/top-typography-trends-2026-for-designers)
308
+ - [Color Trends 2026 - Lounge Lizard](https://www.loungelizard.com/blog/web-design-color-trends/)
309
+ - [Motion UI Trends 2026 - Loma Technology](https://lomatechnology.com/blog/motion-ui-trends-2026/2911)
310
+ - [Motion Library](https://motion.dev/)