skills/design-init/SKILL.md
Creates/updates docs/design-system/DESIGN.md — the single source of truth (SSoT) for the project's design system — and auto-generates src/styles/design-tokens.css. Fills OKLCH color, typography, spacing, and motion tokens from brand keywords, persona, and reference inputs (images/URLs), and codifies the anti-AI aesthetic rules (Vibe Coding). Outputs a hybrid format (YAML Front Matter + Markdown Body) that combines the Google Stitch DESIGN.md spec with the ASTRA 3-tier token structure (Primitive→Semantic→Component). Modes: new generation (init), update on existing (update), regenerate CSS only (--regenerate-css), extract from references then merge (--from-refs=<path-or-url> — internally calls /design-extract once), and merge from a pre-existing extract report (--apply-extract=<report-path>). If docs/planner/*/interview-report.md is present the persona is auto-cited, and /service-planner, /handoff-publish, and design-token-validator all reference this file as the SSoT.
npx skillsauth add astra-technology-company-limited/astra-methodology design-initInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Creates and manages docs/design-system/DESIGN.md as the project's design system single source of truth (SSoT). When this file changes, src/styles/design-tokens.css becomes an artifact regenerated automatically by this skill.
Problems this skill solves:
design-tokens.css (for machines) and components.md (for humans) were separate, forcing the AI to verify consistency between the two files every time.Resolution direction:
/service-planner, /handoff-publish, and design-token-validator all reference DESIGN.md first.Parse from $ARGUMENTS:
| Token | Meaning |
|-------|---------|
| --regenerate-css | Regenerate only design-tokens.css from DESIGN.md. Skip other steps. |
| --from-refs=<paths-or-urls> | Extract tokens from image/PDF/URL references and merge. Internally invokes /design-extract exactly once to generate a report, then merges that report directly without re-entering via --apply-extract (no circular invocation). |
| --apply-extract=<report-path> | Merge an already-generated docs/design-system/extract-report-*.md report into DESIGN.md. Does not invoke /design-extract again. |
| --auto | Skip HITL, use conservative defaults (autorun-compatible). |
Determine the MODE variable (in priority order):
regenerate-css if --regenerate-cssapply-extract if --apply-extract=...from-refs if --from-refs=... (→ inside the Step 1 branch, invoke /design-extract and merge the resulting report verbatim)update if docs/design-system/DESIGN.md existsinit otherwiseAUTO_MODE=1 if --auto.
# Verify project root
test -d docs || { echo "ERROR: Run from project root (docs/ missing)"; exit 1; }
# Existing assets
EXISTING_DESIGN_MD="docs/design-system/DESIGN.md"
EXISTING_CSS="src/styles/design-tokens.css"
EXISTING_COMPONENTS="docs/design-system/components.md"
PLANNER_DIRS=$(ls -d docs/planner/[0-9][0-9][0-9]-* 2>/dev/null)
| State | Behavior |
|-------|----------|
| DESIGN.md exists + update mode | Read existing file → ask intent for changes → partial update |
| DESIGN.md missing + init mode | New-generation workflow (Steps 2~5) |
| regenerate-css mode | Run Step 6 only |
| from-refs mode | Invoke Skill(design-extract, args="<refs>") exactly once to produce docs/design-system/extract-report-*.md → store the resulting report path in EXTRACT_REPORT_PATH → then merge in the same way as apply-extract (Step 2.2 HITL uses values filled by the report as defaults) |
| apply-extract mode | Read the report specified by --apply-extract=<path> and use it as the seed for the Step 3 placeholder substitution. Does not invoke /design-extract. |
| Only components.md exists + DESIGN.md missing | Migration mode — absorb the components.md content into Body §4 during Step 2 |
In init or update mode, collect inputs to fill Front Matter brand and Body §1~§2.
If PLANNER_DIRS is non-empty, read interview-report.md and requirements-definition.md of the most recent directory and auto-extract:
brand.target_persona ← one-sentence summary of the primary persona from interview-report.mdbrand.voice_tone ← tone guide from requirements-definition.md (when present)AUTO_MODE=0)Ask the following 4 items via AskUserQuestion (multiSelect=false):
brand.personalitytokens.color.primitive.primary.*If AUTO_MODE=1, defaults: Calm / Blue / Geist+Pretendard / Comfortable.
Accept free-form input (not AskUserQuestion) — guide the user to write "this project's design philosophy" as a single sentence. If empty, auto-generate from the persona ("a {tone} design system for {persona}").
Read $CLAUDE_PLUGIN_ROOT/skills/project-init/templates/DESIGN.md and substitute the following placeholders:
| Placeholder | Value |
|-------------|-------|
| {project-name} | name from package.json, or the directory name if absent |
| brand.philosophy | Step 2.3 input |
| brand.personality | Step 2.2 (answer #1 + 2 supplemental defaults) |
| brand.target_persona | Step 2.1 auto-extract, or user input if absent |
| brand.voice_tone | Step 2.1 auto-extract, or 3 defaults if absent |
| tokens.color.primitive.primary.* | 11 OKLCH steps based on the Step 2.2 (#2) color family (use a lookup table) |
| tokens.typography.fonts.sans/mono | Step 2.2 (#3) |
| spacing.scale defaults | adjust component heights per Step 2.2 (#4) density |
| Body §1 Design Philosophy | Step 2.3 + 2-3 auto-augmented sentences |
| Body §2 Persona citation | Step 2.1 citation or user input |
| Body §5 aesthetic_rules project-specific elements | 2 tone-based defaults |
| Version 1.0.0 Date | $(date +%Y-%m-%d) |
Output: docs/design-system/DESIGN.md
Primary color OKLCH lookup:
Blue: base hue 259°, L 62.3% (500) — Linear/Vercel tone
Indigo: base hue 277°, L 58.5% (500) — Stripe tone
Emerald: base hue 162°, L 69.6% (500)
Rose: base hue 17°, L 67.0% (500)
Amber: base hue 70°, L 76.9% (500)
Neutral: minimal color emphasis — primary=neutral-900
For each family, the 11 steps (50/100/200/.../950) apply a lightness curve (Tailwind standard curve as reference). Concrete values are loaded from $CLAUDE_PLUGIN_ROOT/skills/design-init/assets/color-palettes.yaml.
Before proceeding to Step 5, immediately call Step 6 (regenerate-css). Without the CSS, preview is impossible.
Task(
subagent_type: "astra-methodology:design-token-validator",
description: "Verify DESIGN.md",
prompt: "Verify the Front Matter token consistency and WCAG contrast ratios of docs/design-system/DESIGN.md. Report any violations."
)
If the report contains Critical items, show them to the user and guide them to fix.
AUTO_MODE=0 and with user consent)Generate docs/design-system/preview.html so the user can visually verify the applied tokens. Structure:
After generation, output only the path. The user opens it directly.
--regenerate-css or called from Step 4)Parse the Front Matter of docs/design-system/DESIGN.md and generate src/styles/design-tokens.css.
The DESIGN.md Front Matter is standard YAML, but semantic tokens use the ASTRA-specific reference syntax "{tokens.color.primitive.neutral.0}" (instead of YAML anchors, for readability). Therefore handle in two stages:
python3 - <<'PY'
import yaml, re, sys
with open('docs/design-system/DESIGN.md') as f:
content = f.read()
m = re.match(r'^---\n(.*?)\n---\n', content, re.DOTALL)
if not m:
sys.exit("DESIGN.md Front Matter missing")
data = yaml.safe_load(m.group(1))
# Resolve reference syntax: "{tokens.color.primitive.neutral.0}" → CSS var()
REF_RE = re.compile(r'^\{([^}]+)\}$')
def resolve_ref(value):
"""If value is a reference string, convert to CSS variable name (delegate actual value lookup to the CSS variable chain)."""
if not isinstance(value, str):
return value
m = REF_RE.match(value)
if not m:
return value
path = m.group(1).split('.')
# tokens.color.primitive.neutral.0 → --primitive-neutral-0
# tokens.color.semantic.surface.base → --surface-base
if path[:3] == ['tokens', 'color', 'primitive']:
return f"var(--primitive-{path[3]}-{path[4]})"
if path[:3] == ['tokens', 'color', 'semantic']:
# path[3]=group, path[4]=name
kebab = path[4].replace('_', '-')
return f"var(--{path[3]}-{kebab})"
# Others (typography·spacing·motion etc.) — extend as needed
return f"var(--{'-'.join(path[1:]).replace('_','-')})"
def walk(node):
if isinstance(node, dict):
return {k: walk(v) for k, v in node.items()}
if isinstance(node, list):
return [walk(x) for x in node]
return resolve_ref(node)
resolved = walk(data)
# Then apply Step 6.2 transformation rules
PY
Important: References are converted to CSS variable chains (not OKLCH value lookups). For example, the generated CSS contains --surface-base: var(--primitive-neutral-0), and the browser chain-resolves at runtime. This is the key mechanism that cleanly supports dark mode token overrides.
| YAML path | CSS variable name |
|-----------|-------------------|
| tokens.color.primitive.{family}.{shade} | --primitive-{family}-{shade} |
| tokens.color.semantic.{group}.{name} | --{group}-{name} (snake_case → kebab-case) |
| tokens.typography.scale.{key} | --text-{key} |
| tokens.typography.fluid_scale.{key} | --fluid-{key} |
| tokens.typography.weight.{key} | --weight-{key} |
| tokens.spacing.scale.{key} | --space-{key} |
| tokens.radius.{key} | --radius-{key} |
| tokens.shadow.{key} | --shadow-{key} |
| tokens.motion.duration.{key} | --duration-{key} |
| tokens.motion.easing.{key} | --ease-{key.replace('_', '-')} |
| tokens.motion.spring.{key} | --ease-spring-{key} |
| tokens.breakpoints.{key} | --breakpoint-{key} |
| tokens.z_index.{key} | --z-{key} |
Reference substitution: "{tokens.color.primitive.neutral.0}" → var(--primitive-neutral-0).
Add an auto-generation warning at the top:
/* ============================================================================
* AUTO-GENERATED from docs/design-system/DESIGN.md — DO NOT EDIT BY HAND
* Regenerate: /design-init --regenerate-css
* Generated: {YYYY-MM-DDTHH:mm:ss}
* Source version: {meta.version from DESIGN.md}
* ============================================================================
*/
:root {
/* Primitive */
--primitive-primary-50: oklch(...);
...
/* Semantic */
--surface-base: var(--primitive-neutral-0);
...
}
if [ -f "$EXISTING_CSS" ]; then
diff -u "${EXISTING_CSS}" "${EXISTING_CSS}.new" | head -40 || true
fi
mv "${EXISTING_CSS}.new" "$EXISTING_CSS"
Output the following to the user:
/service-planner auto-references DESIGN.mdcomponents.* together/design-init --regenerate-csssrc/styles/design-tokens.css must not be hand-edited| Argument | DESIGN.md exists | Behavior |
|----------|------------------|----------|
| (none) | × | init: full Step 0~7 |
| (none) | ○ | update: Step 0~7, partial-update intent asked in Step 2 |
| --regenerate-css | ○ | Step 0 + Step 6 + Step 7 only |
| --regenerate-css | × | Error: "DESIGN.md is missing. Run /design-init first" |
| --from-refs=... | × | Invoke Skill(design-extract) once → merge the generated report directly in this skill (no recursive call) → proceed Step 2~7 |
| --from-refs=... | ○ | Same as above but ask user confirmation before merging |
| --apply-extract=<path> | × | Use the report as seed to proceed Step 3~7 |
| --apply-extract=<path> | ○ | Partial-update the existing DESIGN.md from report content (after user confirmation) |
| --auto | any | All HITL above is handled with defaults |
docs/design-system/DESIGN.md. Do not change.src/styles/design-tokens.css. tokens.json or theme.ts will be handled by separate skills (future).components.md: Do not delete after migration — preserve it. Add a deprecation comment at the top noting that its content has been absorbed into Body §4.meta.version as a major bump only for breaking changes (token name change/removal). Patch bump for token value changes only.| Integration point | Behavior |
|-------------------|----------|
| /project-init | Invokes this skill with --auto during Sprint 0 init to seed the default design system |
| /service-planner Step E | References in order: DESIGN.md 1st → design-tokens.css 2nd → plugin templates 3rd |
| /handoff-publish | 6-component-specs.md cites DESIGN.md Body §4 by reference link |
| design-token-validator agent | Treats DESIGN.md Front Matter as the SSoT. CSS is a fallback. |
| /design-audit command | Validates changed files against DESIGN.md immediately |
| /design-extract skill | Extracts tokens from references and joins this skill |
| /design-redesign skill | Audits and fixes existing UI against DESIGN.md |
After authoring/edits, verify behavior with the following 3 scenarios:
docs/ exists and docs/design-system/ is absent → DESIGN.md + design-tokens.css are newly created. preview.html renders correctly.src/styles/design-tokens.css and docs/design-system/components.md already exist → integrate into DESIGN.md, convert CSS to a generated artifact (with the warning header), and mark components.md as deprecated.--from-refs=https://linear.app → /design-extract is invoked, colors/fonts are extracted and reflected in DESIGN.md, validator passes after contrast verification.Each scenario must pass Step 5.1 validation for PASS.
update mode, only sections the user confirmed are partially updated.tools
Runs UAT (User Acceptance Testing) cases in TRUE PARALLEL using Playwright Test runner with isolated browser contexts per worker (separate cookies, localStorage, sessionStorage). Solves the two main limits of /user-test: (1) sequential single-page execution that does not scale beyond a few cases, and (2) one stuck case blocking the rest of the run. Reuses 100% of the /user-test UAT case Markdown+YAML format under docs/tests/uat-cases/, runs them via `npx playwright test --workers=N`, and emits the same report layout (index.html + issues.md + session.json + screenshots/) under docs/tests/uat-reports/. Use when the user asks to "run UAT in parallel", "speed up UAT", "test multi-user", "song song", "uat parallel", or runs /uat-parallel. Distinct from /user-test (sequential Chrome MCP, supports interactive mode), /test-run (developer integration tests), /test-scenario (scenario authoring).
tools
Performs end-user UAT (User Acceptance Testing) by driving a real browser through Chrome MCP, self-verifying each step with hard assertions (DOM / Network / URL / Console), auto-assigning severity on failure, and emitting an HTML report plus issues.md into a timestamped session folder. Supports two modes: interactive (URL + Vietnamese natural-language flow description) and --auto (batch-run pre-authored test cases under docs/tests/uat-cases/). Use when the user asks for "UAT", "user acceptance test", "kiểm thử người dùng", "regression test", or runs /user-test, /uat. Distinct from /test-run (developer-authored technical integration testing) and /test-scenario (scenario authoring from blueprints).
tools
Authors and validates LLM tool descriptions and input schemas (Anthropic Tool Use, MCP servers, LangChain @tool, Pydantic, Zod). Use when the user mentions "tool description", "function calling", "MCP tool", "Pydantic schema", "Zod schema", "@tool decorator", "input_schema", "tool spec", "툴 정의", "함수 호출 스키마", or when editing files that define LLM tool surfaces. Enforces the six required attributes (one-line summary, anti-pattern, synonyms, parameter examples, enum constraints, return shape) and blocks the seven known failure modes — wrong-tool selection, skipped tool, malformed arguments, retry loops, user-intent bypass, wrong side-effect, and un-auditable traces. For authoring ASTRA SKILL.md files use /skill-author instead — this skill is for *runtime* LLM tool surfaces, not for skill files themselves.
development
Creates new SKILL.md files or refactors existing skills to comply with the ASTRA skill best practices guide (docs/development/skill-best-practices.md). Use when user mentions "new skill", "create skill", "SKILL.md", "skill authoring", "스킬 작성", "스킬 만들기", or when editing any file matching skills/**/SKILL.md.