moai-adk 0.9.0__py3-none-any.whl → 0.15.1__py3-none-any.whl
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.
Potentially problematic release.
This version of moai-adk might be problematic. Click here for more details.
- moai_adk/cli/commands/init.py +14 -2
- moai_adk/cli/commands/update.py +214 -56
- moai_adk/core/issue_creator.py +2 -2
- moai_adk/core/project/detector.py +201 -12
- moai_adk/core/project/initializer.py +62 -1
- moai_adk/core/project/phase_executor.py +48 -6
- moai_adk/core/tags/ci_validator.py +34 -4
- moai_adk/core/tags/pre_commit_validator.py +40 -2
- moai_adk/core/tags/reporter.py +2 -3
- moai_adk/core/tags/validator.py +1 -1
- moai_adk/core/template_engine.py +20 -5
- moai_adk/templates/.claude/agents/alfred/backend-expert.md +319 -0
- moai_adk/templates/.claude/agents/alfred/devops-expert.md +464 -0
- moai_adk/templates/.claude/agents/alfred/doc-syncer.md +1 -1
- moai_adk/templates/.claude/agents/alfred/frontend-expert.md +357 -0
- moai_adk/templates/.claude/agents/alfred/git-manager.md +2 -2
- moai_adk/templates/.claude/agents/alfred/implementation-planner.md +76 -3
- moai_adk/templates/.claude/agents/alfred/project-manager.md +49 -10
- moai_adk/templates/.claude/agents/alfred/quality-gate.md +3 -3
- moai_adk/templates/.claude/agents/alfred/spec-builder.md +180 -41
- moai_adk/templates/.claude/agents/alfred/tag-agent.md +74 -0
- moai_adk/templates/.claude/agents/alfred/tdd-implementer.md +107 -5
- moai_adk/templates/.claude/agents/alfred/trust-checker.md +2 -2
- moai_adk/templates/.claude/agents/alfred/ui-ux-expert.md +571 -0
- moai_adk/templates/.claude/commands/alfred/0-project.md +928 -263
- moai_adk/templates/.claude/commands/alfred/1-plan.md +220 -68
- moai_adk/templates/.claude/commands/alfred/2-run.md +299 -51
- moai_adk/templates/.claude/commands/alfred/3-sync.md +452 -51
- moai_adk/templates/.claude/commands/alfred/9-feedback.md +1 -1
- moai_adk/templates/.claude/hooks/alfred/core/project.py +25 -27
- moai_adk/templates/.claude/hooks/alfred/core/timeout.py +136 -0
- moai_adk/templates/.claude/hooks/alfred/core/ttl_cache.py +108 -0
- moai_adk/templates/.claude/hooks/alfred/core/version_cache.py +4 -4
- moai_adk/templates/.claude/hooks/alfred/handlers/__init__.py +29 -0
- moai_adk/templates/.claude/hooks/alfred/post_tool__log_changes.py +11 -19
- moai_adk/templates/.claude/hooks/alfred/pre_tool__auto_checkpoint.py +11 -19
- moai_adk/templates/.claude/hooks/alfred/session_end__cleanup.py +11 -19
- moai_adk/templates/.claude/hooks/alfred/session_start__show_project_info.py +10 -18
- moai_adk/templates/.claude/hooks/alfred/shared/core/__init__.py +2 -2
- moai_adk/templates/.claude/hooks/alfred/shared/core/checkpoint.py +3 -3
- moai_adk/templates/.claude/hooks/alfred/shared/core/context.py +5 -5
- moai_adk/templates/.claude/hooks/alfred/shared/core/project.py +40 -41
- moai_adk/templates/.claude/hooks/alfred/shared/core/tags.py +55 -23
- moai_adk/templates/.claude/hooks/alfred/shared/core/version_cache.py +4 -4
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/notification.py +132 -3
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/session.py +9 -10
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/tool.py +3 -6
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/user.py +19 -0
- moai_adk/templates/.claude/hooks/alfred/user_prompt__jit_load_docs.py +14 -22
- moai_adk/templates/.claude/hooks/alfred/utils/__init__.py +1 -0
- moai_adk/templates/.claude/hooks/alfred/utils/timeout.py +161 -0
- moai_adk/templates/.claude/settings.json +5 -5
- moai_adk/templates/.claude/skills/moai-alfred-agent-guide/SKILL.md +70 -0
- moai_adk/templates/.claude/skills/moai-alfred-agent-guide/examples.md +62 -0
- moai_adk/templates/{.moai/memory/CLAUDE-AGENTS-GUIDE.md → .claude/skills/moai-alfred-agent-guide/reference.md} +34 -0
- moai_adk/templates/.claude/skills/moai-alfred-config-schema/SKILL.md +56 -0
- moai_adk/templates/.claude/skills/moai-alfred-config-schema/examples.md +28 -0
- moai_adk/templates/.claude/skills/moai-alfred-config-schema/reference.md +444 -0
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/SKILL.md +62 -0
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/examples.md +28 -0
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/reference.md +405 -0
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/SKILL.md +51 -0
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/examples.md +355 -0
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/reference.md +239 -0
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/SKILL.md +323 -0
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/examples.md +286 -0
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/reference.md +126 -0
- moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/SKILL.md +74 -0
- moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/reference.md +269 -0
- moai_adk/templates/.claude/skills/moai-alfred-issue-labels/SKILL.md +19 -0
- moai_adk/templates/.claude/skills/moai-alfred-issue-labels/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-persona-roles/SKILL.md +198 -0
- moai_adk/templates/.claude/skills/moai-alfred-persona-roles/examples.md +431 -0
- moai_adk/templates/.claude/skills/moai-alfred-persona-roles/reference.md +141 -0
- moai_adk/templates/.claude/skills/moai-alfred-practices/SKILL.md +89 -0
- moai_adk/templates/.claude/skills/moai-alfred-practices/examples.md +122 -0
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/SKILL.md +508 -0
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/examples.md +481 -0
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/reference.md +100 -0
- moai_adk/templates/.claude/skills/moai-alfred-reporting/SKILL.md +273 -0
- moai_adk/templates/.claude/skills/moai-alfred-rules/SKILL.md +77 -0
- moai_adk/templates/.claude/skills/moai-alfred-rules/examples.md +265 -0
- moai_adk/templates/.claude/skills/moai-alfred-session-state/SKILL.md +19 -0
- moai_adk/templates/.claude/skills/moai-alfred-session-state/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-session-state/reference.md +84 -0
- moai_adk/templates/.claude/skills/{moai-spec-authoring → moai-alfred-spec-authoring}/SKILL.md +5 -5
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/SKILL.md +115 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/reference.md +348 -0
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/SKILL.md +19 -0
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/reference.md +211 -0
- moai_adk/templates/.claude/skills/moai-alfred-workflow/SKILL.md +288 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-descriptions/SKILL.md +19 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-descriptions/examples.md +4 -0
- moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/SKILL.md +3 -3
- moai_adk/templates/.claude/skills/moai-design-systems/SKILL.md +802 -0
- moai_adk/templates/.claude/skills/moai-design-systems/examples.md +1238 -0
- moai_adk/templates/.claude/skills/moai-design-systems/reference.md +673 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/SKILL.md +17 -13
- moai_adk/templates/.claude/skills/moai-lang-go/SKILL.md +15 -12
- moai_adk/templates/.claude/skills/moai-lang-java/SKILL.md +14 -12
- moai_adk/templates/.claude/skills/moai-lang-php/SKILL.md +14 -11
- moai_adk/templates/.claude/skills/moai-lang-python/SKILL.md +10 -8
- moai_adk/templates/.claude/skills/moai-lang-rust/SKILL.md +15 -12
- moai_adk/templates/.claude/skills/moai-lang-scala/SKILL.md +13 -11
- moai_adk/templates/.claude/skills/moai-lang-typescript/SKILL.md +16 -10
- moai_adk/templates/.claude/skills/moai-project-documentation.md +622 -0
- moai_adk/templates/.git-hooks/pre-push +143 -0
- moai_adk/templates/.github/workflows/c-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/cpp-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/csharp-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/dart-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/go-tag-validation.yml +130 -0
- moai_adk/templates/.github/workflows/java-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/javascript-tag-validation.yml +135 -0
- moai_adk/templates/.github/workflows/kotlin-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/moai-gitflow.yml +182 -25
- moai_adk/templates/.github/workflows/moai-release-pipeline.yml +35 -29
- moai_adk/templates/.github/workflows/php-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/python-tag-validation.yml +118 -0
- moai_adk/templates/.github/workflows/release.yml +76 -7
- moai_adk/templates/.github/workflows/ruby-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/rust-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/shell-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/spec-issue-sync.yml +208 -41
- moai_adk/templates/.github/workflows/swift-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/tag-report.yml +269 -0
- moai_adk/templates/.github/workflows/tag-validation.yml +186 -0
- moai_adk/templates/.github/workflows/typescript-tag-validation.yml +154 -0
- moai_adk/templates/.moai/config.json +3 -1
- moai_adk/templates/CLAUDE.md +940 -45
- moai_adk/templates/workflows/go-tag-validation.yml +30 -0
- moai_adk/templates/workflows/javascript-tag-validation.yml +41 -0
- moai_adk/templates/workflows/python-tag-validation.yml +42 -0
- moai_adk/templates/workflows/typescript-tag-validation.yml +31 -0
- moai_adk/utils/banner.py +5 -5
- {moai_adk-0.9.0.dist-info → moai_adk-0.15.1.dist-info}/METADATA +1253 -527
- {moai_adk-0.9.0.dist-info → moai_adk-0.15.1.dist-info}/RECORD +169 -109
- moai_adk/templates/.claude/hooks/alfred/alfred_hooks.py +0 -209
- moai_adk/templates/.claude/hooks/alfred/notification__handle_events.py +0 -102
- moai_adk/templates/.claude/hooks/alfred/stop__handle_interrupt.py +0 -102
- moai_adk/templates/.claude/hooks/alfred/subagent_stop__handle_subagent_end.py +0 -102
- moai_adk/templates/.claude/output-styles/alfred/agentic-coding.md +0 -640
- moai_adk/templates/.claude/output-styles/alfred/moai-adk-learning.md +0 -696
- moai_adk/templates/.claude/output-styles/alfred/study-with-alfred.md +0 -474
- moai_adk/templates/.github/ISSUE_TEMPLATE/spec.yml +0 -176
- moai_adk/templates/.github/PULL_REQUEST_TEMPLATE.md +0 -69
- moai_adk/templates/.moai/memory/DEVELOPMENT-GUIDE.md +0 -344
- moai_adk/templates/.moai/memory/SPEC-METADATA.md +0 -356
- moai_adk/templates/.moai/memory/gitflow-protection-policy.md +0 -330
- moai_adk/templates/.moai/project/product.md +0 -161
- moai_adk/templates/.moai/project/structure.md +0 -156
- moai_adk/templates/.moai/project/tech.md +0 -227
- moai_adk/templates/README.md +0 -256
- moai_adk/templates/__init__.py +0 -2
- /moai_adk/templates/{.moai/memory/ISSUE-LABEL-MAPPING.md → .claude/skills/moai-alfred-issue-labels/reference.md} +0 -0
- /moai_adk/templates/{.moai/memory/CLAUDE-PRACTICES.md → .claude/skills/moai-alfred-practices/reference.md} +0 -0
- /moai_adk/templates/{.moai/memory/CLAUDE-RULES.md → .claude/skills/moai-alfred-rules/reference.md} +0 -0
- /moai_adk/templates/.claude/skills/{moai-spec-authoring → moai-alfred-spec-authoring}/README.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-spec-authoring → moai-alfred-spec-authoring}/examples/validate-spec.sh +0 -0
- /moai_adk/templates/.claude/skills/{moai-spec-authoring → moai-alfred-spec-authoring}/examples.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-spec-authoring → moai-alfred-spec-authoring}/reference.md +0 -0
- /moai_adk/templates/{.moai/memory/SKILLS-DESCRIPTION-POLICY.md → .claude/skills/moai-cc-skill-descriptions/reference.md} +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/CHECKLIST.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/EXAMPLES.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/INTERACTIVE-DISCOVERY.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/METADATA.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/PARALLEL-ANALYSIS-REPORT.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/PYTHON-VERSION-MATRIX.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/SKILL-FACTORY-WORKFLOW.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/SKILL-UPDATE-ADVISOR.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/STEP-BY-STEP-GUIDE.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/STRUCTURE.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/WEB-RESEARCH.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/reference.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/scripts/generate-structure.sh +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/scripts/validate-skill.sh +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/SKILL_TEMPLATE.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/examples-template.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/reference-template.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/scripts-template.sh +0 -0
- {moai_adk-0.9.0.dist-info → moai_adk-0.15.1.dist-info}/WHEEL +0 -0
- {moai_adk-0.9.0.dist-info → moai_adk-0.15.1.dist-info}/entry_points.txt +0 -0
- {moai_adk-0.9.0.dist-info → moai_adk-0.15.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,1238 @@
|
|
|
1
|
+
# Design Systems: Practical Examples
|
|
2
|
+
|
|
3
|
+
Real-world code examples for implementing design systems with DTCG 2025.10 tokens, WCAG 2.2 accessibility, and Figma MCP workflows.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Example 1: Complete Design Token Setup (DTCG 2025.10)
|
|
8
|
+
|
|
9
|
+
### Color Tokens
|
|
10
|
+
|
|
11
|
+
**File**: `tokens/color.json`
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"$schema": "https://tr.designtokens.org/format/",
|
|
16
|
+
"$tokens": {
|
|
17
|
+
"color": {
|
|
18
|
+
"$type": "color",
|
|
19
|
+
"gray": {
|
|
20
|
+
"50": { "$value": "#f9fafb" },
|
|
21
|
+
"100": { "$value": "#f3f4f6" },
|
|
22
|
+
"200": { "$value": "#e5e7eb" },
|
|
23
|
+
"300": { "$value": "#d1d5db" },
|
|
24
|
+
"400": { "$value": "#9ca3af" },
|
|
25
|
+
"500": { "$value": "#6b7280" },
|
|
26
|
+
"600": { "$value": "#4b5563" },
|
|
27
|
+
"700": { "$value": "#374151" },
|
|
28
|
+
"800": { "$value": "#1f2937" },
|
|
29
|
+
"900": { "$value": "#111827" }
|
|
30
|
+
},
|
|
31
|
+
"primary": {
|
|
32
|
+
"50": { "$value": "#eff6ff" },
|
|
33
|
+
"100": { "$value": "#dbeafe" },
|
|
34
|
+
"200": { "$value": "#bfdbfe" },
|
|
35
|
+
"300": { "$value": "#93c5fd" },
|
|
36
|
+
"400": { "$value": "#60a5fa" },
|
|
37
|
+
"500": { "$value": "#3b82f6" },
|
|
38
|
+
"600": { "$value": "#2563eb" },
|
|
39
|
+
"700": { "$value": "#1d4ed8" },
|
|
40
|
+
"800": { "$value": "#1e40af" },
|
|
41
|
+
"900": { "$value": "#1e3a8a" }
|
|
42
|
+
},
|
|
43
|
+
"semantic": {
|
|
44
|
+
"text": {
|
|
45
|
+
"primary": {
|
|
46
|
+
"$value": "{color.gray.900}",
|
|
47
|
+
"$description": "Main body text color",
|
|
48
|
+
"$extensions": {
|
|
49
|
+
"mode": {
|
|
50
|
+
"dark": "{color.gray.50}"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"secondary": {
|
|
55
|
+
"$value": "{color.gray.600}",
|
|
56
|
+
"$description": "Supporting text color",
|
|
57
|
+
"$extensions": {
|
|
58
|
+
"mode": {
|
|
59
|
+
"dark": "{color.gray.400}"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"disabled": {
|
|
64
|
+
"$value": "{color.gray.400}",
|
|
65
|
+
"$description": "Disabled text color",
|
|
66
|
+
"$extensions": {
|
|
67
|
+
"mode": {
|
|
68
|
+
"dark": "{color.gray.600}"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"background": {
|
|
74
|
+
"default": {
|
|
75
|
+
"$value": "#ffffff",
|
|
76
|
+
"$description": "Default page background",
|
|
77
|
+
"$extensions": {
|
|
78
|
+
"mode": {
|
|
79
|
+
"dark": "{color.gray.900}"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"elevated": {
|
|
84
|
+
"$value": "{color.gray.50}",
|
|
85
|
+
"$description": "Card and elevated surfaces",
|
|
86
|
+
"$extensions": {
|
|
87
|
+
"mode": {
|
|
88
|
+
"dark": "{color.gray.800}"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"action": {
|
|
94
|
+
"primary": {
|
|
95
|
+
"$value": "{color.primary.500}",
|
|
96
|
+
"$description": "Primary action color (buttons, links)"
|
|
97
|
+
},
|
|
98
|
+
"primaryHover": {
|
|
99
|
+
"$value": "{color.primary.600}",
|
|
100
|
+
"$description": "Primary action hover state"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Typography Tokens
|
|
110
|
+
|
|
111
|
+
**File**: `tokens/typography.json`
|
|
112
|
+
|
|
113
|
+
```json
|
|
114
|
+
{
|
|
115
|
+
"$schema": "https://tr.designtokens.org/format/",
|
|
116
|
+
"$tokens": {
|
|
117
|
+
"font": {
|
|
118
|
+
"family": {
|
|
119
|
+
"$type": "fontFamily",
|
|
120
|
+
"sans": {
|
|
121
|
+
"$value": ["Inter", "-apple-system", "BlinkMacSystemFont", "Segoe UI", "Roboto", "sans-serif"],
|
|
122
|
+
"$description": "Primary sans-serif font stack"
|
|
123
|
+
},
|
|
124
|
+
"mono": {
|
|
125
|
+
"$value": ["JetBrains Mono", "Menlo", "Monaco", "Courier New", "monospace"],
|
|
126
|
+
"$description": "Monospace font stack for code"
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
"size": {
|
|
130
|
+
"$type": "dimension",
|
|
131
|
+
"xs": { "$value": "0.75rem", "$description": "12px" },
|
|
132
|
+
"sm": { "$value": "0.875rem", "$description": "14px" },
|
|
133
|
+
"base": { "$value": "1rem", "$description": "16px" },
|
|
134
|
+
"lg": { "$value": "1.125rem", "$description": "18px" },
|
|
135
|
+
"xl": { "$value": "1.25rem", "$description": "20px" },
|
|
136
|
+
"2xl": { "$value": "1.5rem", "$description": "24px" },
|
|
137
|
+
"3xl": { "$value": "1.875rem", "$description": "30px" },
|
|
138
|
+
"4xl": { "$value": "2.25rem", "$description": "36px" }
|
|
139
|
+
},
|
|
140
|
+
"weight": {
|
|
141
|
+
"$type": "fontWeight",
|
|
142
|
+
"normal": { "$value": "400" },
|
|
143
|
+
"medium": { "$value": "500" },
|
|
144
|
+
"semibold": { "$value": "600" },
|
|
145
|
+
"bold": { "$value": "700" }
|
|
146
|
+
},
|
|
147
|
+
"lineHeight": {
|
|
148
|
+
"$type": "number",
|
|
149
|
+
"tight": { "$value": "1.25" },
|
|
150
|
+
"normal": { "$value": "1.5" },
|
|
151
|
+
"relaxed": { "$value": "1.75" }
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Spacing Tokens
|
|
159
|
+
|
|
160
|
+
**File**: `tokens/spacing.json`
|
|
161
|
+
|
|
162
|
+
```json
|
|
163
|
+
{
|
|
164
|
+
"$schema": "https://tr.designtokens.org/format/",
|
|
165
|
+
"$tokens": {
|
|
166
|
+
"spacing": {
|
|
167
|
+
"$type": "dimension",
|
|
168
|
+
"0": { "$value": "0" },
|
|
169
|
+
"1": { "$value": "0.25rem", "$description": "4px" },
|
|
170
|
+
"2": { "$value": "0.5rem", "$description": "8px" },
|
|
171
|
+
"3": { "$value": "0.75rem", "$description": "12px" },
|
|
172
|
+
"4": { "$value": "1rem", "$description": "16px" },
|
|
173
|
+
"5": { "$value": "1.25rem", "$description": "20px" },
|
|
174
|
+
"6": { "$value": "1.5rem", "$description": "24px" },
|
|
175
|
+
"8": { "$value": "2rem", "$description": "32px" },
|
|
176
|
+
"10": { "$value": "2.5rem", "$description": "40px" },
|
|
177
|
+
"12": { "$value": "3rem", "$description": "48px" },
|
|
178
|
+
"16": { "$value": "4rem", "$description": "64px" }
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Style Dictionary Build Configuration
|
|
185
|
+
|
|
186
|
+
**File**: `style-dictionary.config.js`
|
|
187
|
+
|
|
188
|
+
```javascript
|
|
189
|
+
export default {
|
|
190
|
+
source: ['tokens/**/*.json'],
|
|
191
|
+
|
|
192
|
+
platforms: {
|
|
193
|
+
// CSS Variables
|
|
194
|
+
css: {
|
|
195
|
+
transformGroup: 'css',
|
|
196
|
+
buildPath: 'build/css/',
|
|
197
|
+
files: [{
|
|
198
|
+
destination: 'variables.css',
|
|
199
|
+
format: 'css/variables',
|
|
200
|
+
options: {
|
|
201
|
+
outputReferences: true
|
|
202
|
+
}
|
|
203
|
+
}]
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
// JavaScript/TypeScript
|
|
207
|
+
js: {
|
|
208
|
+
transformGroup: 'js',
|
|
209
|
+
buildPath: 'build/js/',
|
|
210
|
+
files: [{
|
|
211
|
+
destination: 'tokens.js',
|
|
212
|
+
format: 'javascript/es6'
|
|
213
|
+
}, {
|
|
214
|
+
destination: 'tokens.d.ts',
|
|
215
|
+
format: 'typescript/es6-declarations'
|
|
216
|
+
}]
|
|
217
|
+
},
|
|
218
|
+
|
|
219
|
+
// Tailwind CSS
|
|
220
|
+
tailwind: {
|
|
221
|
+
transformGroup: 'js',
|
|
222
|
+
buildPath: 'build/tailwind/',
|
|
223
|
+
files: [{
|
|
224
|
+
destination: 'tokens.js',
|
|
225
|
+
format: 'javascript/module-flat'
|
|
226
|
+
}]
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
**Generated Output** (`build/css/variables.css`):
|
|
233
|
+
|
|
234
|
+
```css
|
|
235
|
+
:root {
|
|
236
|
+
/* Colors */
|
|
237
|
+
--color-gray-50: #f9fafb;
|
|
238
|
+
--color-gray-900: #111827;
|
|
239
|
+
--color-primary-500: #3b82f6;
|
|
240
|
+
--color-semantic-text-primary: var(--color-gray-900);
|
|
241
|
+
--color-semantic-action-primary: var(--color-primary-500);
|
|
242
|
+
|
|
243
|
+
/* Typography */
|
|
244
|
+
--font-family-sans: Inter, -apple-system, BlinkMacSystemFont, sans-serif;
|
|
245
|
+
--font-size-base: 1rem;
|
|
246
|
+
--font-weight-bold: 700;
|
|
247
|
+
--font-line-height-normal: 1.5;
|
|
248
|
+
|
|
249
|
+
/* Spacing */
|
|
250
|
+
--spacing-4: 1rem;
|
|
251
|
+
--spacing-8: 2rem;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
[data-theme="dark"] {
|
|
255
|
+
--color-semantic-text-primary: var(--color-gray-50);
|
|
256
|
+
--color-semantic-background-default: var(--color-gray-900);
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## Example 2: Atomic Design Folder Structure
|
|
263
|
+
|
|
264
|
+
```
|
|
265
|
+
src/design-system/
|
|
266
|
+
├── tokens/ # DTCG 2025.10 JSON tokens
|
|
267
|
+
│ ├── color.json
|
|
268
|
+
│ ├── typography.json
|
|
269
|
+
│ ├── spacing.json
|
|
270
|
+
│ ├── border.json
|
|
271
|
+
│ └── shadow.json
|
|
272
|
+
│
|
|
273
|
+
├── components/
|
|
274
|
+
│ ├── atoms/ # Basic building blocks
|
|
275
|
+
│ │ ├── Button/
|
|
276
|
+
│ │ │ ├── Button.tsx
|
|
277
|
+
│ │ │ ├── Button.stories.tsx
|
|
278
|
+
│ │ │ ├── Button.test.tsx
|
|
279
|
+
│ │ │ ├── Button.module.css
|
|
280
|
+
│ │ │ └── index.ts
|
|
281
|
+
│ │ ├── Input/
|
|
282
|
+
│ │ ├── Label/
|
|
283
|
+
│ │ ├── Icon/
|
|
284
|
+
│ │ ├── Spinner/
|
|
285
|
+
│ │ └── Badge/
|
|
286
|
+
│ │
|
|
287
|
+
│ ├── molecules/ # Simple component combinations
|
|
288
|
+
│ │ ├── FormField/
|
|
289
|
+
│ │ │ ├── FormField.tsx
|
|
290
|
+
│ │ │ ├── FormField.stories.tsx
|
|
291
|
+
│ │ │ ├── FormField.test.tsx
|
|
292
|
+
│ │ │ └── index.ts
|
|
293
|
+
│ │ ├── SearchBar/
|
|
294
|
+
│ │ ├── Card/
|
|
295
|
+
│ │ ├── Alert/
|
|
296
|
+
│ │ └── Dropdown/
|
|
297
|
+
│ │
|
|
298
|
+
│ ├── organisms/ # Complex component sections
|
|
299
|
+
│ │ ├── Header/
|
|
300
|
+
│ │ ├── Footer/
|
|
301
|
+
│ │ ├── DataTable/
|
|
302
|
+
│ │ ├── Modal/
|
|
303
|
+
│ │ └── NavigationMenu/
|
|
304
|
+
│ │
|
|
305
|
+
│ └── templates/ # Page-level layouts
|
|
306
|
+
│ ├── DashboardLayout/
|
|
307
|
+
│ ├── AuthLayout/
|
|
308
|
+
│ └── LandingPageLayout/
|
|
309
|
+
│
|
|
310
|
+
├── hooks/ # Shared React hooks
|
|
311
|
+
│ ├── useKeyboardNavigation.ts
|
|
312
|
+
│ ├── useFocusTrap.ts
|
|
313
|
+
│ └── useReducedMotion.ts
|
|
314
|
+
│
|
|
315
|
+
├── utils/ # Utility functions
|
|
316
|
+
│ ├── a11y/
|
|
317
|
+
│ │ ├── contrast.ts
|
|
318
|
+
│ │ └── ariaUtils.ts
|
|
319
|
+
│ └── tokens/
|
|
320
|
+
│ └── tokenHelpers.ts
|
|
321
|
+
│
|
|
322
|
+
├── styles/ # Global styles
|
|
323
|
+
│ ├── global.css
|
|
324
|
+
│ ├── reset.css
|
|
325
|
+
│ └── theme.css
|
|
326
|
+
│
|
|
327
|
+
└── index.ts # Public API exports
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
**Public API** (`src/design-system/index.ts`):
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
// Atoms
|
|
334
|
+
export { Button } from './components/atoms/Button';
|
|
335
|
+
export { Input } from './components/atoms/Input';
|
|
336
|
+
export { Icon } from './components/atoms/Icon';
|
|
337
|
+
|
|
338
|
+
// Molecules
|
|
339
|
+
export { FormField } from './components/molecules/FormField';
|
|
340
|
+
export { Card } from './components/molecules/Card';
|
|
341
|
+
|
|
342
|
+
// Organisms
|
|
343
|
+
export { Header } from './components/organisms/Header';
|
|
344
|
+
export { DataTable } from './components/organisms/DataTable';
|
|
345
|
+
|
|
346
|
+
// Hooks
|
|
347
|
+
export { useKeyboardNavigation } from './hooks/useKeyboardNavigation';
|
|
348
|
+
export { useFocusTrap } from './hooks/useFocusTrap';
|
|
349
|
+
|
|
350
|
+
// Utils
|
|
351
|
+
export { getContrastRatio, meetsWCAG } from './utils/a11y/contrast';
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## Example 3: WCAG 2.2 AA Compliance Checklist
|
|
357
|
+
|
|
358
|
+
### Color Contrast Validation
|
|
359
|
+
|
|
360
|
+
**Automated Test** (`utils/a11y/contrast.test.ts`):
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
import { getContrastRatio, meetsWCAG } from './contrast';
|
|
364
|
+
|
|
365
|
+
describe('Color Contrast WCAG 2.2 Compliance', () => {
|
|
366
|
+
describe('Level AA Requirements', () => {
|
|
367
|
+
it('should pass AA for normal text (4.5:1)', () => {
|
|
368
|
+
// Gray 600 on white background
|
|
369
|
+
expect(meetsWCAG('#4b5563', '#ffffff', 'AA', false)).toBe(true);
|
|
370
|
+
expect(getContrastRatio('#4b5563', '#ffffff')).toBeGreaterThanOrEqual(4.5);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it('should pass AA for large text (3:1)', () => {
|
|
374
|
+
// Gray 400 on white background (18pt+)
|
|
375
|
+
expect(meetsWCAG('#9ca3af', '#ffffff', 'AA', true)).toBe(true);
|
|
376
|
+
expect(getContrastRatio('#9ca3af', '#ffffff')).toBeGreaterThanOrEqual(3);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it('should fail AA for insufficient contrast', () => {
|
|
380
|
+
// Light gray on white (fails)
|
|
381
|
+
expect(meetsWCAG('#e5e7eb', '#ffffff', 'AA', false)).toBe(false);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('should pass AA for UI components (3:1)', () => {
|
|
385
|
+
// Gray 300 border on white background
|
|
386
|
+
expect(getContrastRatio('#d1d5db', '#ffffff')).toBeGreaterThanOrEqual(3);
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
describe('Level AAA Requirements', () => {
|
|
391
|
+
it('should pass AAA for normal text (7:1)', () => {
|
|
392
|
+
// Gray 900 on white background
|
|
393
|
+
expect(meetsWCAG('#111827', '#ffffff', 'AAA', false)).toBe(true);
|
|
394
|
+
expect(getContrastRatio('#111827', '#ffffff')).toBeGreaterThanOrEqual(7);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it('should pass AAA for large text (4.5:1)', () => {
|
|
398
|
+
// Gray 600 on white background (18pt+)
|
|
399
|
+
expect(meetsWCAG('#4b5563', '#ffffff', 'AAA', true)).toBe(true);
|
|
400
|
+
expect(getContrastRatio('#4b5563', '#ffffff')).toBeGreaterThanOrEqual(4.5);
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
});
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
**Token Validation Script** (`scripts/validate-contrast.ts`):
|
|
407
|
+
|
|
408
|
+
```typescript
|
|
409
|
+
import { readFileSync } from 'fs';
|
|
410
|
+
import { getContrastRatio } from '../utils/a11y/contrast';
|
|
411
|
+
|
|
412
|
+
interface ColorToken {
|
|
413
|
+
$value: string;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const colorTokens = JSON.parse(readFileSync('tokens/color.json', 'utf-8'));
|
|
417
|
+
const semantic = colorTokens.$tokens.color.semantic;
|
|
418
|
+
|
|
419
|
+
console.log('WCAG 2.2 AA Contrast Validation\n');
|
|
420
|
+
|
|
421
|
+
// Validate text colors
|
|
422
|
+
const textPrimary = semantic.text.primary.$value;
|
|
423
|
+
const bgDefault = semantic.background.default.$value;
|
|
424
|
+
const ratio = getContrastRatio(textPrimary, bgDefault);
|
|
425
|
+
|
|
426
|
+
console.log(`Text Primary on Default Background: ${ratio.toFixed(2)}:1`);
|
|
427
|
+
console.log(`✅ WCAG AA (4.5:1): ${ratio >= 4.5 ? 'PASS' : 'FAIL'}`);
|
|
428
|
+
console.log(`✅ WCAG AAA (7:1): ${ratio >= 7 ? 'PASS' : 'FAIL'}\n`);
|
|
429
|
+
|
|
430
|
+
// Validate all semantic text colors
|
|
431
|
+
const textColors = ['primary', 'secondary', 'disabled'];
|
|
432
|
+
const backgrounds = ['default', 'elevated'];
|
|
433
|
+
|
|
434
|
+
textColors.forEach(textType => {
|
|
435
|
+
backgrounds.forEach(bgType => {
|
|
436
|
+
const textColor = semantic.text[textType].$value;
|
|
437
|
+
const bgColor = semantic.background[bgType].$value;
|
|
438
|
+
const ratio = getContrastRatio(textColor, bgColor);
|
|
439
|
+
|
|
440
|
+
const status = ratio >= 4.5 ? '✅' : '❌';
|
|
441
|
+
console.log(`${status} ${textType} on ${bgType}: ${ratio.toFixed(2)}:1`);
|
|
442
|
+
});
|
|
443
|
+
});
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### Keyboard Navigation Implementation
|
|
447
|
+
|
|
448
|
+
**Hook** (`hooks/useKeyboardNavigation.ts`):
|
|
449
|
+
|
|
450
|
+
```typescript
|
|
451
|
+
import { useEffect, useRef } from 'react';
|
|
452
|
+
|
|
453
|
+
interface KeyboardNavigationOptions {
|
|
454
|
+
onEscape?: () => void;
|
|
455
|
+
onEnter?: () => void;
|
|
456
|
+
trapFocus?: boolean;
|
|
457
|
+
enableArrowKeys?: boolean;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
export function useKeyboardNavigation<T extends HTMLElement>(
|
|
461
|
+
options: KeyboardNavigationOptions = {}
|
|
462
|
+
) {
|
|
463
|
+
const elementRef = useRef<T>(null);
|
|
464
|
+
|
|
465
|
+
useEffect(() => {
|
|
466
|
+
const element = elementRef.current;
|
|
467
|
+
if (!element) return;
|
|
468
|
+
|
|
469
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
470
|
+
// Escape key handling
|
|
471
|
+
if (e.key === 'Escape' && options.onEscape) {
|
|
472
|
+
options.onEscape();
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// Enter key handling
|
|
477
|
+
if (e.key === 'Enter' && options.onEnter) {
|
|
478
|
+
options.onEnter();
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Focus trap (Tab key)
|
|
483
|
+
if (e.key === 'Tab' && options.trapFocus) {
|
|
484
|
+
const focusableElements = element.querySelectorAll<HTMLElement>(
|
|
485
|
+
'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
|
|
486
|
+
);
|
|
487
|
+
|
|
488
|
+
if (focusableElements.length === 0) return;
|
|
489
|
+
|
|
490
|
+
const firstElement = focusableElements[0];
|
|
491
|
+
const lastElement = focusableElements[focusableElements.length - 1];
|
|
492
|
+
|
|
493
|
+
if (e.shiftKey) {
|
|
494
|
+
// Shift+Tab: going backwards
|
|
495
|
+
if (document.activeElement === firstElement) {
|
|
496
|
+
lastElement.focus();
|
|
497
|
+
e.preventDefault();
|
|
498
|
+
}
|
|
499
|
+
} else {
|
|
500
|
+
// Tab: going forwards
|
|
501
|
+
if (document.activeElement === lastElement) {
|
|
502
|
+
firstElement.focus();
|
|
503
|
+
e.preventDefault();
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// Arrow key navigation
|
|
509
|
+
if (options.enableArrowKeys && ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) {
|
|
510
|
+
const focusableElements = Array.from(
|
|
511
|
+
element.querySelectorAll<HTMLElement>(
|
|
512
|
+
'[role="menuitem"], [role="option"], [role="tab"]'
|
|
513
|
+
)
|
|
514
|
+
);
|
|
515
|
+
|
|
516
|
+
if (focusableElements.length === 0) return;
|
|
517
|
+
|
|
518
|
+
const currentIndex = focusableElements.indexOf(document.activeElement as HTMLElement);
|
|
519
|
+
let nextIndex = currentIndex;
|
|
520
|
+
|
|
521
|
+
if (e.key === 'ArrowDown' || e.key === 'ArrowRight') {
|
|
522
|
+
nextIndex = (currentIndex + 1) % focusableElements.length;
|
|
523
|
+
} else if (e.key === 'ArrowUp' || e.key === 'ArrowLeft') {
|
|
524
|
+
nextIndex = (currentIndex - 1 + focusableElements.length) % focusableElements.length;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
focusableElements[nextIndex].focus();
|
|
528
|
+
e.preventDefault();
|
|
529
|
+
}
|
|
530
|
+
};
|
|
531
|
+
|
|
532
|
+
element.addEventListener('keydown', handleKeyDown);
|
|
533
|
+
return () => element.removeEventListener('keydown', handleKeyDown);
|
|
534
|
+
}, [options]);
|
|
535
|
+
|
|
536
|
+
return elementRef;
|
|
537
|
+
}
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
**Usage Example** (Modal component):
|
|
541
|
+
|
|
542
|
+
```typescript
|
|
543
|
+
import { useKeyboardNavigation } from '../../hooks/useKeyboardNavigation';
|
|
544
|
+
|
|
545
|
+
interface ModalProps {
|
|
546
|
+
isOpen: boolean;
|
|
547
|
+
onClose: () => void;
|
|
548
|
+
children: React.ReactNode;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
export function Modal({ isOpen, onClose, children }: ModalProps) {
|
|
552
|
+
const modalRef = useKeyboardNavigation<HTMLDivElement>({
|
|
553
|
+
onEscape: onClose,
|
|
554
|
+
trapFocus: true
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
useEffect(() => {
|
|
558
|
+
if (isOpen && modalRef.current) {
|
|
559
|
+
const firstFocusable = modalRef.current.querySelector<HTMLElement>(
|
|
560
|
+
'button, [href], input'
|
|
561
|
+
);
|
|
562
|
+
firstFocusable?.focus();
|
|
563
|
+
}
|
|
564
|
+
}, [isOpen]);
|
|
565
|
+
|
|
566
|
+
if (!isOpen) return null;
|
|
567
|
+
|
|
568
|
+
return (
|
|
569
|
+
<div
|
|
570
|
+
ref={modalRef}
|
|
571
|
+
role="dialog"
|
|
572
|
+
aria-modal="true"
|
|
573
|
+
aria-labelledby="modal-title"
|
|
574
|
+
className="modal-overlay"
|
|
575
|
+
>
|
|
576
|
+
<div className="modal-content">
|
|
577
|
+
{children}
|
|
578
|
+
</div>
|
|
579
|
+
</div>
|
|
580
|
+
);
|
|
581
|
+
}
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
### ARIA Implementation Examples
|
|
585
|
+
|
|
586
|
+
**FormField with Complete ARIA** (`components/molecules/FormField/FormField.tsx`):
|
|
587
|
+
|
|
588
|
+
```typescript
|
|
589
|
+
import { useId, forwardRef } from 'react';
|
|
590
|
+
import { Input } from '../../atoms/Input';
|
|
591
|
+
import { Label } from '../../atoms/Label';
|
|
592
|
+
|
|
593
|
+
interface FormFieldProps {
|
|
594
|
+
label: string;
|
|
595
|
+
error?: string;
|
|
596
|
+
hint?: string;
|
|
597
|
+
required?: boolean;
|
|
598
|
+
disabled?: boolean;
|
|
599
|
+
type?: string;
|
|
600
|
+
value?: string;
|
|
601
|
+
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
export const FormField = forwardRef<HTMLInputElement, FormFieldProps>(
|
|
605
|
+
({ label, error, hint, required, disabled, ...inputProps }, ref) => {
|
|
606
|
+
const inputId = useId();
|
|
607
|
+
const errorId = `${inputId}-error`;
|
|
608
|
+
const hintId = `${inputId}-hint`;
|
|
609
|
+
|
|
610
|
+
return (
|
|
611
|
+
<div className="form-field">
|
|
612
|
+
<Label htmlFor={inputId}>
|
|
613
|
+
{label}
|
|
614
|
+
{required && (
|
|
615
|
+
<span className="required-indicator" aria-label="required field">
|
|
616
|
+
*
|
|
617
|
+
</span>
|
|
618
|
+
)}
|
|
619
|
+
</Label>
|
|
620
|
+
|
|
621
|
+
{hint && !error && (
|
|
622
|
+
<p id={hintId} className="hint-text">
|
|
623
|
+
{hint}
|
|
624
|
+
</p>
|
|
625
|
+
)}
|
|
626
|
+
|
|
627
|
+
<Input
|
|
628
|
+
ref={ref}
|
|
629
|
+
id={inputId}
|
|
630
|
+
aria-invalid={!!error}
|
|
631
|
+
aria-describedby={
|
|
632
|
+
error ? errorId : hint ? hintId : undefined
|
|
633
|
+
}
|
|
634
|
+
aria-required={required}
|
|
635
|
+
disabled={disabled}
|
|
636
|
+
{...inputProps}
|
|
637
|
+
/>
|
|
638
|
+
|
|
639
|
+
{error && (
|
|
640
|
+
<p id={errorId} role="alert" className="error-text">
|
|
641
|
+
{error}
|
|
642
|
+
</p>
|
|
643
|
+
)}
|
|
644
|
+
</div>
|
|
645
|
+
);
|
|
646
|
+
}
|
|
647
|
+
);
|
|
648
|
+
|
|
649
|
+
FormField.displayName = 'FormField';
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
---
|
|
653
|
+
|
|
654
|
+
## Example 4: Figma → React Component Export Workflow
|
|
655
|
+
|
|
656
|
+
### Step 1: Figma File Structure
|
|
657
|
+
|
|
658
|
+
**Best Practices**:
|
|
659
|
+
- Name layers semantically: `PrimaryButton`, `HeaderNavigation`, `ProductCard`
|
|
660
|
+
- Use Figma Variables for colors, spacing, typography
|
|
661
|
+
- Apply Auto Layout for responsive components
|
|
662
|
+
- Add component descriptions for AI context
|
|
663
|
+
|
|
664
|
+
**Figma Variables Example**:
|
|
665
|
+
|
|
666
|
+
```
|
|
667
|
+
Colors/
|
|
668
|
+
Primary/
|
|
669
|
+
primary-50: #eff6ff
|
|
670
|
+
primary-500: #3b82f6
|
|
671
|
+
primary-600: #2563eb
|
|
672
|
+
|
|
673
|
+
Semantic/
|
|
674
|
+
text-primary: {gray-900}
|
|
675
|
+
action-primary: {primary-500}
|
|
676
|
+
|
|
677
|
+
Spacing/
|
|
678
|
+
spacing-2: 8px
|
|
679
|
+
spacing-4: 16px
|
|
680
|
+
spacing-6: 24px
|
|
681
|
+
|
|
682
|
+
Typography/
|
|
683
|
+
font-size-base: 16px
|
|
684
|
+
font-weight-bold: 700
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
### Step 2: Extract Design Tokens via MCP
|
|
688
|
+
|
|
689
|
+
**Prompt to Claude**:
|
|
690
|
+
|
|
691
|
+
```
|
|
692
|
+
Extract all design tokens from this Figma file:
|
|
693
|
+
https://www.figma.com/file/XYZ123/DesignSystem
|
|
694
|
+
|
|
695
|
+
Output as DTCG 2025.10 JSON format.
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
**MCP Response** (Generated JSON):
|
|
699
|
+
|
|
700
|
+
```json
|
|
701
|
+
{
|
|
702
|
+
"$schema": "https://tr.designtokens.org/format/",
|
|
703
|
+
"$tokens": {
|
|
704
|
+
"color": {
|
|
705
|
+
"primary": {
|
|
706
|
+
"$type": "color",
|
|
707
|
+
"500": { "$value": "#3b82f6" }
|
|
708
|
+
}
|
|
709
|
+
},
|
|
710
|
+
"spacing": {
|
|
711
|
+
"$type": "dimension",
|
|
712
|
+
"4": { "$value": "1rem" }
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
### Step 3: Generate Component Code
|
|
719
|
+
|
|
720
|
+
**Prompt to Claude**:
|
|
721
|
+
|
|
722
|
+
```
|
|
723
|
+
Generate a React TypeScript component from this Figma frame:
|
|
724
|
+
https://www.figma.com/file/XYZ123/DesignSystem?node-id=123:456
|
|
725
|
+
|
|
726
|
+
Requirements:
|
|
727
|
+
- Use design tokens from tokens/color.json
|
|
728
|
+
- WCAG 2.2 AA compliant
|
|
729
|
+
- Include Storybook story
|
|
730
|
+
- Add accessibility tests
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
**Generated Component** (`components/atoms/Button/Button.tsx`):
|
|
734
|
+
|
|
735
|
+
```typescript
|
|
736
|
+
import { forwardRef } from 'react';
|
|
737
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
738
|
+
import styles from './Button.module.css';
|
|
739
|
+
|
|
740
|
+
const buttonVariants = cva(styles.button, {
|
|
741
|
+
variants: {
|
|
742
|
+
variant: {
|
|
743
|
+
primary: styles.primary,
|
|
744
|
+
secondary: styles.secondary,
|
|
745
|
+
outline: styles.outline
|
|
746
|
+
},
|
|
747
|
+
size: {
|
|
748
|
+
sm: styles.sm,
|
|
749
|
+
md: styles.md,
|
|
750
|
+
lg: styles.lg
|
|
751
|
+
}
|
|
752
|
+
},
|
|
753
|
+
defaultVariants: {
|
|
754
|
+
variant: 'primary',
|
|
755
|
+
size: 'md'
|
|
756
|
+
}
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
export interface ButtonProps
|
|
760
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
761
|
+
VariantProps<typeof buttonVariants> {
|
|
762
|
+
isLoading?: boolean;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
|
766
|
+
({ className, variant, size, isLoading, children, disabled, ...props }, ref) => {
|
|
767
|
+
return (
|
|
768
|
+
<button
|
|
769
|
+
ref={ref}
|
|
770
|
+
className={buttonVariants({ variant, size, className })}
|
|
771
|
+
disabled={disabled || isLoading}
|
|
772
|
+
aria-busy={isLoading}
|
|
773
|
+
{...props}
|
|
774
|
+
>
|
|
775
|
+
{isLoading ? (
|
|
776
|
+
<>
|
|
777
|
+
<span className={styles.spinner} aria-hidden="true" />
|
|
778
|
+
<span className="sr-only">Loading...</span>
|
|
779
|
+
</>
|
|
780
|
+
) : (
|
|
781
|
+
children
|
|
782
|
+
)}
|
|
783
|
+
</button>
|
|
784
|
+
);
|
|
785
|
+
}
|
|
786
|
+
);
|
|
787
|
+
|
|
788
|
+
Button.displayName = 'Button';
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
**Generated Styles** (`Button.module.css`):
|
|
792
|
+
|
|
793
|
+
```css
|
|
794
|
+
.button {
|
|
795
|
+
display: inline-flex;
|
|
796
|
+
align-items: center;
|
|
797
|
+
justify-content: center;
|
|
798
|
+
border-radius: 0.375rem;
|
|
799
|
+
font-weight: 500;
|
|
800
|
+
transition: background-color 0.2s, color 0.2s;
|
|
801
|
+
cursor: pointer;
|
|
802
|
+
border: none;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
.button:focus-visible {
|
|
806
|
+
outline: 2px solid var(--color-primary-500);
|
|
807
|
+
outline-offset: 2px;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
.button:disabled {
|
|
811
|
+
opacity: 0.5;
|
|
812
|
+
cursor: not-allowed;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
.primary {
|
|
816
|
+
background-color: var(--color-primary-500);
|
|
817
|
+
color: #ffffff;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
.primary:hover:not(:disabled) {
|
|
821
|
+
background-color: var(--color-primary-600);
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
.secondary {
|
|
825
|
+
background-color: var(--color-gray-200);
|
|
826
|
+
color: var(--color-gray-900);
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
.secondary:hover:not(:disabled) {
|
|
830
|
+
background-color: var(--color-gray-300);
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
.sm {
|
|
834
|
+
height: 2rem;
|
|
835
|
+
padding: 0 0.75rem;
|
|
836
|
+
font-size: 0.875rem;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
.md {
|
|
840
|
+
height: 2.5rem;
|
|
841
|
+
padding: 0 1rem;
|
|
842
|
+
font-size: 1rem;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
.lg {
|
|
846
|
+
height: 3rem;
|
|
847
|
+
padding: 0 1.5rem;
|
|
848
|
+
font-size: 1.125rem;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
@media (prefers-reduced-motion: reduce) {
|
|
852
|
+
.button {
|
|
853
|
+
transition: none;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
```
|
|
857
|
+
|
|
858
|
+
---
|
|
859
|
+
|
|
860
|
+
## Example 5: Storybook Configuration & Stories
|
|
861
|
+
|
|
862
|
+
### Storybook Main Configuration
|
|
863
|
+
|
|
864
|
+
**File**: `.storybook/main.ts`
|
|
865
|
+
|
|
866
|
+
```typescript
|
|
867
|
+
import type { StorybookConfig } from '@storybook/react-vite';
|
|
868
|
+
|
|
869
|
+
const config: StorybookConfig = {
|
|
870
|
+
stories: [
|
|
871
|
+
'../src/**/*.mdx',
|
|
872
|
+
'../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'
|
|
873
|
+
],
|
|
874
|
+
|
|
875
|
+
addons: [
|
|
876
|
+
'@storybook/addon-links',
|
|
877
|
+
'@storybook/addon-essentials',
|
|
878
|
+
'@storybook/addon-interactions',
|
|
879
|
+
'@storybook/addon-a11y', // Accessibility testing
|
|
880
|
+
'@chromatic-com/storybook' // Visual regression
|
|
881
|
+
],
|
|
882
|
+
|
|
883
|
+
framework: {
|
|
884
|
+
name: '@storybook/react-vite',
|
|
885
|
+
options: {}
|
|
886
|
+
},
|
|
887
|
+
|
|
888
|
+
docs: {
|
|
889
|
+
autodocs: 'tag'
|
|
890
|
+
},
|
|
891
|
+
|
|
892
|
+
viteFinal: async (config) => {
|
|
893
|
+
// Add custom Vite config here
|
|
894
|
+
return config;
|
|
895
|
+
}
|
|
896
|
+
};
|
|
897
|
+
|
|
898
|
+
export default config;
|
|
899
|
+
```
|
|
900
|
+
|
|
901
|
+
### Theme Configuration
|
|
902
|
+
|
|
903
|
+
**File**: `.storybook/preview.ts`
|
|
904
|
+
|
|
905
|
+
```typescript
|
|
906
|
+
import type { Preview } from '@storybook/react';
|
|
907
|
+
import '../src/design-system/styles/global.css';
|
|
908
|
+
|
|
909
|
+
const preview: Preview = {
|
|
910
|
+
parameters: {
|
|
911
|
+
actions: { argTypesRegex: '^on[A-Z].*' },
|
|
912
|
+
controls: {
|
|
913
|
+
matchers: {
|
|
914
|
+
color: /(background|color)$/i,
|
|
915
|
+
date: /Date$/i
|
|
916
|
+
}
|
|
917
|
+
},
|
|
918
|
+
// Accessibility addon configuration
|
|
919
|
+
a11y: {
|
|
920
|
+
config: {
|
|
921
|
+
rules: [
|
|
922
|
+
{
|
|
923
|
+
id: 'color-contrast',
|
|
924
|
+
enabled: true
|
|
925
|
+
},
|
|
926
|
+
{
|
|
927
|
+
id: 'label',
|
|
928
|
+
enabled: true
|
|
929
|
+
}
|
|
930
|
+
]
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
},
|
|
934
|
+
|
|
935
|
+
// Global decorators
|
|
936
|
+
decorators: [
|
|
937
|
+
(Story) => (
|
|
938
|
+
<div style={{ padding: '2rem' }}>
|
|
939
|
+
<Story />
|
|
940
|
+
</div>
|
|
941
|
+
)
|
|
942
|
+
],
|
|
943
|
+
|
|
944
|
+
// Theme switching
|
|
945
|
+
globalTypes: {
|
|
946
|
+
theme: {
|
|
947
|
+
description: 'Global theme for components',
|
|
948
|
+
defaultValue: 'light',
|
|
949
|
+
toolbar: {
|
|
950
|
+
title: 'Theme',
|
|
951
|
+
icon: 'circlehollow',
|
|
952
|
+
items: ['light', 'dark'],
|
|
953
|
+
dynamicTitle: true
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
};
|
|
958
|
+
|
|
959
|
+
export default preview;
|
|
960
|
+
```
|
|
961
|
+
|
|
962
|
+
### Complete Component Story
|
|
963
|
+
|
|
964
|
+
**File**: `components/atoms/Button/Button.stories.tsx`
|
|
965
|
+
|
|
966
|
+
```typescript
|
|
967
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
968
|
+
import { fn } from '@storybook/test';
|
|
969
|
+
import { Button } from './Button';
|
|
970
|
+
|
|
971
|
+
const meta: Meta<typeof Button> = {
|
|
972
|
+
title: 'Design System/Atoms/Button',
|
|
973
|
+
component: Button,
|
|
974
|
+
tags: ['autodocs'],
|
|
975
|
+
|
|
976
|
+
parameters: {
|
|
977
|
+
layout: 'centered',
|
|
978
|
+
a11y: {
|
|
979
|
+
// Component-specific a11y config
|
|
980
|
+
config: {
|
|
981
|
+
rules: [{ id: 'button-name', enabled: true }]
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
},
|
|
985
|
+
|
|
986
|
+
argTypes: {
|
|
987
|
+
variant: {
|
|
988
|
+
control: 'select',
|
|
989
|
+
options: ['primary', 'secondary', 'outline'],
|
|
990
|
+
description: 'Visual style variant',
|
|
991
|
+
table: {
|
|
992
|
+
type: { summary: 'string' },
|
|
993
|
+
defaultValue: { summary: 'primary' }
|
|
994
|
+
}
|
|
995
|
+
},
|
|
996
|
+
size: {
|
|
997
|
+
control: 'select',
|
|
998
|
+
options: ['sm', 'md', 'lg'],
|
|
999
|
+
description: 'Button size',
|
|
1000
|
+
table: {
|
|
1001
|
+
type: { summary: 'string' },
|
|
1002
|
+
defaultValue: { summary: 'md' }
|
|
1003
|
+
}
|
|
1004
|
+
},
|
|
1005
|
+
isLoading: {
|
|
1006
|
+
control: 'boolean',
|
|
1007
|
+
description: 'Loading state indicator'
|
|
1008
|
+
},
|
|
1009
|
+
disabled: {
|
|
1010
|
+
control: 'boolean',
|
|
1011
|
+
description: 'Disabled state'
|
|
1012
|
+
}
|
|
1013
|
+
},
|
|
1014
|
+
|
|
1015
|
+
args: {
|
|
1016
|
+
onClick: fn()
|
|
1017
|
+
}
|
|
1018
|
+
};
|
|
1019
|
+
|
|
1020
|
+
export default meta;
|
|
1021
|
+
type Story = StoryObj<typeof Button>;
|
|
1022
|
+
|
|
1023
|
+
// Default story
|
|
1024
|
+
export const Primary: Story = {
|
|
1025
|
+
args: {
|
|
1026
|
+
children: 'Primary Button',
|
|
1027
|
+
variant: 'primary'
|
|
1028
|
+
}
|
|
1029
|
+
};
|
|
1030
|
+
|
|
1031
|
+
export const Secondary: Story = {
|
|
1032
|
+
args: {
|
|
1033
|
+
children: 'Secondary Button',
|
|
1034
|
+
variant: 'secondary'
|
|
1035
|
+
}
|
|
1036
|
+
};
|
|
1037
|
+
|
|
1038
|
+
export const Outline: Story = {
|
|
1039
|
+
args: {
|
|
1040
|
+
children: 'Outline Button',
|
|
1041
|
+
variant: 'outline'
|
|
1042
|
+
}
|
|
1043
|
+
};
|
|
1044
|
+
|
|
1045
|
+
// Size variations
|
|
1046
|
+
export const Small: Story = {
|
|
1047
|
+
args: {
|
|
1048
|
+
children: 'Small Button',
|
|
1049
|
+
size: 'sm'
|
|
1050
|
+
}
|
|
1051
|
+
};
|
|
1052
|
+
|
|
1053
|
+
export const Large: Story = {
|
|
1054
|
+
args: {
|
|
1055
|
+
children: 'Large Button',
|
|
1056
|
+
size: 'lg'
|
|
1057
|
+
}
|
|
1058
|
+
};
|
|
1059
|
+
|
|
1060
|
+
// State variations
|
|
1061
|
+
export const Disabled: Story = {
|
|
1062
|
+
args: {
|
|
1063
|
+
children: 'Disabled Button',
|
|
1064
|
+
disabled: true
|
|
1065
|
+
}
|
|
1066
|
+
};
|
|
1067
|
+
|
|
1068
|
+
export const Loading: Story = {
|
|
1069
|
+
args: {
|
|
1070
|
+
children: 'Loading Button',
|
|
1071
|
+
isLoading: true
|
|
1072
|
+
}
|
|
1073
|
+
};
|
|
1074
|
+
|
|
1075
|
+
// All variants showcase
|
|
1076
|
+
export const AllVariants: Story = {
|
|
1077
|
+
render: () => (
|
|
1078
|
+
<div style={{ display: 'flex', gap: '1rem', flexWrap: 'wrap' }}>
|
|
1079
|
+
<Button variant="primary">Primary</Button>
|
|
1080
|
+
<Button variant="secondary">Secondary</Button>
|
|
1081
|
+
<Button variant="outline">Outline</Button>
|
|
1082
|
+
<Button variant="primary" disabled>Disabled</Button>
|
|
1083
|
+
<Button variant="primary" isLoading>Loading</Button>
|
|
1084
|
+
</div>
|
|
1085
|
+
)
|
|
1086
|
+
};
|
|
1087
|
+
|
|
1088
|
+
// All sizes showcase
|
|
1089
|
+
export const AllSizes: Story = {
|
|
1090
|
+
render: () => (
|
|
1091
|
+
<div style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
|
|
1092
|
+
<Button size="sm">Small</Button>
|
|
1093
|
+
<Button size="md">Medium</Button>
|
|
1094
|
+
<Button size="lg">Large</Button>
|
|
1095
|
+
</div>
|
|
1096
|
+
)
|
|
1097
|
+
};
|
|
1098
|
+
|
|
1099
|
+
// Interaction test
|
|
1100
|
+
export const WithInteraction: Story = {
|
|
1101
|
+
args: {
|
|
1102
|
+
children: 'Click to test'
|
|
1103
|
+
},
|
|
1104
|
+
play: async ({ canvasElement, step }) => {
|
|
1105
|
+
const { userEvent, within, expect } = await import('@storybook/test');
|
|
1106
|
+
const canvas = within(canvasElement);
|
|
1107
|
+
|
|
1108
|
+
await step('Click button', async () => {
|
|
1109
|
+
const button = canvas.getByRole('button');
|
|
1110
|
+
await userEvent.click(button);
|
|
1111
|
+
});
|
|
1112
|
+
|
|
1113
|
+
await step('Verify focus', async () => {
|
|
1114
|
+
const button = canvas.getByRole('button');
|
|
1115
|
+
await expect(button).toHaveFocus();
|
|
1116
|
+
});
|
|
1117
|
+
}
|
|
1118
|
+
};
|
|
1119
|
+
```
|
|
1120
|
+
|
|
1121
|
+
### Accessibility Test Story
|
|
1122
|
+
|
|
1123
|
+
**Dedicated a11y story**:
|
|
1124
|
+
|
|
1125
|
+
```typescript
|
|
1126
|
+
export const AccessibilityTest: Story = {
|
|
1127
|
+
args: {
|
|
1128
|
+
children: 'Accessibility Test Button'
|
|
1129
|
+
},
|
|
1130
|
+
parameters: {
|
|
1131
|
+
a11y: {
|
|
1132
|
+
element: '#storybook-root',
|
|
1133
|
+
config: {
|
|
1134
|
+
rules: [
|
|
1135
|
+
{ id: 'color-contrast', enabled: true },
|
|
1136
|
+
{ id: 'button-name', enabled: true },
|
|
1137
|
+
{ id: 'focus-order', enabled: true }
|
|
1138
|
+
]
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
};
|
|
1143
|
+
```
|
|
1144
|
+
|
|
1145
|
+
---
|
|
1146
|
+
|
|
1147
|
+
## Example 6: Visual Regression Testing (Chromatic)
|
|
1148
|
+
|
|
1149
|
+
### GitHub Actions Workflow
|
|
1150
|
+
|
|
1151
|
+
**File**: `.github/workflows/chromatic.yml`
|
|
1152
|
+
|
|
1153
|
+
```yaml
|
|
1154
|
+
name: Chromatic
|
|
1155
|
+
|
|
1156
|
+
on:
|
|
1157
|
+
push:
|
|
1158
|
+
branches:
|
|
1159
|
+
- main
|
|
1160
|
+
- develop
|
|
1161
|
+
pull_request:
|
|
1162
|
+
branches:
|
|
1163
|
+
- main
|
|
1164
|
+
|
|
1165
|
+
jobs:
|
|
1166
|
+
chromatic:
|
|
1167
|
+
runs-on: ubuntu-latest
|
|
1168
|
+
|
|
1169
|
+
steps:
|
|
1170
|
+
- name: Checkout repository
|
|
1171
|
+
uses: actions/checkout@v4
|
|
1172
|
+
with:
|
|
1173
|
+
fetch-depth: 0 # Required for Chromatic
|
|
1174
|
+
|
|
1175
|
+
- name: Setup Node.js
|
|
1176
|
+
uses: actions/setup-node@v4
|
|
1177
|
+
with:
|
|
1178
|
+
node-version: 20
|
|
1179
|
+
cache: 'npm'
|
|
1180
|
+
|
|
1181
|
+
- name: Install dependencies
|
|
1182
|
+
run: npm ci
|
|
1183
|
+
|
|
1184
|
+
- name: Build Storybook
|
|
1185
|
+
run: npm run build-storybook
|
|
1186
|
+
|
|
1187
|
+
- name: Publish to Chromatic
|
|
1188
|
+
uses: chromaui/action@v1
|
|
1189
|
+
with:
|
|
1190
|
+
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
|
|
1191
|
+
exitZeroOnChanges: true
|
|
1192
|
+
onlyChanged: true # Only test changed components
|
|
1193
|
+
buildScriptName: 'build-storybook'
|
|
1194
|
+
```
|
|
1195
|
+
|
|
1196
|
+
### Package.json Scripts
|
|
1197
|
+
|
|
1198
|
+
```json
|
|
1199
|
+
{
|
|
1200
|
+
"scripts": {
|
|
1201
|
+
"storybook": "storybook dev -p 6006",
|
|
1202
|
+
"build-storybook": "storybook build",
|
|
1203
|
+
"chromatic": "chromatic --project-token=$CHROMATIC_PROJECT_TOKEN",
|
|
1204
|
+
"test:visual": "npm run build-storybook && npm run chromatic"
|
|
1205
|
+
},
|
|
1206
|
+
"devDependencies": {
|
|
1207
|
+
"@chromatic-com/storybook": "^1.0.0",
|
|
1208
|
+
"@storybook/addon-a11y": "^8.0.0",
|
|
1209
|
+
"@storybook/react-vite": "^8.0.0",
|
|
1210
|
+
"chromatic": "^11.0.0"
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
```
|
|
1214
|
+
|
|
1215
|
+
---
|
|
1216
|
+
|
|
1217
|
+
## Complete Example: Production-Ready Button Component
|
|
1218
|
+
|
|
1219
|
+
**All files combined for a production-ready atomic component:**
|
|
1220
|
+
|
|
1221
|
+
### 1. Component (`Button.tsx`)
|
|
1222
|
+
### 2. Styles (`Button.module.css`)
|
|
1223
|
+
### 3. Tests (`Button.test.tsx`)
|
|
1224
|
+
### 4. Stories (`Button.stories.tsx`)
|
|
1225
|
+
### 5. Index (`index.ts`)
|
|
1226
|
+
|
|
1227
|
+
**See SKILL.md Level 2 for complete implementations.**
|
|
1228
|
+
|
|
1229
|
+
---
|
|
1230
|
+
|
|
1231
|
+
**This examples file demonstrates:**
|
|
1232
|
+
- ✅ DTCG 2025.10 token structure
|
|
1233
|
+
- ✅ WCAG 2.2 AA/AAA compliance
|
|
1234
|
+
- ✅ Figma MCP integration workflow
|
|
1235
|
+
- ✅ Storybook 8 documentation patterns
|
|
1236
|
+
- ✅ Visual regression testing setup
|
|
1237
|
+
- ✅ Accessibility testing automation
|
|
1238
|
+
- ✅ Production-ready component architecture
|