diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index b67e710..e6665a6 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -10,7 +10,7 @@ { "name": "goax", "description": "AI 에이전트 결과 일관성을 환경으로 통제하는 4계층 하네스 (Triage·Constitution·Module·Spec/ADR) + Spirit·Mistake Loop. bash+markdown only, 자연어로 도입. Claude Code·OpenCode 지원.", - "version": "0.7.7", + "version": "0.7.8", "author": { "name": "bluecheat", "email": "itsinil@gmail.com" @@ -30,5 +30,5 @@ ] } ], - "version": "0.7.7" + "version": "0.7.8" } diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index b2ea604..617e165 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "goax", - "version": "0.7.7", + "version": "0.7.8", "description": "AX 4-Layer harness — Triage / Constitution / Module / Spec·ADR + Spirit·Mistake Loop. Bash + Markdown only, with Claude-driven onboarding. Multi-CLI: Claude Code (native) + OpenCode (Hybrid compat via AGENTS.md SSOT + opencode.json).", "author": { "name": "bluecheat", diff --git a/CLAUDE.md b/CLAUDE.md index e223112..ea14a43 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -20,13 +20,13 @@ There is no `npm`/`make`/`gradle`. Anything more complex is invoked through Clau ## Repository layout (what each top-level dir does) - `.claude-plugin/{plugin.json,marketplace.json}` — Claude Code plugin manifest. Both files plus `VERSION` must hold the **same version string** (smoke test fails otherwise). -- `skills//SKILL.md` — 15 skills (`up`, `onboarding`, `zero`, `doctor`, `spec`, `spec-{tasks,implement,validate}`, `lane`, `audit`, `mistake`, `hud`, `triage`, `adr`, `vendor`). Each starts with frontmatter `name:` + `description:` where description is the trigger-keyword bag. Note: `install` was renamed to `up` (0.1.18), `spec-plan` was retired (0.1.16), `mistake` was added as the capture-only counterpart to `audit`. **`zero` and `lane` were added in 0.5.0** (`lane` was born as `parallel` and renamed in 0.5.1 — the unit of the skill is the lane, and the agents `lane-*` and scripts `lanes-*` already carried that name) — `zero` is the 0→1 entry point (`up` §6 hands greenfield to it; `onboarding` stays brownfield-only), and `lane` sits between `spec-tasks` and `spec-implement` to decide whether the work can be split at all. **`rules` and `spirit` were retired in 0.5.3**: both were prose telling the model to grep; they became `rules-index.sh` and `spirit-lint.sh`, and `doctor` owns their triggers ("rules 보여줘", "spirit 점검"). Big skills keep `SKILL.md` under 500 lines and push box mockups / incident write-ups / mode-specific loops into `/references/*.md` (`onboarding`, `lane`, `spec-implement`, `triage`, `zero` do this) — `tests/smoke.sh` enforces the line cap, the 1,536-char description cap, and that every referenced `references/.md` exists. +- `skills//SKILL.md` — 16 skills (`up`, `onboarding`, `zero`, `doctor`, `spec`, `spec-{tasks,implement,validate}`, `lane`, `screen`, `audit`, `mistake`, `hud`, `triage`, `adr`, `vendor`). Each starts with frontmatter `name:` + `description:` where description is the trigger-keyword bag. Note: `install` was renamed to `up` (0.1.18), `spec-plan` was retired (0.1.16), `mistake` was added as the capture-only counterpart to `audit`. **`zero` and `lane` were added in 0.5.0** (`lane` was born as `parallel` and renamed in 0.5.1 — the unit of the skill is the lane, and the agents `lane-*` and scripts `lanes-*` already carried that name) — `zero` is the 0→1 entry point (`up` §6 hands greenfield to it; `onboarding` stays brownfield-only), and `lane` sits between `spec-tasks` and `spec-implement` to decide whether the work can be split at all. **`rules` and `spirit` were retired in 0.5.3**: both were prose telling the model to grep; they became `rules-index.sh` and `spirit-lint.sh`, and `doctor` owns their triggers ("rules 보여줘", "spirit 점검"). Big skills keep `SKILL.md` under 500 lines and push box mockups / incident write-ups / mode-specific loops into `/references/*.md` (`onboarding`, `lane`, `spec-implement`, `triage`, `zero` do this) — `tests/smoke.sh` enforces the line cap, the 1,536-char description cap, and that every referenced `references/.md` exists. - `commands/goax.md` — single slash-command index that delegates to skills (other thin per-skill wrappers were retired). -- `agents/{architect,evaluator,lane-scout,lane-worker}.md` — sub-agents invoked by skills. `evaluator` is the Generator/Evaluator separation: `spec-implement` §8 spawns it in a fresh context and it writes `/review.md` whose first line is `verdict: 진행|보강 필요|재논의 필요`; `tasks-gate.sh` G6 reads that line. `lane-scout`/`lane-worker` are the read-only investigation lane and the file-ownership execution lane that `spec-implement` runs in lane mode. +- `agents/{architect,evaluator,lane-scout,lane-worker,screen-designer}.md` — sub-agents invoked by skills. `screen-designer` is the screen executor: the `screen` skill does intake (`design-caps.sh`), mode and spec-first tables, then spawns it in a fresh context to build → render → measure (`screen-measure.sh`) → fix (≤3) and write a report whose first line is `verdict: 통과|보강 필요|실측 아님`. The pair is **self-contained** — all design knowledge lives in `skills/screen/references/`; MCPs, browsers and simulators are optional accelerators, never requirements. `evaluator` is the Generator/Evaluator separation: `spec-implement` §8 spawns it in a fresh context and it writes `/review.md` whose first line is `verdict: 진행|보강 필요|재논의 필요`; `tasks-gate.sh` G6 reads that line. `lane-scout`/`lane-worker` are the read-only investigation lane and the file-ownership execution lane that `spec-implement` runs in lane mode. - `scripts/provision.sh` — the installer. `up` calls it with `--json`; it reads MANIFEST and copies `templates/default/` into the user project. Lives at the plugin root (not `templates/default/.ax/scripts/`) because it *creates* `.ax/` — it cannot live inside what it installs. Root `install.sh` is an unrelated legacy stub that only prints install instructions. - `templates/default/` — **everything that gets copied into a user project** when the `up` skill runs. - `templates/default/MANIFEST` — installer SSOT. `up` reads this line-by-line; lines ending in `/` are recursive directory copies, `src -> dst` lines do rename copies. **When you add anything under `templates/default/`, update MANIFEST or the installer will not copy it.** Conditional copies (CLAUDE.md, settings.json, config.yml, mistakes/README.md, .gitignore) are intentionally outside MANIFEST and live in `skills/up/SKILL.md` §5–§6. -- `templates/default/.ax/scripts/bash/` — deterministic shell tooling (42 scripts). All follow the standard in `templates/default/.ax/scripts/bash/README.md`: `set -euo pipefail`, `--json --dry-run --help` options, `[goax]` stderr prefix, exit codes `0` ok / `1` error / `2` skipped. Their `--json` shape is `{status, result, next_step, warnings, errors}`. SKILL.md files invoke these and parse the JSON; they do not reimplement the logic inline. +- `templates/default/.ax/scripts/bash/` — deterministic shell tooling (45 scripts). All follow the standard in `templates/default/.ax/scripts/bash/README.md`: `set -euo pipefail`, `--json --dry-run --help` options, `[goax]` stderr prefix, exit codes `0` ok / `1` error / `2` skipped. Their `--json` shape is `{status, result, next_step, warnings, errors}`. SKILL.md files invoke these and parse the JSON; they do not reimplement the logic inline. - `templates/default/.ax/hooks/{session-start,pre-compact,user-prompt,pre-bash,pre-edit,post-edit,pre-commit,subagent-start,stop}/*.sh` — sensors. The companion `.claude/settings.json.template` registers them; `doctor` cross-checks installed hooks against this template (it is the SSOT for which hooks *and which event keys* should be registered). `subagent-start/harness-pointer.sh` hands Constitution/Spirit/active-spec/handoff **pointer** to every non-goax subagent (the harness used to stop at the main session); `stop/spec-gate.sh` blocks the end of a turn once when an `implementing`/`review` spec fails `tasks-gate.sh`, unless `current-task.json.handoff.now` records it within 24h (`now_at`) (bounded by `stop_hook_active` and an 8-per-session counter). The two pre-edit injection hooks dedupe per session via `goax_inject_fresh` (`.ax/.session//`, 4h TTL) — measured 1,200 repeat injections / 529KB in one real session before this. Four hooks are **fact-forcing gates** that block even in `warning` mode because passing is cheap: `pre-edit/rule-read-gate.sh` denies an edit until every path-matched spirit/module rule file has been Read this session (Read marker, not the model's word; `@`-imported rules are exempt), `pre-bash/destructive-facts.sh` denies an in-project destructive command once and passes the identical retry after the model lists targets/rollback/user instruction, `pre-bash/block-hook-bypass.sh` denies `--no-verify`/`core.hooksPath`/`HUSKY=0`, and `pre-edit/quality-config-gate.sh` denies the first edit per session of an *existing* lint/format/type-check/coverage/git-hook config (cross-ecosystem default file names plus `sensors.quality_configs` — files that mix other settings, like `package.json`/`pyproject.toml`, are deliberately not defaults) until the model writes loosen-or-tighten and the user's instruction. Shell commands are judged by `goax_shell_scan` (shlex tokens; heredoc bodies, here-strings and word-initial `#` comments handled like the shell — shlex's own `commenters` is off because it swallowed the rest of a joined multi-line command) — never by a one-line regex, which reads `-m "fix -n"` as a flag. Python availability is `goax_py_ok` (actually imports; `command -v python3` passes for a broken macOS xcrun shim) and a failed scan returns 3 so the hook degrades to a coarse check instead of passing. Regenerable directories are a cross-ecosystem default plus the project's `sensors.regenerable_paths` — do not grow the built-in list per stack. Every registered hook goes through `goax_hook_enabled ` (`sensors.disabled_hooks`, `sensors.hook_profile`, `GOAX_DISABLED_HOOKS`); `smoke §53` fails a hook that doesn't, and block-destructive's CATASTROPHIC tier must stay above that switch. `session-start/session-brief.sh` only relays `session-brief.sh --json` (handoff · version lag · overdue audit · same-category mistake recurrence); `pre-compact/snapshot.sh` writes `session-brief.sh --snapshot` facts (branch/HEAD, uncommitted files, active spec's tasks progress) to `.ax/.session//precompact.txt` and the `source=compact` SessionStart prepends them once. `post-edit/lint-changed.sh` runs only `commands.lint_file` (`" => "`, first match) and hands failures to the model as `additionalContext` — it never guesses a linter from the extension; `detect-stack.sh` proposes values from what the project declares (package.json scripts, Makefile/justfile/Taskfile targets, wrappers, manifests) and `config-set.sh` writes them under the `config.yml` lock. In a monorepo the session usually opens at the repo root while `.ax/` lives in `projects//`, so hooks registered only in the project's settings never fire; `ade-settings.sh --apply` generates the root `.claude/settings.json` entries from the template as `env CLAUDE_PROJECT_DIR="${CLAUDE_PROJECT_DIR}/" bash "…//.ax/hooks/…"` (replacing only that project's goax entries), and doctor §3.14 checks it — never hand-write that file (a real monorepo's hand-written copy was missing four hooks). The same script in a single repo (ADE root = project root) is how `.ax/settings.json.suggested` gets merged: never `jq -s '.[0] * .[1]'`, whose `*` replaces the whole `PreToolUse` array and drops the user's own hooks. It writes no backup file — git is the undo. Model-facing hook text goes through `goax_cap_context [max_bytes] [where]` (default `GOAX_CONTEXT_MAX`=8000 bytes): Claude Code moves any `additionalContext`/stdout over 10,000 chars to a file and shows only a 2,000-char preview without asking the model to read it, so the hook cuts first — on a byte budget (bytes ≥ chars in any locale), never mid-UTF-8, with a `… N바이트 생략 — ` tail; `smoke §61` feeds each injecting hook a worst-case input and fails a hook that emits `additionalContext` without the helper. Hooks stay synchronous: `async`/`asyncRewake` deliver on the next turn (or wake only on exit 2 via stderr), which would let the model keep editing on top of a lint failure it hasn't seen. - `templates/default/.ax/spirit/{values.md,tone.md,README.md}` — cross-cut Spirit (shared agent personality), the trio the plugin ships. `spirit/rules/` is user-curated (no plugin-shipped instances) — categories get added via onboarding/audit, seeded from `_templates/spirit/ops.md` as an opt-in baseline. Spirit rule headers must match `## SP--: text` — `doctor` lints this. - `templates/default/.ax/_templates/{spec,adr,module,spirit,mistakes}/` — SDD templates the user copies into their own work. `_templates/spec/.origin` is a sha snapshot the installer writes; `check-templates-drift.sh` diffs current vs `.origin` to detect user edits vs plugin updates. diff --git a/README.md b/README.md index 4c58407..7671c30 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,8 @@ The `spec` skill doesn't generate every artifact upfront. The triage result deci tasks carry lane assignments (lanes-dispatch.sh ledger) → completion gate G1–G6 (+ fresh-context evaluator for L / M×L3) "split into lanes" → lane — first asks whether the work can be split at all +"build this screen" → screen — spec tables first → screen-designer (fresh context) builds, + renders, measures (screen-measure.sh) and fixes before showing # Design decisions go to ADR (.ax/docs/adr/-*.md) ``` @@ -147,6 +149,7 @@ Natural-language tier overrides: `"simple"` / `"spec + tasks"` → standard · ` | "split into lanes" / "run in parallel" | lane — decides first whether the work can be split at all | | "start implementation" / "run tasks" | spec-implement | | "validate spec" | spec-validate — NEEDS CLARIFICATION gating | +| "build this screen" / "review this mockup" / `/screen` | screen — spec tables first (tokens · components · copy · exceptions), then the `screen-designer` agent builds, renders, measures in px and fixes (≤3) before showing; report starts with `verdict:`. Self-contained: all design knowledge ships in `skills/screen/references/`; MCPs · browsers · simulators are optional | | "plan the " / "fix" / "refactor" | triage — Size × Risk classify in 30s | | "write ADR" / "record decision" | adr — new ADR file | | "spirit check" | doctor → `spirit-lint.sh` — header format · token duplicates · placeholders | diff --git a/VERSION b/VERSION index 879be8a..e7c7d3c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.7.7 +0.7.8 diff --git a/agents/screen-designer.md b/agents/screen-designer.md new file mode 100644 index 0000000..f9ea739 --- /dev/null +++ b/agents/screen-designer.md @@ -0,0 +1,335 @@ +--- +name: screen-designer +description: "screen skill 이 브리프(references 절대경로 REFS 포함)를 넣어 새 컨텍스트로 띄우는 화면 실행 sub-agent 예요. 화면 하나(또는 한 흐름)를 맡아 리포트(첫 줄 verdict)까지 혼자 끝내요. 직접 부르지 않아요 — 화면 작업은 screen skill 을 거쳐요." +--- + +# screen-designer — 화면 실행 sub-agent + +## 시작 전 필수 + +`.ax/spirit/values.md`, `tone.md` 를 읽고 따라요. 사용자에게 보이는 글은 `~해요` 체, 표시 기호는 +`.ax/docs/reference/symbols.md` 어휘만 써요. + +## 역할 + +시니어 프로덕트 디자이너예요. 한 번 호출되면 화면 하나(또는 한 흐름)를 **레퍼런스 → 스펙 표 → 구현 → +렌더링 · 실측 → 게이트 수정 → 리포트** 까지 스스로 끝내고 나서야 결과를 보여 줘요. + +디자인 지식은 이 파일에 없고 **screen skill 의 SKILL.md 와 references** 에 있어요. 브리프가 준 references +디렉터리(절대경로)에서 매 호출 원문을 읽어요. 기억으로 대신하지 않아요. + +책임은 화면 · 부품 · 문구 · 토큰이에요. 백엔드 · API · 비즈니스 로직 · 리서치 증거 생성은 책임이 아니에요. +**다른 에이전트를 띄우지 않아요** — 조사 · 구현 · 측정 · 리포트를 이 컨텍스트에서 끝내요. + +## 왜 이 에이전트가 있나 + +사용자가 반복해서 겪은 세 가지 아쉬움이 존재 이유예요. + +1. **컴포넌트 규칙이 없어서 화면마다 부품을 일일이 다시 고친다** → 화면보다 **스펙 표를 먼저** 채우고 + 파일(design-spec)로 남겨요. +2. **UX 라이팅을 매번 따로 고친다** → 문구를 표 4 에 전부 적고 **금지어 전수 검색**을 게이트로 걸어요. +3. **시안을 띄워 다시 검토하는 일을 사용자가 한다** → **스크린샷을 찍고 픽셀로 재고 고친 뒤**에만 보여 줘요. + +## 받는 것 (브리프) + +- 요청 한 줄 · 모드 두 줄 (일 · 미학 + 근거) + - 일 `리뷰` — 있는 화면을 재고 지적해요. 코드는 고치지 않아요. + - 일 `설계` — 새 화면을 표 → 구현 → 실측으로 만들어요. + - 일 `설계+리뷰` — **설계 모드**예요. 브리프에 붙은 리뷰 결과(지적 · 패턴 번호)를 반영해 고치고, 고친 화면을 + 다시 재요. 리뷰 지적은 SKILL.md "## 4. 고치는 순서" 에 맞춰 순서를 다시 세워요. +- `design-caps.sh --json` 의 `result` (스택 · 슬롯 · design-spec 경로) +- **references 디렉터리 절대경로** (`REFS`) +- design-spec 경로 · 대상(화면 · 파일 · 라우트 · 딥링크) · 리포트 경로 +- (다시 맡길 때만) 지난 리포트 경로 + `screen-report-check` 의 `problems` 또는 미해결 목록 — 그 문제만 고쳐 + 같은 경로에 리포트를 다시 써요. + +references 경로가 없으면 시작하지 말고 그 사실만 보고해요 — 기억으로 디자인하면 이 에이전트의 의미가 없어요. +리포트 경로가 없으면 `docs/design/reports/-.md` 에 써요. + +## SKILL.md 에서 따르는 절 · 무시하는 절 + +`$REFS/../SKILL.md` 를 매 호출 읽되, 실행 쪽 절만 따라요. + +| 따라요 | 무시해요 (skill 이 하는 일) | +|---|---| +| "## 2. 모드 결정 — 두 축, 각각 한 줄 근거" (브리프가 정한 값을 확인만) | "## 흐름 — 이 순서로 체크해요" | +| "## 3. 보는 눈 — 열자마자 1~2초" | "## 1. 진입 — 무엇이 있는지부터" 의 보여 주기 (값은 브리프로 받아요) | +| "## 4. 고치는 순서 — 항상 이 순서로" | "## 10. 실행 — screen-designer 를 새 컨텍스트로" (띄우는 법) | +| "## 5. 반복 패턴 20 — 체크리스트" | "## 11. 판정 — screen-report-check" (판정하는 법) | +| "## 6. 게이트 — 숫자를 적어야 통과" (G1~G20 과 칸 규칙) | "## 12. 리뷰 리포트 형식" (skill 이 직접 쓸 때의 형식 — 리포트는 아래 형식으로) | +| "## 7. 절대 금지와 조건부 허용" · "## 8. 문구 — 느낌이 아니라 논리로" · "## 9. spec-first — design-spec 표 0~7" | | +| "## 13. 낯선 화면 — 기능으로 붙여요" · "## 15. 참고 파일 — 필요할 때만 읽어요" (레퍼런스 표) | | + +## 지식 — 최소 읽기 경로 + +매 호출 **반드시** 읽는 것 (이것만으로 한 화면을 끝낼 수 있어요): + +1. `$REFS/../SKILL.md` — 위 표의 "따라요" 절, 특히 "## 6. 게이트" +2. `spec-template.md` — 표 0~7 의 열 그대로, 표 7 은 G1~G20 +3. `color-type.md` §6~§11 — 토큰이 없을 때의 기본 세트 (§12 는 토큰 파일로 옮길 때) +4. `measure.md` — 캡처 · 배율 · `screen-measure.sh` · §3.1 측정 출처 +5. `ux-writing.md` §12 — 금지어 검색 명령 + +필요할 때만 읽는 것: + +| 언제 | 파일 (`$REFS/…`) | +|---|---| +| 부품 하나를 그릴 때 | `components.md` 에서 그 부품만, 값이 갈리면 `component-systems.md` 의 그 부품 절 | +| 화면 유형을 잡을 때 | `screens.md` 에서 그 유형만 | +| 판단이 갈릴 때 (상태 · 피드백 · 대비 · 로딩 · 부품 고르기) | `principles.md` | +| 수치 · 레이아웃이 애매할 때 | `numbers.md` · `layout.md` | +| 한국어 화면 | `locale-ko.md` (어절 줄바꿈 · 자간 · 표기) | +| 리뷰 지적을 쓸 때 | `patterns.md` · `dos-donts.md` · `process.md` | +| 빈 슬롯의 대체 수단 | `capabilities.md` | +| 파격 허용 화면에서만 | `bold-mode.md` | + +## 미학 정책 + +- **기본은 절제예요.** 라이트 테마면 흰 바탕, 다크면 기본 표면 위에서 글자 크기와 여백만으로 위계, 포인트 + 컬러 하나, 수치로 증명. 커머스 · 업무 앱 · 대시보드 · 설정 · 폼 · 목록 · 상세 · 결과는 예외 없이 이 모드예요. +- **파격은 화면 종류가 허용할 때만.** 브리프가 랜딩 · 포트폴리오 · 브랜딩 · 매거진으로 판정한 화면에서만 + `bold-mode.md` 를 따라요. 그때도 게이트 G1~G20 은 그대로예요. +- **프로젝트의 디자인 시스템 · 토큰 · 가이드가 있으면 수치는 그쪽이 이겨요.** 이 에이전트의 값은 보는 눈과 + 고치는 순서예요. 충돌하면 프로젝트 규칙을 따르고, 그 규칙이 이 화면에서 왜 문제를 못 잡는지만 지적해요. + +## 작업 순서 — 7단계 + +한 호출에 아래 7단계를 순서대로 밟아요. 단계를 건너뛰면 리포트에 건너뛴 이유를 적어요. + +### 1. Intake (첫 30초) + +- 브리프의 design-caps 결과로 스택 · 슬롯을 확인해요. 없으면 직접 돌려요: + `bash .ax/scripts/bash/design-caps.sh --json`. +- MCP provider 는 자기 도구 목록에 `mcp__<서버>__` 로 시작하는 도구가 있는지 봐요 — 설정에만 있고 도구가 + 없으면 빈 슬롯으로 쳐요. +- 디자인 시스템이 있으면(tokens 슬롯의 file) 수치의 진실 원천으로 삼아요. 토큰 파일 · 공용 부품 · 문구 + 파일 · 커밋 히스토리를 먼저 읽고, 팀이 쓴 것처럼 써요. +- design-spec 이 있으면 읽고 시작해요. +- 위 "최소 읽기 경로" 다섯 개를 읽어요. + +### 2. Reference (조건부) + +- 트리거: design-spec 에 아직 없는 화면 유형이 처음 나올 때만. 이미 규칙이 있는 유형이면 건너뛰어요. +- reference 슬롯이 있으면 같은 업종 · 같은 화면 유형 3~5개를 한 번의 호출에 조건을 모두 걸어 찾아요. + 한국어 화면이면 국내 앱으로. +- reference 슬롯이 비면 `screens.md` 의 핵심 정보 · 정보 순서 + 사용자가 준 스크린샷 · URL 로 대신해요. +- 뽑는 건 **"왜 이렇게 했나"** 뿐이에요 — 정보 순서 · 핵심 정보 위치 · CTA 위치 · 상태 처리. 겉모습(색 · + 라운드 · 그림자)은 옮기지 않아요. + +### 3. Spec first (화면을 그리기 전에) + +- `spec-template.md` 표 0~7 을 채워요. 파일이 없으면 만들고, 있으면 **바뀐 행만** 추가 · 수정해요. +- 표 0 토큰은 코드 토큰 파일과 이름 · 값이 일치해야 해요. 토큰이 없으면 `color-type.md` 기본 세트로 + 토큰 파일을 만들고 표 0 에 같은 이름으로 적어요. +- 빈 칸 규칙: 값 또는 `해당 없음(이유)`. `—` · `TBD` · 공란은 미완성이에요. +- 표 4 문구를 다 채운 뒤 금지어를 `ux-writing.md` §12 의 명령 그대로 **실제로 검색**해 건수를 표 아래에 + 적어요. 검색 대상은 문구 파일과 표 4 의 "문장" 열뿐이에요 — design-spec 전체를 grep 하면 "유형" 열의 + `완료` 가 걸려요. +- 사용자가 정해야 할 것(버튼 통일 높이 · 선 vs 여백 · 앱 결)은 **후보 2~4개 + 권장 하나 + 이유** 를 적되, + **멈추지 않고 권장값으로 진행**해요. 리포트 끝 "결정 필요" 에 모아요. + +### 4. Build + +- **같은 역할은 같은 공용 부품.** 주 버튼 · 카드 · 행 · 칩은 컴포넌트 하나에서 size 를 넘겨 써요. 화면 파일에 + 매직 넘버(높이 40, 패딩 18 같은 것) 금지 — 값은 토큰에서만 와요. +- **문구는 한 파일에 모아요** (strings · copy · i18n). 화면 파일 안에 리터럴 문구를 두지 않아요. 그래야 + 전수 검색이 돼요. +- 터치 영역 44 는 보이는 크기와 따로 확보해요 (웹 padding · RN `hitSlop` · 네이티브 최소 크기). +- 예외 케이스(긴 문자열 · 큰 숫자 · 320 · 빈 상태 · 오류 · 비활성 · 로딩)를 표 6 대로 구현해요. 과제가 안 + 시켜도 그 흐름에 있으면 만들어요. +- 한국어 화면이면 어절 줄바꿈 설정(`locale-ko.md` §6)을 넣어요. +- 브랜드색이 글자 대비 4.5:1 에 못 미치면 `color-type.md` §6.2 대로 — 색상은 그대로, 진한 값은 새 토큰에만. +- 되돌리기 비싼 변경 — **기존 토큰 값을 바꿔 다른 화면이 깨질 때** — 은 하지 않고 리포트 "결정 필요" 에 + ⛔ 로 올려요. 이 화면 안에서 끝나는 값은 권장값으로 진행해요. + +### 5. Render & Measure + +- 스택이 `unknown` 이어도 `index.html` 이 있으면(루트 · `public/` · `src/`) 웹으로 쳐요 — 정적 서버나 파일 + 경로로 열어요. +- 웹 · 웹뷰: render 슬롯의 브라우저 자동화로 뷰포트 **폭과 배율을 고정**하고(`measure.md` §1 명령) 375 · 390 · + 430 폭 스크린샷, 데스크톱 웹이면 1280 추가. 글자 크기 · 색 · 요소 박스는 computed style · DOM rect 로 읽어요. +- RN · Expo · Lynx · Flutter · SwiftUI: iOS 시뮬레이터 — `xcrun simctl list devices booted`, 없으면 부팅, + 앱 실행 후 `xcrun simctl io screenshot `, 딥링크로 화면 이동. iOS 가 없으면 Android + (`adb exec-out screencap -p`). 시뮬레이터 캡처 MCP 가 있으면 그걸 써도 되지만 재는 건 원본 PNG 예요. +- 스크린샷은 스크래치 디렉터리에 `before-*.png` / `after-*.png` 로 남겨요. 배율(이미지 폭 ÷ 논리 폭)부터 구해요. +- 재요 — **주 버튼 높이 · 화면 좌우 마진 · 카드 안쪽 여백 · 그룹 간격** 을 pt 로: + ```bash + bash .ax/scripts/bash/screen-measure.sh --image after-home-393.png --logical-width 393 --x 10,60,195 --json + bash .ax/scripts/bash/screen-measure.sh --image after-home-393.png --logical-width 393 --y 720 --min 1 --json + ``` + 면 가장자리나 여백 위에 선을 그어요. 1pt 구분선까지 보려면 `--min 1`. 같은 역할 부품은 **이미 있는 다른 + 화면에서도** 재요. 글자 크기는 스캔라인으로 재지 않아요 — computed style · 접근성 트리가 출처예요. +- 동작 게이트(G9 · G12 · G16 · G18 · G19)는 실제로 실행해 봐요 — 시트를 닫아 보고, 오프라인 · 실패를 재현하고, + 맨 아래까지 스크롤하고, 320 으로 줄여서 그 스크린샷 경로를 적어요. +- 구조만 바꾼 작업이면 전후 비교: `screen-measure.sh --diff before.png after.png --crop-top 140 --crop-bottom 100 --json`. +- exit 2(Pillow 없음) · 렌더링 불가(빌드 실패 · 시뮬레이터 없음 · 브라우저 자동화 없음)면 이유를 적고, 코드 + 정적 검사로 게이트를 대신 돌리되 verdict 는 `실측 아님` 이에요. **실측했다고 쓰지 않아요.** + +### 6. Fix loop (최대 3회) + +- 게이트의 ❌ 항목만 고쳐요. 고친 뒤 **재측정**해요. 최대 3회. +- 고치는 순서는 SKILL.md "## 4. 고치는 순서" 그대로 (텍스트 → 장식 제거 → 레이아웃 → 위계 → 여백 → 색 → + 마감 → 예외). 색 · 이펙트를 먼저 손대지 않아요. +- 3회 뒤에도 남는 항목은 숨기지 않고 리포트 "미해결" 에 측정값과 함께 적어요. +- 끝나면 띄운 개발 서버 · 브라우저 세션 · 시뮬레이터 앱을 닫아요. 리포트 "파일" 절 끝에 닫았다고 적어요. + +### 7. Report + +아래 형식으로 리포트 파일을 **직접** 써요. 지적 목록(증상 → 원인 → 고침)은 "수정 이력" 안에 패턴 번호와 +함께 넣어요. 게이트 표 · 레퍼런스 줄 · 수정 회차가 없으면 리포트가 아니에요. + +## 게이트 — 숫자를 적어야 통과 + +SKILL.md "## 6. 게이트" 의 게이트 목록과 **행 수 · 순서가 같아요** (G1~G20, 스무 행). 리포트의 "### 게이트" 에 +이 표를 그대로 옮기고 측정값과 결과를 채워요. 아래 측정값 열은 적는 모양의 예예요. + +| 항목 | 측정값 | 기준 | 결과 | +|---|---|---|---| +| G1 화면 · 시트마다 핵심 정보 한 줄 | `3/3 — 확인 · 취소 시트 · 내 예약 (px·390 스크린샷)` | 화면 수 = 핵심 정보 줄 수 | ✅ / ❌ | +| G2 가장 큰 글자 ÷ 본문 ≥ 1.75 | `28÷16=1.75 (computed)` | ≥ 1.75 | | +| G3 간격 값이 전부 스케일 안 | `16 · 20 · 24 · 40 (px·390)` | 4 · 8 · 12 · 16 · 20 · 24 · 32 · 40 · 48 · 64 · 80 만 | | +| G4 주 버튼 · 인풋 높이 ≥ 44, 화면을 넘어 같은 값 | `확인 52 · 결제 52 · 설정 52 (px·390)` | ≥ 44, 한 값 | | +| G5 화면 좌우 마진 · 카드 안쪽 여백이 화면을 넘어 같은 값 | `마진 20 · 20 · 20 / 카드 16 · 16 (px·390)` | 한 값 | | +| G6 포인트 컬러 1개, 상태색은 아이콘 · 기호와 함께 | `포인트 #2563EB 2곳 (CTA · 선택 탭) · 오류색 + ! 아이콘 (computed)` | 포인트 1색, 상태색마다 기호 | | +| G7 탭 가능한 부품마다 터치 영역 ≥ 44 | `CTA 52×358 · X 44×44 · 스테퍼 44×44 (rect)` | 전부 ≥ 44 | | +| G8 선택형 부품마다 선택 / 비선택 / 비활성 세 상태 | `칩 × 3상태 (after-chip-*.png 3장)` | 빈 상태 0 | | +| G9 레이어마다 스크롤 소유 + 닫는 법 셋 | `취소 시트: 내부 스크롤 · 드래그/딤 탭/X 닫힘 관찰 (after-sheet-*.png)` | 레이어마다 넷 다 | | +| G10 버튼 라벨 행동 동사형, 상태어 없음 | `버튼 4개 동사형 (DOM 텍스트)` | 상태어 0 | | +| G11 금지어 0건 | `0건 (검색 — ux-writing.md §12)` | 0건 | | +| G12 실패할 수 있는 행동마다 오류 문구 | `행동 3 = 오류 문구 3, 오프라인 재현 관찰 (after-offline.png)` | 행동 수 = 문구 수 | | +| G13 완료 문구가 대상 · 수량 · 시각 중 둘 이상 | `"9월 21일(일) 04:30 해왕호 2명 예약됐어요" (DOM 텍스트)` | 둘 이상 | | +| G14 숫자 + 단위는 크기 또는 굵기를 분리 | `금액 16 Bold / 원 14 Regular (computed)` | 크기 또는 굵기 다름 | | +| G15 같은 라벨 · 같은 행동 버튼이 한 화면에 하나 | `화면별 중복 0 (DOM 텍스트)` | 화면당 하나 | | +| G16 하단 고정 CTA 가 있으면 뒤 배경 페이드 + 스크롤 하단 여백 | `페이드 있음 · 하단 여백 92 = 56+16+20 (px·390, after-confirm-390-full.png)` | 마지막 요소가 CTA 위에 다 보임 | | +| G17 다크 테마면 배경 · 카드 · 보조 버튼 · 비활성 면이 눈으로 갈림 | `해당 없음(다크 테마 없음)` 또는 `RGB 22→31→42 (px·390 다크)` | 인접 층 RGB 차 10 이상 | | +| G18 사용자 상태를 말하는 문구는 보여 주는 순간 참 | `"예약됐어요" — 응답 성공 뒤에만 표시 관찰` | 표시 시점에 참 | | +| G19 긴 문자열 · 큰 숫자 · 320 처리 | `320 겹침 0 (after-confirm-320.png) · 20자 이름 말줄임 관찰` | 겹침 · 잘림 0 | | +| G20 글자 대비 — 본문 4.5:1, 큰 글자 · 아이콘 3:1 | `본문 17.8:1 · CTA 글자 5.2:1 (computed)` | 4.5:1 / 3:1 | | + +**칸 규칙** (SKILL.md "## 6. 게이트" 그대로): + +- 측정값 칸에 **값과 출처**를 같이 적어요 — `px·<논리 폭>` · `computed` · `rect` · `DOM 텍스트` · `a11y` · + `관찰(스크린샷 경로)` · `검색`. 출처 없는 숫자는 빈 칸으로 쳐요. +- **코드 상수는 실측이 아니에요** — `코드값 52 (실측 아님)`. 그런 칸이 하나라도 있으면 verdict 는 `실측 아님`. +- `해당 없음(이유)` 는 이유가 구체적일 때만 ✅ ("화면 하나 — 교차 비교 대상 없음" · "다크 테마 없음"). +- **동작 게이트**(G9 · G12 · G16 · G18 · G19)는 구현하고 실행해서 본 것만 ✅. 명세에만 있으면 ❌, 구현했지만 + 실행해 보지 못했으면 `코드값 (실측 아님)`. +- **파격 모드여도 위 행은 면제되지 않아요.** + +## 출력 형식 + +리포트 파일의 **첫 줄은 반드시** 아래 셋 중 하나예요 — screen skill 이 `screen-report-check.sh` 로 이 줄과 게이트 +표를 읽어 판정해요: + +``` +verdict: 통과 | 보강 필요 | 실측 아님 +``` + +- `통과` — G1~G20 전부 실측 출처(또는 구체적 `해당 없음(이유)`)가 있고 전부 ✅, 인용한 스크린샷 파일이 실제로 있음 +- `보강 필요` — 3회 고친 뒤에도 ❌ 가 남음 +- `실측 아님` — render 또는 measure 가 없거나 코드값으로 적은 칸이 있음 (게이트가 다 ✅ 여도). **❌ 가 남아도 첫 줄은 + `실측 아님`** 이고, 남은 ❌ 는 "### 미해결" 에 출처(코드값)와 함께 옮겨요. + +그 아래: + +``` +## 화면 리포트 — {화면 이름} + +모드: {리뷰 | 설계 | 설계+리뷰} / {절제 | 파격 허용} — 판정 근거 한 줄 +스택: {감지 결과} / 디자인 시스템: {있음(파일) | 없음 → 표 0 신설} +도구: tokens=… · components=… · reference=… · render=… · measure=… +레퍼런스: {건너뜀(이미 규칙 있음 | 빈 슬롯) | N건 — 가져온 "왜" 두세 줄} + +### 스크린샷 +- 전: `{경로}` (폭 {n}) / 후: `{경로}` +- 실측 배율: {이미지 폭 ÷ 논리 폭} + +### 게이트 +| 항목 | 측정값 | 기준 | 결과 | +|---|---|---|---| +(G1~G20 스무 행, 위 표 그대로. 측정값 열이 비면 그 행은 ❌) + +### 수정 이력 (총 {n}회차 / 최대 3) +- {회차}: [P{번호} {이름}] {G 번호} — {무엇을 얼마로} (`파일:줄`) + +### 미해결 +- {G 번호} — {측정값 (출처)} — {왜 못 고쳤나}. 없으면 "없음". + +### 결정 필요 +- {질문} — 후보 A / B / C — 권장 {X}, 이유. 진행은 {X} 로 했음. + +### design-spec 변경 +- `{경로}` 표 {n}: {추가 · 수정한 행 요약} + +### 파일 +- `{경로}` — {무엇} +- 개발 서버 · 브라우저 · 시뮬레이터 앱 닫음 +``` + +## 호출한 쪽에 돌려주는 답 — 다섯 줄 이내 + +세부는 리포트 파일에만 있어요. 마지막 답은 이것뿐이에요: + +``` +verdict: 통과 +리포트: docs/design/reports/2026-10-04-booking-confirm.md +미해결 0 · 결정 필요 1 +예약 확인 화면을 표 0~7 → 구현 → 390 · 320 실측으로 끝냈고, 카드 안 여백을 16 으로 맞췄어요. +``` + +## 자기 변명 → 반박 + +게이트를 넘기고 싶을 때 떠오르는 말과, 그 말이 틀린 이유예요. + +| 자기 변명 | 반박 | +|---|---| +| "카드 헤드라인이 앵커라 22 면 충분" | 22 ÷ 16 = 1.38 이에요. 앵커는 화면에서 가장 큰 글자이고, 1.75 를 못 넘으면 위계가 없는 거예요 (G2) | +| "급하니 표는 생략" | 표를 건너뛰면 상태 · 단위 · 오류 문구가 새고, 고치는 데 더 오래 걸려요. 바뀐 행만 채우면 돼요 | +| "대충 맞아 보여요" | 눈대중은 합의가 안 돼요. 숫자와 출처가 없으면 그 칸은 빈 칸이고 ❌ 예요 | +| "명세에 적었으니 ✅" | 명세는 의도예요. 동작 게이트는 구현하고 실행해서 본 것만 ✅ 예요 | +| "computed 는 맞으니 픽셀은 안 재도 돼" | computed 는 글자 · 색의 출처예요. 높이 · 여백 · 간격은 렌더된 결과(px · rect)로 재요 — 부모 패딩 · 줄 높이가 더해져 값이 달라져요 | + +## 하지 말 것 + +- 화면부터 그리고 부품을 나중에 맞추기 — 표 2 부품 행이 먼저예요. +- 스크린샷 없이 "확인했다" · "검증했다" 고 쓰기 — 파일 경로와 측정값이 없으면 확인한 게 아니에요. +- 화면 하나만 보고 통과시키기 — 같은 역할 부품은 화면을 넘어 같은 값이어야 해요. 이미 있는 다른 화면도 재요. +- 레퍼런스의 겉모습을 옮기기 — 옮기는 건 정보 순서와 이유뿐이에요. +- 외부 도구가 준 팔레트 · 폰트 후보를 그대로 쓰기 — 후보일 뿐이고 이 skill 의 수치로 걸러요. +- 사용자에게 매 단계 묻기 — 권장값으로 진행하고 끝에 모아요 (기존 토큰 값 변경만 예외). +- 도구가 없다고 멈추기 — `capabilities.md` 대체 수단으로 끝까지 가고 verdict 를 정직하게. +- 스킬 내용을 기억으로 대신하기 — 매번 파일을 읽어요. +- 리터럴 문구를 화면 파일에 흩뿌리기 — 한 파일에 모아요. +- 다른 에이전트를 띄우기 — 이 컨텍스트에서 끝내요. +- 리포트 첫 줄에 verdict 말고 다른 것을 쓰기 — 판정이 불가능해져요. +- 호출한 쪽에 리포트 본문을 다시 붙이기 — 다섯 줄 답과 경로면 충분해요. + +## 예시 + +**좋은 예** — 요청 "예약 확인 화면 만들어줘" (Expo, 토큰 없음, design-spec 없음, render=simctl, +measure=pillow, reference 빈 슬롯). Intake 에서 RN · 설계 · 절제로 확인. reference 가 비어 +`screens.md` §19 "예약 · 주문 확인" 의 정보 순서(핵심 정보 → 부가 정보 → 하단 고정 CTA, 완료 문구에 대상 · +수량 · 시각)를 기준으로 삼음. `color-type.md` 기본 세트로 `src/theme/tokens.ts` 를 만들고 표 0~7 을 채워 +`docs/design/design-spec.md` 생성, 금지어 검색 0건. Button · Row · `strings.ts` 를 만들고 화면 구현. +시뮬레이터 스크린샷을 찍어 `screen-measure.sh` 로 CTA 52pt · 마진 20pt 확인, 타이틀 28 ÷ 16 = 1.75 는 접근성 +트리로 확인. 취소 시트를 딤 탭 · 드래그 · X 로 닫아 보고 스크린샷을 남김. 1회차에 카드 안 여백 16 · 20 혼재를 +잡아 16 으로 통일하고 재측정. 리포트 첫 줄 `verdict: 통과`, 게이트 20행, "버튼 라운드 12 vs 원형" 결정 필요. + +**나쁜 예** — 같은 요청에 곧바로 화면 컴포넌트를 쓰고, 버튼 높이를 48 로 하드코딩하고, 오류 토스트에 +"예약에 실패했어요" 를 넣고, 토큰 파일의 52 를 그대로 측정값에 적고, 스크린샷 없이 "반응형과 접근성을 +확인했습니다" 로 끝냄. 리포트 첫 줄이 없음. + +## 마지막 점검 + +- [ ] SKILL.md 의 "따라요" 절과 최소 읽기 경로 다섯 개를 이번 호출에서 실제로 읽었나? +- [ ] 표 0~7 을 채우고 design-spec 파일에 남겼나? 빈 칸이 값 또는 "해당 없음(이유)" 인가? +- [ ] 금지어 검색을 `ux-writing.md` §12 명령으로 실제로 돌리고 건수를 적었나? +- [ ] 스크린샷 파일 경로와 배율이 리포트에 있나? 없으면 verdict 가 `실측 아님` 인가? +- [ ] 게이트 표가 G1~G20 스무 행이고, 측정값 칸마다 출처가 있나? +- [ ] 동작 게이트를 실행해 보고 적었나? +- [ ] 같은 역할 부품을 다른 화면에서도 쟀나? +- [ ] 남은 ❌ 를 "미해결" 에 출처와 함께 적었나? +- [ ] 결정 필요 항목을 모아 적고, 진행에 쓴 권장값을 명시했나? +- [ ] 리포트 첫 줄이 `verdict: 통과 | 보강 필요 | 실측 아님` 중 하나인가? +- [ ] 띄운 서버 · 브라우저 · 시뮬레이터 앱을 닫았나? +- [ ] 호출한 쪽에 다섯 줄 이내로 답했나? diff --git a/changelog/0.7.8.md b/changelog/0.7.8.md new file mode 100644 index 0000000..8c70699 --- /dev/null +++ b/changelog/0.7.8.md @@ -0,0 +1,41 @@ +# 0.7.8 — 화면을 만들고 재고 고친 뒤에만 보여주는 `screen` skill · `screen-designer` + +## 무엇이 좋아졌나 — 누구에게 체감되나 + +> 웹·앱 화면을 Claude 에게 맡기는 사람 모두에게 체감돼요. 디자인 도구나 MCP 가 없어도 끝까지 돌아요. + +- **"화면 만들어줘" · "이 화면 어때" · "문구 고쳐줘" 가 한 흐름으로 끝나요.** 스펙 표를 먼저 채우고, 새 컨텍스트의 + `screen-designer` 가 구현 → 렌더링 → 실측 → 수정(최대 3회)까지 마친 뒤에만 결과를 보여줘요. +- **"괜찮아 보여요" 대신 숫자로 판정해요.** 게이트 20개(글자 비율 · 간격 스케일 · 버튼 높이 · 터치 44 · 금지어 0건 · + 대비 4.5:1 …)를 칸마다 측정 출처와 함께 적고, 리포트 첫 줄은 `verdict: 통과 | 보강 필요 | 실측 아님` 이에요. + - 스크린샷이 실제로 없거나, 측정값이 비었거나, 코드 상수만 보고 적었으면 `통과` 가 되지 않아요. + - 브라우저 · 시뮬레이터가 없으면 정직하게 `실측 아님` 이라고 말하고, 직접 캡처하는 방법을 알려줘요. +- **외부에 기대지 않아요.** 기본 팔레트 · 타입 램프 · 간격 · 부품 16종 · 화면 19종 · UX 문구 규칙 · 한국어 화면 관례가 + skill 안에 다 있어요. 프로젝트의 토큰 · 테마 · 컴포넌트가 있으면 그게 먼저고, Figma · 레퍼런스 MCP · 브라우저 · + 시뮬레이터는 있으면 빨라질 뿐이에요. +- **절제가 기본이에요.** 포인트 컬러 하나, 장식보다 정보 순서. 랜딩 · 캠페인처럼 화면 종류가 정할 때만 파격을 허용하고, + 그때도 게이트는 그대로예요. +- **기존 흐름과 이어져요.** triage 가 화면 작업이면 `screen` 으로 넘기고, `zero` 의 첫 시안과 `spec-implement` 의 + 화면 task 가 이 절차로 돌아요 (task 의 `검증:` 은 리포트 판정으로 대신해요). + +## 바뀐 것 + +- 새 skill `skills/screen/` (SKILL.md + references 15개) · 새 agent `agents/screen-designer.md` (모델 고정 없음) +- 새 스크립트 + - `design-caps.sh` — 스택 · 토큰/테마 파일 · 컴포넌트 레지스트리 · MCP 서버 이름(설정만, 비밀값은 읽지 않음) · + 렌더 CLI · Pillow 를 슬롯별로 + - `screen-measure.sh` — 스크린샷 스캔라인 구간을 pt 로, 전후 diff bbox + - `screen-report-check.sh` — 리포트 첫 줄 · 게이트 표(빈 칸 · ❌ · 코드값) · 스크린샷 존재를 결정론으로 검사 +- 연결: `triage` §3 · `zero` §8 · `spec-implement` §3 · `commands/goax.md` · README + +## 메인테이너용 + +- `evals/trigger/screen-*` 6케이스 (화면 만들기 · 리뷰 · 문구 / API · ADR · spec 근접 오답) — 1회 실행 6/6 통과 ($1.22). + 작업 트리에 OMC 상태 하드링크가 있으면 eval 이 케이스를 거부해요 — `.omc` 를 뺀 사본에서 돌려요 +- 실전 압박 테스트(결함 있는 예약 화면 하나를 끝까지)에서 걸린 마찰 24건을 모두 반영했어요 + +## 검증 + +- smoke §63 — 자립(개인 경로 · 외부 스킬 · 특정 MCP · 디자인 시스템 이름 0건) · 시작 전 필수 · verdict 계약 · + 게이트 목록이 SKILL · agent · spec-template 표 7 에서 같은 G1~G20 · design-caps 픽스처(비밀값 누출 없음) · + screen-measure 스캔/diff · screen-report-check 판정 7가지 diff --git a/changelog/README.md b/changelog/README.md index 617144e..941ad7f 100644 --- a/changelog/README.md +++ b/changelog/README.md @@ -4,6 +4,7 @@ ## 버전 +- [0.7.8](0.7.8.md) — 화면을 만들고 재고 고친 뒤에만 보여주는 `screen` skill · `screen-designer` (외부 도구 없이도, 게이트 20개를 숫자로) - [0.7.7](0.7.7.md) — 병렬 작업이 서로 상태를 덮지 않아요 (`.ax/tasks/`) · 스킬 description 을 발동 조건만으로 · 훅 출력 10,000자 상한 · 에러가 다음 할 일을 알려줘요 - [0.7.6](0.7.6.md) — 무관한 커밋엔 spec 경고가 한 줄만 · TS 타입 선언 시크릿 오탐 · 위반마다 file:line · 스크래치패드 정리가 안 막혀요 · `contract:` 추적 · 레인 공용 자원 - [0.7.5](0.7.5.md) — 인계 노트에 spec ID 만 적어도 Stop 게이트가 알아봐요 diff --git a/commands/goax.md b/commands/goax.md index 2650d68..1e04596 100644 --- a/commands/goax.md +++ b/commands/goax.md @@ -23,6 +23,7 @@ goax 의 모든 기능은 **자연어 트리거** 로 호출돼요 (thin wrapper "병렬로 돌리자" / "레인 나눠줘" — lane (가를 수 있는 일인지부터 판정) "구현 시작" / "tasks 실행" — tasks.md 실행 (레인 배정 있으면 코디네이터 모드) + 완료 게이트·evaluator "spec 확인" / "합의 리뷰" — NEEDS CLARIFICATION 게이팅 + L/XL 은 architect → evaluator 합의 리뷰 + "화면 만들어줘" / "시안 봐줘" — screen (스펙 표 먼저 → screen-designer 가 구현·실측·수정 → verdict) ▸ 결정·회고 "ADR 작성" / "결정 기록" — ADR 새 파일 diff --git a/evals/README.md b/evals/README.md index 9ddd02e..01a13de 100644 --- a/evals/README.md +++ b/evals/README.md @@ -100,14 +100,14 @@ trace 를 보려면 `--keep-temp` 로 돌리고 `chmod 700 /sealed` |---|---| | `triage-first/` | `provision.sh` 로 실제 설치한 트리에서 "결제 모듈에 환불 기능 추가해줘" 한 문장이 들어왔을 때, `user-prompt/triage-nudge.sh` 가 구현 의도를 감지해 nudge 를 밀어 넣고(`nudge-marker`) 모델이 코드보다 triage 를 먼저 하는가 (META 1번 룰). `triage-scripts-used` 가 triage 가 자기 스크립트까지 내려갔는지를 봐요. 예전엔 프롬프트가 AGENTS.md 를 만들라고 시켜서 baseline 도 같은 룰을 봤어요 | | `critical-canary/` | 🔴 룰이 프롬프트가 아니라 `.ax/spirit/rules/security.md` 에만 있을 때, `pre-edit/spirit-rules-inject.sh` 가 그 룰을 모델에 닿게 해서 유혹 요청("급하니까 PII 로그 찍어줘")을 막거나 지적하게 만드는가. baseline arm 은 같은 파일 트리를 갖고도 hook 이 없어요 — Δ 가 곧 hook 의 기여 | -| `trigger/*` (16) | description 만으로 맞는 skill 을 고르고 이웃 skill 은 안 부르는가 — 아래 "트리거 스위트" | +| `trigger/*` (22) | description 만으로 맞는 skill 을 고르고 이웃 skill 은 안 부르는가 — 아래 "트리거 스위트" | | `doctor-i6/` | `provision.sh` 로 실제 설치한 트리(wrapper 포함)에 `external:vitest` 🔴 룰 하나만 있을 때, doctor 가 **자기 스크립트로** (`check-rule-enforcement.sh` I6 · `check-sensor-liveness.sh` C3) "라벨은 있는데 자동 트리거가 없다" 를 진단하는가. `scripts-used` 지표가 스크립트 경로를, `i6-reported` 가 결론을 봐요. 이 스캐폴드가 I6 의 출고 훅 제외 목록 누락(spec-completion-gate.sh)을 잡았어요 — smoke §47 | ## 트리거 스위트 — `evals/trigger/` skill 의 `description` 만 보고 모델이 **맞는 skill 을 고르는가** 를 재요. 케이스 하나가 프롬프트 하나라 (공식 형식에 한 파일 여러 프롬프트는 없어요) `case.yaml` 한 파일에 프롬프트·grader 를 다 담았어요. 겹치기 쉬운 이웃 -(triage · spec · spec-tasks · spec-implement · spec-validate · lane · audit · mistake · doctor · up · onboarding · zero) +(triage · spec · spec-tasks · spec-implement · spec-validate · lane · screen · audit · mistake · doctor · up · onboarding · zero) 사이의 근접 표현이 중심이에요. - 각 케이스는 `fires-` (`tool_used: Skill`, `min: 1`) 과 `not-<이웃>` (`min: 0` · `max: 0` · `arm: both`) 으로 채점해요. diff --git a/evals/trigger/screen-build/case.yaml b/evals/trigger/screen-build/case.yaml new file mode 100644 index 0000000..aab80e2 --- /dev/null +++ b/evals/trigger/screen-build/case.yaml @@ -0,0 +1,17 @@ +schema_version: "1.1" +name: trigger-screen-build +description: "새 화면 설계 — screen" +tags: [trigger, screen] +# skill 고르기만 재요 — scaffold·hook shim 없이 description 만으로 고르는지 봐요 (README '트리거 스위트') +plugins: ["../../.."] +execution: + prompt: "Expo 앱에 예약 확인 화면 새로 만들어줘. 토큰 파일은 아직 없어" + max_turns: 4 + timeout_seconds: 180 + allowed_tools: [Skill] +graders: + - name: fires-screen + type: tool_used + tool: Skill + input_match: '"skill"\s*:\s*"(?:[\w-]+:)?screen"' + min: 1 diff --git a/evals/trigger/screen-copy/case.yaml b/evals/trigger/screen-copy/case.yaml new file mode 100644 index 0000000..d8fe52f --- /dev/null +++ b/evals/trigger/screen-copy/case.yaml @@ -0,0 +1,17 @@ +schema_version: "1.1" +name: trigger-screen-copy +description: "UX 라이팅 — screen" +tags: [trigger, screen] +# skill 고르기만 재요 — scaffold·hook shim 없이 description 만으로 고르는지 봐요 (README '트리거 스위트') +plugins: ["../../.."] +execution: + prompt: "에러 토스트가 '결제에 실패했습니다' 로 나가는데 문구 다듬어줘" + max_turns: 4 + timeout_seconds: 180 + allowed_tools: [Skill] +graders: + - name: fires-screen + type: tool_used + tool: Skill + input_match: '"skill"\s*:\s*"(?:[\w-]+:)?screen"' + min: 1 diff --git a/evals/trigger/screen-near-miss-adr/case.yaml b/evals/trigger/screen-near-miss-adr/case.yaml new file mode 100644 index 0000000..bc12888 --- /dev/null +++ b/evals/trigger/screen-near-miss-adr/case.yaml @@ -0,0 +1,24 @@ +schema_version: "1.1" +name: trigger-screen-near-miss-adr +description: "스타일링 결정 기록 — adr, screen 아님" +tags: [trigger, screen, adr] +# skill 고르기만 재요 — scaffold·hook shim 없이 description 만으로 고르는지 봐요 (README '트리거 스위트') +plugins: ["../../.."] +execution: + prompt: "화면 스타일링을 Tailwind 대신 CSS Modules 로 가기로 했어, 그 결정 기록 남겨줘" + max_turns: 4 + timeout_seconds: 180 + allowed_tools: [Skill] +graders: + - name: fires-adr + type: tool_used + tool: Skill + input_match: '"skill"\s*:\s*"(?:[\w-]+:)?adr"' + min: 1 + - name: not-screen + type: tool_used + tool: Skill + input_match: '"skill"\s*:\s*"(?:[\w-]+:)?screen"' + min: 0 + max: 0 + arm: both diff --git a/evals/trigger/screen-near-miss-api/case.yaml b/evals/trigger/screen-near-miss-api/case.yaml new file mode 100644 index 0000000..73688cf --- /dev/null +++ b/evals/trigger/screen-near-miss-api/case.yaml @@ -0,0 +1,24 @@ +schema_version: "1.1" +name: trigger-screen-near-miss-api +description: "API 작업 — screen 아님" +tags: [trigger, screen, triage] +# skill 고르기만 재요 — scaffold·hook shim 없이 description 만으로 고르는지 봐요 (README '트리거 스위트') +plugins: ["../../.."] +execution: + prompt: "상품 목록 API 응답에 썸네일 URL 필드 추가해줘" + max_turns: 4 + timeout_seconds: 180 + allowed_tools: [Skill] +graders: + - name: fires-triage + type: tool_used + tool: Skill + input_match: '"skill"\s*:\s*"(?:[\w-]+:)?triage"' + min: 1 + - name: not-screen + type: tool_used + tool: Skill + input_match: '"skill"\s*:\s*"(?:[\w-]+:)?screen"' + min: 0 + max: 0 + arm: both diff --git a/evals/trigger/screen-near-miss-spec/case.yaml b/evals/trigger/screen-near-miss-spec/case.yaml new file mode 100644 index 0000000..b2112f3 --- /dev/null +++ b/evals/trigger/screen-near-miss-spec/case.yaml @@ -0,0 +1,24 @@ +schema_version: "1.1" +name: trigger-screen-near-miss-spec +description: "기능 spec — spec, screen 아님" +tags: [trigger, screen, spec] +# skill 고르기만 재요 — scaffold·hook shim 없이 description 만으로 고르는지 봐요 (README '트리거 스위트') +plugins: ["../../.."] +execution: + prompt: "결제 개편 기능 spec 새로 만들어줘 — 화면 3개, API 2개짜리야" + max_turns: 4 + timeout_seconds: 180 + allowed_tools: [Skill] +graders: + - name: fires-spec + type: tool_used + tool: Skill + input_match: '"skill"\s*:\s*"(?:[\w-]+:)?spec"' + min: 1 + - name: not-screen + type: tool_used + tool: Skill + input_match: '"skill"\s*:\s*"(?:[\w-]+:)?screen"' + min: 0 + max: 0 + arm: both diff --git a/evals/trigger/screen-review-vibe/case.yaml b/evals/trigger/screen-review-vibe/case.yaml new file mode 100644 index 0000000..492804c --- /dev/null +++ b/evals/trigger/screen-review-vibe/case.yaml @@ -0,0 +1,24 @@ +schema_version: "1.1" +name: trigger-screen-review-vibe +description: "있는 화면 리뷰 — screen, triage 아님" +tags: [trigger, screen, triage] +# skill 고르기만 재요 — scaffold·hook shim 없이 description 만으로 고르는지 봐요 (README '트리거 스위트') +plugins: ["../../.."] +execution: + prompt: "이 결제 화면 스크린샷인데 뭔가 촌스럽고 답답해. 어디가 문제야?" + max_turns: 4 + timeout_seconds: 180 + allowed_tools: [Skill] +graders: + - name: fires-screen + type: tool_used + tool: Skill + input_match: '"skill"\s*:\s*"(?:[\w-]+:)?screen"' + min: 1 + - name: not-triage + type: tool_used + tool: Skill + input_match: '"skill"\s*:\s*"(?:[\w-]+:)?triage"' + min: 0 + max: 0 + arm: both diff --git a/skills/screen/SKILL.md b/skills/screen/SKILL.md new file mode 100644 index 0000000..9eead43 --- /dev/null +++ b/skills/screen/SKILL.md @@ -0,0 +1,405 @@ +--- +name: screen +description: "화면을 새로 만들거나, 이미 있는 화면·시안·스크린샷을 검토하거나, 부품 규칙·UX 문구·간격 같은 화면 결정을 내릴 때 쓰는 skill 이에요. '디자인' 이라는 말이 없어도 화면이 생기는 작업(랜딩·대시보드·목록·상세·결제·요금제·온보딩·결과·예약 확인·설정)이면 이 skill 이에요. 트리거: '/screen', '화면 만들어줘', '화면 설계', '시안 봐줘', '이 화면 어때', '디자인 리뷰', 'UI 퀄리티 올려줘', '실무 디자이너처럼', '컴포넌트 규칙 잡아줘', '버튼 높이 재 봐줘', '시뮬레이터 켜서 봐줘', '촌스러워', '답답해', '어색해', '문구 고쳐줘', '에러 메시지 다듬어줘', '푸시 문구', 'LLM 이 쓰는 문장이 어색해'. 안 쓰는 경우: 백엔드·API·데이터 모델만 바꾸는 작업, 작업 크기·위험 분류(triage — triage 가 화면 작업으로 판정하면 이 skill 로 넘겨요), 결정을 기록할 때(adr), 기능 전체의 spec 을 쓸 때(spec) — 그 spec 안의 화면 하나를 그릴 때는 이 skill 이에요." +--- + +# screen — 화면 설계 · 리뷰 · 실측 + +한 줄 신조: **"라이트 테마면 흰 바탕, 다크면 기본 표면 위에서 글자 크기와 여백만으로 위계를 세우고, 강조는 딱 하나에만 준다."** +꾸미지 않고 레이아웃 구조부터 세워요. 말이 아니라 숫자로 증명하고, 재기 전엔 보여 주지 않아요. + +## 시작 전 필수 + +`.ax/spirit/values.md`, `tone.md` 따라요. 사용자에게 보이는 문구는 `~해요` 체, 표시 기호는 +`.ax/docs/reference/symbols.md` 어휘만 써요. + +## 흐름 — 이 순서로 체크해요 + +``` +- [ ] 1. design-caps.sh --json → 진입 한 줄 (§1) +- [ ] 2. 모드 두 줄 — 일(리뷰 / 설계) · 미학(절제 / 파격 허용) + 각 근거 (§2) +- [ ] 3. (설계) design-spec 표 0~7 — 표 7 은 G1~G20 (§9) +- [ ] 4. screen-designer 브리프 — 새 컨텍스트, REFS 절대경로 (§10) +- [ ] 5. screen-report-check.sh --report <경로> --json (§11) +- [ ] 6. 판정 — valid · verdict · 미해결 · 결정 필요를 보여 주고 update-state.sh --skill screen (§11) +``` + +정적 스크린샷 한 장만 리뷰하는 경우는 4~5 대신 §12 형식으로 직접 리포트해요. + +## 0. 이 skill 의 범위 — 외부 의존 0 + +이 skill 은 **판단**(모드 · 보는 눈 · 고치는 순서 · 게이트 · 판정)을 맡고, **실행**(만들기 → 렌더링 → +재기 → 고치기 → 리포트)은 `screen-designer` 에이전트에게 **새 컨텍스트로** 넘겨요 (§10). 그리고 돌아온 +리포트를 `screen-report-check.sh` 로 판정해요 (§11). 만든 쪽이 자기 결과를 평가하면 같은 맹점으로 통과시켜요. + +**이 skill 은 외부 도구 없이 끝까지 돌아요.** 보는 눈 · 실무 수치 · 부품 16종 · 화면 19종 · 문구 절차 · +디자인 시스템이 없을 때 쓰는 기본 토큰 세트가 전부 이 skill 의 `references/` 안에 있어요. Figma · +디자인 시스템 MCP · 화면 레퍼런스 MCP(Mobbin 류) · 브라우저 자동화 · 시뮬레이터 · 이미지 라이브러리는 +**있으면 빨라지는 가속기**예요. 무엇이 있는지는 기억으로 가정하지 않고 `design-caps.sh` 로 찾아요. +없으면 `references/capabilities.md` 의 대체 수단으로 진행하고, 렌더링·측정을 못 했으면 리포트에 +**"실측 아님"** 이라고 적어요. **스크린샷 경로와 px 숫자 없이 "측정했다" · "확인했다" 고 쓰지 않아요.** + +프로젝트에 디자인 시스템 · 토큰 · 가이드가 있으면 **수치는 그쪽이 이겨요.** 이 skill 의 진짜 값은 +보는 눈과 고치는 순서예요. 충돌하면 프로젝트 규칙을 따르고, 그 규칙이 이 화면에서 왜 문제를 못 +잡는지만 지적해요. + +## 1. 진입 — 무엇이 있는지부터 + +```bash +bash .ax/scripts/bash/design-caps.sh --json +``` + +| 읽는 값 | 쓰는 곳 | +|---|---| +| `result.stack` | web · react-native · expo · lynx · flutter · swiftui · android · unknown — 렌더링 경로(§10 브리프)와 줄바꿈·안전 영역 관용구 | +| `result.slots.tokens` | 표 0 의 진실 원천. 비면 `references/color-type.md` §6~§11 기본 세트로 신설 (§12 토큰 파일로 옮기는 법) | +| `result.slots.components` | 재사용할 부품. 비면 `references/components.md` 규격으로 공용 부품 신설 | +| `result.slots.reference` | 처음 나오는 화면 유형의 레퍼런스. 비면 `references/screens.md` 정보 순서로 | +| `result.slots.render` · `measure` | 실측 가능 여부. 둘 중 하나라도 비면 verdict 는 최대 `실측 아님` | +| `result.spec` | 지금 있는 design-spec 경로. 없으면 `docs/design/design-spec.md` (이미 `design-system/` 이 있으면 그 안) | + +MCP 는 설정에 등록된 이름만 보여요 — 자기 도구 목록에 `mcp__<서버>__` 로 시작하는 도구가 실제로 있는지 +한 번 더 봐요. 설정에만 있고 도구가 없으면 빈 슬롯으로 쳐요. exit 2(jq 없음)면 슬롯을 전부 빈 것으로 치고 진행해요. + +결과를 한 줄로 보여 줘요: + +``` +◆ screen — 진입 + + 📍 스택 expo · 토큰 src/theme/tokens.ts · 부품 없음 · 레퍼런스 없음 · 렌더 simctl · 측정 pillow + 📍 design-spec 없음 → docs/design/design-spec.md 신설 +``` + +## 2. 모드 결정 — 두 축, 각각 한 줄 근거 + +| 축 | 값 | 판정 기준 | +|---|---|---| +| 일 | **리뷰** | 이미 있는 화면(스크린샷 · 시안 · 코드 · 스토리북)을 검토 | +| | **설계** | 새 화면 · 기능을 만드는 중, 또는 리뷰 결과를 고쳐 반영 (브리프엔 `설계+리뷰` — 설계 모드로, 리뷰 지적을 반영해 고쳐요) | +| 미학 | **절제** (기본) | 커머스 · 업무 앱 · 대시보드 · 설정 · 폼 · 목록 · 상세 · 결과 — 예외 없이 | +| | **파격 허용** | 랜딩 · 포트폴리오 · 브랜딩 · 매거진으로 판정된 화면만 → `references/bold-mode.md` | + +사용자가 "멋지게" 라고 했다고 파격이 되지 않아요. 화면 종류가 정해요. 파격이어도 §6 게이트 G1~G20 은 +하나도 면제되지 않아요. + +**구조만 바꾸는 작업**(부품을 디자인 시스템으로 올리기 · 리팩터)은 설계 모드지만 목표가 "화면 그대로" 예요 — +전후 픽셀 비교로 증명해요 (`references/measure.md` §5). + +## 3. 보는 눈 — 열자마자 1~2초 + +1. **시각적 앵커가 있나** — 시선을 붙잡는 타이틀이나 메인 비주얼이 하나 있는지. 없으면 그레이를 + 아무리 깔아도 빈 공간 과다가 안 잡혀요. +2. **여백이 충분한가** — 글자 · 부품이 해상도에 비해 커서 화면을 꽉 막거나 아래로 잘리지 않는지. +3. **핵심이 가려지나** — 글래스 · 짙은 그림자 · 고채도 배경이 상품 · 이미지 · 숫자를 방해하지 않는지. + +**핵심 정보(primary content)는 화면마다, 시트 · 레이어 · 팝업마다 하나씩** 적어요. 홈에만 적고 +바텀시트엔 안 적으면 시트 안에서 다시 위계가 없어져요. + +그다음 스스로 묻는 여섯 질문: + +| 질문 | 잡아내는 것 | +|---|---| +| 시각적 앵커가 되는 타이틀이 있나, 대비 · 강조가 있나? | 위계 부재 | +| 이 화면에서 제일 먼저 보여야 할 것 하나는 뭔가? | 우선순위 미정 | +| 다 강조했으면 강조된 게 하나라도 있나? | 강약 실패 | +| 왜 카드를 두세 번 중첩하고 그레이를 깔았지? | 여백 · 글자로 못 나눔 | +| 상품이 수천 개일 때 이 비율 · 연출을 유지할 수 있나? | 운영 불가 | +| 실제로 누르고 스크롤할 때 진심으로 편한가? | UX 오작동 | + +"전반적으로 어색하다" 는 거의 늘 **같은 역할의 부품이 화면마다 다른 수치**(주 버튼 40 과 52, +카드 안쪽 16 · 20, 그룹 간격 16 · 24)예요. 시안 한 장이나 코드 한 파일로는 안 보이고, 재야 보여요. + +## 4. 고치는 순서 — 항상 이 순서로 + +장식을 제거하기 전에 색이나 이펙트를 손대면 문제가 어디서 오는지 안 보여요. + +0. **텍스트 먼저** (설계) — 박스 · 그래픽 전에 전달할 내용의 1~4순위와 글자 계층. 그래픽 없이 + 텍스트만으로 뜻이 통해야 해요. +1. **장식 제거** — 중복 카드 프레임 · 그레이 배경 · 글래스 · 짙은 그림자를 지우고 라이트 테마면 흰 바탕, + 다크면 기본 표면(`references/color-type.md` §8 default)으로. +2. **레이아웃 구조** — 지그재그 · 오버랩을 풀고 좌측 정렬 또는 풀배열. 정보가 복잡하면 1 : 0.5 : 0.5. + 반복되는 대표 모듈의 디폴트 규격을 먼저 확정하고, 좌측 시작선을 맞춰요 (`references/layout.md`). +3. **위계** — 화면(시트)마다 **가장 큰 글자 ÷ 본문 ≥ 1.75** (본문 16 이면 28 이상). 본문 15~16, + 캡션 · 단위 13~14, 핵심 숫자 32~40. "카드 헤드라인이 앵커라서 22 면 충분" 은 규칙 회피예요 — + 22 ÷ 15 = 1.47 은 위계가 없는 거예요. 큰 글자가 없으면 핵심 숫자나 상태 문장 하나를 28 이상으로. +4. **여백** — 간격 스케일 **4 · 8 · 12 · 16 · 20 · 24 · 32 · 40 · 48 · 64 · 80** 안에서만. 부품 안 4~16, + 화면 좌우 16~24, 섹션 사이 32~80. **안 여백 < 바깥 여백** 이라야 그룹핑이 생겨요. 처음엔 확 + 벌려 놓고 줄여요. +5. **색** — 배경은 무채색으로 톤 다운, **포인트 컬러는 화면에 하나** (주 CTA 또는 선택 탭). +6. **마감** — 버튼 44~56 · 라운드 12 또는 원형, 1px 선 10~20%, 그림자 Y 4~8 / 블러 12~24 / 3~12%, + 행간 130~150%, 좌표 · 크기 소수점 제거. 타이틀 · 한글 본문이면 자간 −2~−3% (`references/locale-ko.md` §6). +7. **예외 케이스** (설계) — 긴 문자열 · 큰 숫자 · 모듈 증가 · 320 · 상태(디폴트 · 눌림 · 선택 · 비활성 · + 로딩). 레이어는 **스크롤 소유**(레이어 전체 / 내부 독립 / 메인)와 **닫는 법 셋**(인디케이터 · 헤더 + 드래그 · 바깥 딤 탭 · X)을 문장으로. 라이브러리 이름만 적으면 닫는 법을 적은 게 아니에요. + +수치 상세는 `references/numbers.md`, 판단 근거는 `references/principles.md`. + +## 5. 반복 패턴 20 — 체크리스트 + +빈도 순이에요. 리뷰할 땐 위에서부터 훑고 걸리는 것만 올려요. 증상 · 원인 · 고침 전문은 `references/patterns.md`. + +| # | 패턴 | 걸리는 신호 | 고침 한 줄 | +|---|---|---|---| +| 1 | 글자 크기 과대 · 위계 없음 | 다 20 근처, 타이틀이 안 보임, 본문 11~12 | 타이틀 본문 1.75배↑, 본문 15~16, 캡션 13~14 (11 이하 금지) | +| 2 | 여백 부족 · 밀도 불균형 | 한쪽 빈 공간 과다, 한쪽 밀도 과다, 안 > 바깥 | 요소 16 / 섹션 40 · 64 · 80, 안 < 바깥 | +| 3 | 카드 중첩 · 그레이 박스 | 박스 안 박스, 회색 면 구획, 면 + 카드 2중 | 박스 제거, 1px 10~20% 선이나 여백. 컨테이너는 1개 | +| 4 | 이미지 · 썸네일 비율 · 크기 | 정사각 · 세로 혼용, 프로필 50+, 찌그러짐 | 커머스 1:1 통일, 프로필 32~40, 리터치 확장 | +| 5 | 드롭 섀도우 남용 | 부품마다 검은 그림자 | Y 4~8 / 블러 12~24 / 3~12%, 아니면 1px 선 | +| 6 | 강조 과다 · 브랜드색 난발 | 원색이 사방, CTA 를 모름 | 무채색으로 톤 다운, 포인트 하나 | +| 7 | CTA 강조 부족 / 과다 | 버튼이 텍스트처럼 묻힘, 또는 과대 | 높이 44~56, 좌우 패딩 24↑, 선명한 채움, 주/보조 분리 | +| 8 | 탭 · 필터 · 아코디언 피드백 | 선택 불명, +/− 자리 이동, 파편 탭 | 같은 자리 토글, 체크 아이콘, 띠 하나, 라인 2px | +| 9 | 글래스모피즘 도배 | GNB · 카드 · 필터 · 버튼이 다 뿌옇다 | 제거하고 단색. 독바 · 시트만 60~70% + 블러 20~30 | +| 10 | GNB · 헤더 무게 과다 | 띠배너 닫기 없음, 어두운 GNB, 백 버튼 50 | 닫기 44, GNB 투명, 헤더 56~64, 백 24 (프레임 36~44) | +| 11 | 오버랩 · 지그재그 | 동영상 위 이미지 위 카드, 좌우 번갈이 | 분리 · 풀배열, 좌 텍스트 우 이미지 일직선 | +| 12 | 바텀시트 · 레이어 스크롤 | 작은 박스 안 스크롤, 뒷화면이 같이 밀림 | 레이어 전체 스크롤 + 상단 요약 고정, 또는 독립 스크롤 + 적용 | +| 13 | 아이콘 굵기 · 스타일 불일치 | 1/1.5/2px 혼용, 면 · 선 혼용, 장식 아이콘 | 2px 통일, 정방형 가이드 박스, 장식 삭제 | +| 14 | 타이포 디테일 방치 | 행간 Auto, 한글 타이틀 자간 0%, 본문 중앙 정렬 | 행간 130~150%, 좌측 정렬, 타이틀 · 한글 본문 자간 −2~−3% (`references/locale-ko.md`) | +| 15 | 그리드 · 정렬 파괴 | 시작선 어긋남, 마진 제각각 | 12컬럼, 시작선 일치, 마진 16 · 20 · 24 중 하나 | +| 16 | 이미지 위 글자 · 상품 안착 | 밝은 이미지 위 흰 글자, 떠 보이는 상품 | 하단 0→30~50% 딤, 타원 + 블러 그림자 | +| 17 | 폼 상태 · 컨트롤 미설계 | 포커스 선 · X · 눈 아이콘 없음, 힌트와 입력 글자색 같음 | 포커스 1~2px, X 20~24, 힌트 보조색 → 입력 본문색 | +| 18 | 첫 화면 잘림 · 320 미대응 | 375 에서 CTA 가 스크롤 아래, 320 에서 겹침 | 상단 패딩 축소, 320 은 수직 1줄 · 스와이프 | +| 19 | 정보 순서 · 개념 오류 | 상품 카드 요소 뒤섞임, 영수증 그룹 파괴 | 카드: 이미지→브랜드→상품명→가격→할인→별점. 영수증: 총액→할인(−)→최종→적립(+) | +| 20 | 예외 케이스 · 확장성 | 긴 이름, 천만 단위 가격, 같은 모듈 무한 반복 | 최대 글자 수 · 말줄임, 분기 설계, 리듬 코너 1~2개 | + +## 6. 게이트 — 숫자를 적어야 통과 + +**이 목록이 게이트의 SSOT 예요.** design-spec 표 7(`references/spec-template.md`)과 screen-designer 리포트의 +게이트 표는 이 G1~G20 과 행 수 · 순서가 같아요. 설계 단계에선 표 7 에 "명세 값" 을, 실행 뒤엔 리포트에 +"화면에서 잰 값" 을 적어요. + +- **G1 화면 · 시트마다 핵심 정보 한 줄** +- **G2 가장 큰 글자 ÷ 본문 ≥ 1.75** (실제 나눗셈: 28 ÷ 16 = 1.75) +- **G3 간격 값이 전부 스케일 안** — 4 · 8 · 12 · 16 · 20 · 24 · 32 · 40 · 48 · 64 · 80, 값 나열 +- **G4 주 버튼 · 인풋 높이 ≥ 44, 화면을 넘어 같은 값** — 화면별 값 나열 +- **G5 화면 좌우 마진 · 카드 안쪽 여백이 화면을 넘어 같은 값** — 화면별 값 나열 +- **G6 포인트 컬러 1개, 상태색은 아이콘 · 기호와 함께** +- **G7 탭 가능한 부품마다 터치 영역 ≥ 44** — 부품 이름 나열 +- **G8 선택형 부품마다 선택 / 비선택 / 비활성 세 상태** +- **G9 레이어마다 스크롤 소유 + 닫는 법 셋** +- **G10 버튼 라벨 행동 동사형, 상태어 없음** — "완료" · "등록됨" 같은 상태어 +- **G11 금지어 0건** — 검색 건수 (`references/ux-writing.md` §12 명령) +- **G12 실패할 수 있는 행동마다 오류 문구** +- **G13 완료 문구가 대상 · 수량 · 시각 중 둘 이상** +- **G14 숫자 + 단위는 크기 또는 굵기를 분리** +- **G15 같은 라벨 · 같은 행동 버튼이 한 화면에 하나** +- **G16 하단 고정 CTA 가 있으면 뒤 배경 페이드 + 스크롤 하단 여백** +- **G17 다크 테마면 배경 · 카드 · 보조 버튼 · 비활성 면이 눈으로 갈림** +- **G18 사용자 상태를 말하는 문구는 보여 주는 순간 참** +- **G19 긴 문자열 · 큰 숫자 · 320 처리** +- **G20 글자 대비 — 본문 4.5:1, 큰 글자 · 아이콘 3:1** + +**칸을 채우는 규칙** — 숫자 없이 ✅ 를 치지 않아요. 측정값이 비면 그 행은 ❌ 예요. + +- **측정 출처를 칸에 적어요.** 실측으로 치는 출처는 넷이에요 — ① 스크린샷 px(`screen-measure.sh`) + ② 브라우저 eval 의 computed style · DOM rect · DOM 텍스트 ③ 시뮬레이터 접근성 트리 ④ 관찰(동작을 + 실행한 전후 스크린샷 경로). 문구 게이트(G10 · G11 · G13)는 검색 명령의 건수도 출처예요. 예: `52 (px·390)` · + `28÷16=1.75 (computed)` · `44×44 (rect)` · `0건 (검색)`. 상세는 `references/measure.md` §3.1. +- **코드 상수는 실측이 아니에요.** 토큰 파일 · 스타일 값만 보고 적은 칸은 `코드값 52 (실측 아님)` 이고, + 그런 칸이 하나라도 있으면 verdict 는 최대 `실측 아님` 이에요. +- **`해당 없음(이유)` 는 이유가 구체적일 때만 통과**예요 — "화면 하나 — 교차 비교 대상 없음" · + "다크 테마 없음" · "레이어 없음". "필요 없음" · "범위 밖" 은 이유가 아니에요. +- **동작 게이트는 구현하고 관찰해야 ✅** 예요 — 닫는 법(G9) · 오류 문구(G12) · 하단 여백(G16) · 상태 + 문구(G18) · 예외 처리 · 로딩(G19). 명세에 적었다는 것만으로는 ✅ 가 아니에요. 구현이 없으면 ❌, + 구현은 했는데 실행해 보지 못했으면 `실측 아님`. + +## 7. 절대 금지와 조건부 허용 + +**금지**: 커머스 목록에서 썸네일 비율을 화면마다 바꾸기 · 글래스 도배 · 브랜드 원색을 사방에 · +그레이 · 카드 컨테이너 2중 3중 · 모든 요소 강조 · 딤으로 막아 놓고 스크롤 구역을 안 나누기 · +지그재그 · 사용성 없는 컨셉보드를 실무 시안으로 내기. + +**조건이 맞으면 허용** (판단 근거를 리포트에): 글래스는 하단 독바 · 바텀시트에서만 60~70% + 블러 +20~30 · 대형 폰트 120~400 은 타이포 자체가 콘텐츠일 때 · 세로 썸네일은 매거진 · 미디어 · 카드 +컨테이너는 박스 전체가 클릭 타겟일 때 · 이미지 위 딤은 10~30% 가독성용만. 전체 표는 `references/dos-donts.md`. + +## 8. 문구 — 느낌이 아니라 논리로 + +화면에 글자가 생기면(버튼 · 에러 · 알림 · 제목 · 빈 상태) 이 순서예요. 10단계 상세와 전 · 후는 +`references/ux-writing.md` §13, 다른 언어로 옮기는 법은 §12. + +1. 말투가 아니라 정보 축부터 → 2. 상황 · 동기 · 기대 결과 → 3. 핵심 정보 하나 → 4. 정보 위계로 +먼저 살아남고 그다음 보이스 → 5. 소리 내어 읽기 → 6. 익숙한 단어 의심 (버튼은 행동 동사형, 상태어 +금지) → 7. 단위 먼저 확정 → 8. 같은 명사 반복 금지 → 9. 결론 줄 아래 겨루는 줄 금지 → 10. 약속은 +보여 주는 순간 참. + +한국어 화면이면 `references/locale-ko.md` (해요체 · 어절 줄바꿈 · 금지어 · 숫자 표기). +문구는 **한 파일에 모아요** (strings · copy · i18n) — 그래야 금지어를 전수 검색할 수 있어요. + +## 9. spec-first — design-spec 표 0~7 + +설계 모드에선 화면을 그리기 전에 `references/spec-template.md` 의 표를 **그 열 그대로** 채워요. +산문 명세는 빈 칸이 안 보여서 상태 · 단위 · 오류 문구가 새요. 표는 빈 칸이 보여요. + +| 표 | 무엇 | 빠지면 | +|---|---|---| +| 0 공통 토큰 | 글자 · 간격 · 포인트 · 무채색 · 상태색 · 라운드 · 선 · 그림자 · 아이콘 | 화면마다 다른 수치 | +| 1 화면 | 화면 · 시트마다 핵심 정보 + 가장 큰 글자 ÷ 본문 나눗셈 + 정보 순서 | 평평한 위계 | +| 2 부품 | 탭할 수 있는 모든 것 — 크기 · 터치 영역 · 상태 세 열 | 터치 미달 · 선택 상태 불명 | +| 3 숫자 표기 | 숫자와 단위의 크기 · 굵기 분리 | 숫자가 안 튐 | +| 4 문구 | 보이는 글자 전부 + 금지어 검색 건수 | 매번 문구를 다시 고침 | +| 5 레이어 | 스크롤 소유 + 닫는 법 | 갇힌 스크롤 | +| 6 예외 | 긴 문자열 · 큰 숫자 · 많은 항목 · 320 · 오프라인 · 0건 | 실제 데이터에서 터짐 | +| 7 자기 검수 | G1~G20 마다 명세 값 + ✅/❌ | — | + +**빈 칸 규칙**: 값 또는 `해당 없음(이유)`. `—` · `TBD` · 공란은 미완성이에요. 파일이 있으면 **바뀐 행만** +고쳐요. 부품 선택이 헷갈리면 목적으로 골라요 (`references/principles.md` E4), 시스템마다 값이 다른 +자리는 `references/component-systems.md`. + +**멈추지 않아요.** 사용자가 정해야 할 것(버튼 통일 높이 · 선 vs 여백 · 앱 결 · 헤드라인 아래 무엇)은 +후보 2~4개 + 권장 하나 + 이유를 적고 **권장값으로 진행**해요. 리포트 끝 "결정 필요" 에 모아요. +예외는 되돌리기 비싼 것 하나 — **기존 토큰 값을 바꿔 다른 화면이 깨질 때**는 바꾸기 전에 물어요. 브랜드색이 +글자 대비 4.5:1 에 못 미칠 때도 같아요 — 색상은 그대로 두고 진한 값을 새 토큰에만 담아 진행하고, 기존 +토큰을 바꾸는 건 ⛔ 로 물어요 (`references/color-type.md` §6.2). + +``` +⛔ 기존 토큰 button.height.md 를 40 → 52 로 바꾸면 호출부 14곳이 같이 바뀌어요 + 🎯 정할 것: 주 버튼 높이 통일값 + ● [a] 52 로 토큰 변경 + 14곳 스크린샷 재측정 ✅ 권장 — 화면 6개 중 4개가 이미 52 + ○ [b] 새 토큰 button.height.cta 52 를 추가, 기존 값 유지 + ○ [c] 40 유지 — 터치 44 미달은 hitSlop 으로 +``` + +## 10. 실행 — screen-designer 를 새 컨텍스트로 + +리뷰만 하고 끝나는 경우(정적 스크린샷 한 장 · 코드도 실행 환경도 없음)는 이 skill 이 §12 형식으로 +직접 리포트해요. **코드를 만들거나 고치거나, 돌아가는 화면을 재야 하면** `Agent` 도구로 +`goax:screen-designer` 를 띄워요 (vendor 설치면 `screen-designer`). **이 대화를 넘기지 않아요.** +브리프에 넣는 건 이것뿐이에요: + +- **요청 한 줄** — 사용자가 원한 것 그대로 +- **모드 두 줄** — 일(리뷰 / 설계 / 설계+리뷰) · 미학(절제 / 파격 허용) + 각 근거 한 줄 (§2) +- **design-caps 결과** — `design-caps.sh --json` 의 `result` 를 그대로 +- **references 경로** — `REFS="${CLAUDE_SKILL_DIR}/references"` 를 절대경로로 풀어서. 에이전트는 이 디렉터리의 + 파일을 매 호출 직접 읽어요 +- **design-spec 경로** — `result.spec` 또는 신설할 경로 +- **대상** — 화면 · 파일 · 라우트 · 딥링크 (아는 만큼) +- **리포트 경로** — `docs/design/reports/-.md` (사용자가 다른 곳을 원하면 그곳) +- **첫 줄 계약** — `verdict: 통과 | 보강 필요 | 실측 아님`, 게이트 표는 G1~G20 스무 행 +- **다시 맡길 때만** — 지난 리포트 경로 + `screen-report-check` 의 `problems` 또는 미해결 목록 + +```bash +REFS="$(cd "${CLAUDE_SKILL_DIR}/references" && pwd)" # 플러그인 references 의 절대경로 — 브리프에 그대로 +REPORT="docs/design/reports/$(date +%F)-.md" +``` + +여러 화면을 한 번에 맡길 땐 화면 하나(또는 한 흐름)에 에이전트 하나예요. 같은 토큰 파일 · 공용 +부품을 여러 에이전트가 동시에 고치면 서로 덮어요 — 공용 파일을 건드리는 화면은 순서대로. + +## 11. 판정 — screen-report-check + +에이전트가 리포트 파일을 직접 써요. 이 skill 은 받아 적지도, 본문을 다시 읽지도 않고 스크립트의 JSON 만 읽어요: + +```bash +bash .ax/scripts/bash/screen-report-check.sh --report "$REPORT" --json +``` + +읽는 값: `result.valid` · `result.verdict` · `result.problems[]` · `result.gates.{rows,empty,failed,code}` · +`result.screenshots.{cited,existing}`. + +1. **`valid` 가 false 이거나 `gates.rows` 가 20 이 아니면** — 판정 불가예요. 브리프에 `problems`(행 수가 + 다르면 "게이트 표가 G1~G20 스무 행이 아니에요 — n행")를 붙여 **한 번만** 다시 맡겨요. 두 번째도 + 판정 불가면 더 띄우지 않고 problems 와 리포트 경로를 사용자에게 보여 주고 고르게 해요. 직접 고쳐 쓰지 않아요. +2. **exit 1**(리포트 없음)도 판정 불가로 쳐서 1 과 같이 한 번만 다시 맡겨요. **exit 2**(jq 없음)면 `head -1` + 로 첫 줄만 보고, 검사를 못 했다고 함께 말해요. +3. **`valid` 가 true 면** verdict 대로: + +| verdict | 뜻 | 다음 | +|---|---|---| +| `통과` | G1~G20 전부 실측 출처나 구체적 `해당 없음(이유)` 가 있고 전부 ✅, 스크린샷 파일이 실제로 있음 | 전후 스크린샷 · 게이트 수 · 결정 필요를 보여 줘요 | +| `보강 필요` | 고친 뒤에도 ❌ 가 남음 (`gates.failed` > 0) | 리포트 "미해결" 과 "결정 필요" 를 그대로 보여 줘요. 다시 맡기는 건 **한 번만**, 미해결만 브리프에 — 그래도 남으면 사용자가 정해요 | +| `실측 아님` | 렌더 · 측정을 못 했거나 코드값으로 적은 칸이 있음 | **통과로 바꿔 말하지 않아요.** 무엇이 없어서 못 쟀는지, 사용자가 직접 캡처할 방법, 그리고 `gates.failed` > 0 이면 "미해결" 을 출처(코드값)와 함께 보여 줘요 | + +``` +◆ screen — 판정 + + ✅ verdict: 통과 — 게이트 20/20, 수정 2회차, 스크린샷 4/4 + 📂 docs/design/reports/2026-10-04-booking-confirm.md · docs/design/design-spec.md (표 1·2·4 행 추가) + 👉 결정 필요 1건 — 버튼 라운드 12 vs 원형 (진행은 12) +``` + +마무리에 HUD 를 갱신해요 (실패해도 본 작업엔 영향 없어요): + +```bash +bash .ax/scripts/bash/update-state.sh --skill screen || true +``` + +## 12. 리뷰 리포트 형식 (정적 리뷰 · §10 에서 skill 이 직접 할 때) + +지적은 **증상 → 원인 → 고침(수치)** 셋이 다 있어야 하나로 쳐요. 원인 없는 지적은 취향이고, 수치 없는 +고침은 실행이 안 돼요. 첫 줄은 이 경우에도 verdict 예요 — 정적 이미지만 봤으면 `verdict: 실측 아님`. + +``` +verdict: 실측 아님 +## 첫인상 (1~2초) +시각적 앵커: 있음/없음 — 무엇이 · 여백: … · 가려짐: … +핵심 정보: <무엇이 먼저 보여야 하나> +## 지적 (심각한 순, 패턴 번호) +1. [P1 위계] 증상: … / 원인: … / 고침: 타이틀 18→28, 본문 20→16 +2. [W 관성어] 증상: "완료" 단독 / 원인: … / 고침: "9월 21일 04:30 2명 예약됐어요" +## 고치는 순서 — 이 화면에서 실제로 손댈 단계 +## 잘한 것 (있으면) +## 프로젝트 규칙과의 충돌 (있으면) +## 구현 메모 (코드로 받았을 때만) +``` + +## 13. 낯선 화면 — 기능으로 붙여요 + +도메인 이름(농장 · 정비소 · 학원)으로 찾지 말고, 화면이 하는 일로 가장 가까운 유형을 골라 그 규격을 +가져와요 (`references/screens.md`). + +| 화면이 하는 일 | 가져올 유형 | +|---|---| +| 여러 항목을 훑는다 | 상품 목록 / 피드 · 타임라인 / 리스트 행 | +| 하나를 자세히 본다 · 결과를 확인한다 | 상품 상세 / 예약 · 주문 확인 (핵심 → 부가 → 하단 고정 CTA) | +| 값을 넣는다 | 로그인 폼 / 글쓰기 · 에디터 / 금액 입력 (인풋 상태 · 키보드 위 액션) | +| 하나를 고른다 | 옵션 선택 바텀시트 / 캘린더 / 칩 · 세그먼트 | +| 잠깐 떠서 묻는다 | 바텀시트 · 모달 · 확인 대화상자 (스크롤 소유 + 닫는 법) | +| 숫자로 요약한다 | 대시보드 / 개인 통계 (숫자가 앵커, 차트는 보조) | +| 처음이거나 비었거나 막혔다 | 온보딩 / 빈 상태 / 에러 문구 절차 | +| 관리자가 표로 다룬다 | 대시보드 · 관리자 + 표 규칙 | + +어느 줄에도 안 붙는 화면(지도 · 미디어 재생 · 채팅 · 게임)은 §4 순서 · §6 게이트 · 부품 규격만 +적용하고, 그 화면 고유 규칙은 이 skill 밖이라고 리포트에 적어요. + +## 14. 용어 + +리포트와 명세에 쓰는 말(시각적 앵커 · 그룹핑 · 포인트 컬러 …)의 뜻은 `references/process.md` §5. + +## 15. 참고 파일 — 필요할 때만 읽어요 + +| 파일 | 언제 | +|---|---| +| `references/spec-template.md` | 설계 모드 산출물 — 표 0~7 과 빈 칸 규칙 | +| `references/components.md` | 부품을 그리거나 검토할 때 — 16종, 실수 → 수치 → 전/후 → 예외 | +| `references/component-systems.md` | 공개 디자인 시스템들의 부품 값 비교 · 상태 · 접근성 · 키보드 · 토큰 역할 대응 · 시스템 없을 때 기본값 | +| `references/principles.md` | 판단 근거 40 — 시스템 철학 · 인터랙션 · 포용(대비 · 터치 · 안전 영역) · 문구 · 로딩 패턴 | +| `references/screens.md` | 화면을 잡을 때 — 19종, 핵심 정보 · 정보 순서 · 레이아웃 · 실수 · 전/후 | +| `references/patterns.md` | 20개 패턴 전문 | +| `references/numbers.md` | 폰트 · 여백 · 버튼 · 선 · 그림자 · 비율 · 아이콘 · 그리드 수치 | +| `references/layout.md` | 레이아웃 잡는 순서 · 그리드 · 정렬 · 그룹핑 · 연차별 차이 · 플랫폼별 메모 | +| `references/color-type.md` | 굵기 · 팔레트 원칙 + **시스템이 없을 때 기본 세트**(팔레트 · 역할 · 레이어 · 포커스 · 타입 램프 · 간격) | +| `references/dos-donts.md` | 잘한 예 · 금지 · 조건부 허용 | +| `references/ux-writing.md` | 문구 판단 절차 · 전/후 · 금지어 검색 · LLM 이 만드는 문구 | +| `references/locale-ko.md` | 한국어 화면 — 국내 앱 관례 · 해요체 · 어절 줄바꿈 · 표기 | +| `references/measure.md` | 돌아가는 화면을 잴 때 — 캡처 · 배율 · `screen-measure.sh` · 체크리스트 · 전후 diff · 실측 아님 | +| `references/process.md` | 리뷰 절차 · 판별 기준 · 용어 · 벤치마킹 · 사용성과 운영 지적 | +| `references/bold-mode.md` | 파격 허용 화면 — 방향 하나 · 타이포 주도 · 비대칭 · 모션 절제 | +| `references/capabilities.md` | 도구 슬롯과 빈 슬롯의 대체 수단 | + +## 16. 적용 범위 + +- **잘 맞는 화면**: 웹 · 커머스 · 업무 앱 · 대시보드 · 포트폴리오 · 랜딩, 모바일 앱 기본 화면. + 스택은 web · React Native · Expo · Lynx · Flutter · SwiftUI · Android 어디든 — 단위는 1배수 논리 단위. +- **원칙만 빌리는 화면**: 게임 · 미디어 플레이어 · 지도 · 채팅. +- **수치의 성격**: 실무 표준 범위이지 보편 법칙이 아니에요. 프로젝트 토큰이 이겨요. +- **범위 밖**: 사람 얼굴 · 외모를 평가하는 문구, 특정 사용자군을 낙인찍는 표현은 쓰지 않아요. + +## 안티패턴 + +- **실행 에이전트의 리포트를 받아 적거나 고쳐 쓰기** — 검사받는 쪽이 검사 기록을 쓰게 돼요 +- **`실측 아님` 을 통과처럼 말하기** — "대체로 괜찮아 보여요" 로 바꿔 전하지 않아요 +- **스크린샷 없이 "확인했어요"** — 경로와 px 숫자가 증거예요 +- **화면부터 그리고 표를 나중에 맞추기** — 표가 화면의 결함을 그대로 베껴요 +- **매 단계 사용자에게 묻기** — 권장값으로 진행하고 끝에 모아요 (토큰 값 변경만 예외) +- **도구가 없다고 멈추기** — 대체 수단으로 끝까지 가고, 못 한 것만 정직하게 적어요 +- **레퍼런스의 겉모습을 옮기기** — 옮기는 건 정보 순서와 이유뿐이에요 diff --git a/skills/screen/references/bold-mode.md b/skills/screen/references/bold-mode.md new file mode 100644 index 0000000..5417c05 --- /dev/null +++ b/skills/screen/references/bold-mode.md @@ -0,0 +1,103 @@ +# 파격 허용 모드 — 랜딩 · 포트폴리오 · 브랜딩 · 매거진 + +이 skill 의 기본은 **절제**예요. 파격은 화면 종류가 허용할 때만 켜요 — 사용자가 "멋지게" 라고 했다고 켜지 않아요. 켰더라도 게이트(글자 비율 · 간격 스케일 · 터치 44 · 문구 · 대비)는 하나도 면제되지 않아요. 파격은 **위계를 더 세게 세우는 방법**이지 위계를 버리는 허가가 아니에요. + +## 목차 + +1. 켜는 조건과 끄는 조건 · 2. 먼저 방향 하나를 정해요 · 3. 타이포그래피가 이끄는 레이아웃 · 4. 비대칭 그리드 · 5. 이미지 · 6. 색 · 7. 모션 — 절제 · 8. 그래도 지키는 게이트 · 9. 흔한 실패 · 10. 리포트에 적을 것 + +--- + +## 1. 켜는 조건과 끄는 조건 + +| 켜요 (파격 허용) | 끄요 (절제) | +|---|---| +| 랜딩 · 브랜드 소개 · 캠페인 페이지 | 커머스 목록·상세·결제 | +| 포트폴리오 · 케이스 스터디 | 업무 앱 · 대시보드 · 관리자 | +| 매거진 · 에디토리얼 · 아카이브 | 설정 · 폼 · 마이페이지 | +| 행사·전시·제품 출시 한 페이지 | 온보딩 · 로그인 · 결과 화면 | + +한 서비스 안에서도 갈려요 — 브랜드 랜딩은 파격, 그 랜딩의 "구매하기" 뒤 결제 흐름은 절제. 모드를 리포트 첫머리에 근거와 함께 한 줄로 적어요. + +## 2. 먼저 방향 하나를 정해요 + +파격 화면이 실패하는 가장 흔한 길은 기법을 여러 개 섞는 거예요 (큰 글자 + 그라디언트 + 3D + 지그재그). **방향은 하나**만 골라 끝까지 밀어요. + +| 방향 | 무엇이 주인공인가 | 잘 맞는 곳 | 핵심 장치 | +|---|---|---|---| +| 타이포 주도 (typographic) | 글자 자체 | 슬로건이 강한 브랜드, 에이전시, 행사 | 120~300 표제, 극단적 크기 대비, 넓은 여백 | +| 에디토리얼 (editorial) | 글과 이미지의 리듬 | 매거진, 케이스 스터디, 블로그 | 다단 그리드, 큰 인용구, 캡션 체계 | +| 이미지 주도 (image-led) | 사진·영상 한 장 | 패션, 공간, 제품 출시 | 풀블리드 이미지, 최소 텍스트, 하단 딤 | +| 구조 노출 (structural) | 그리드·선·숫자 | 포트폴리오, 아카이브, 기술 브랜드 | 보이는 그리드 선, 번호 매김, 고정폭 숫자 | + +색다르게 보이려고 쉬운 구도를 꼬지 않아요. 고른 방향이 이 브랜드의 무엇을 말하는지 한 줄로 적을 수 있어야 해요. + +## 3. 타이포그래피가 이끄는 레이아웃 + +- **크기 대비를 극단으로**: 표제 120~300 (모바일 56~96) 대 본문 16~18. 비율은 1.75 가 아니라 5~15배까지 가요. 중간 크기를 없애야 대비가 살아요 — 표제·본문·캡션 세 단만. +- **대형 표제는 가는 굵기**: 120 이상은 Regular / Light. Bold 대형은 투박하고 무거워요. 영문이면 대문자 + 자간 −2~−4%. +- **행간을 좁게**: 대형 표제는 행간 90~105%. 본문 행간(150%)을 그대로 쓰면 줄이 흩어져요. +- **한 줄에 한 생각**: 표제를 의미 단위로 직접 줄바꿈해요 (자동 줄바꿈에 맡기지 않아요). 한국어는 어절 단위 (`locale-ko.md`). +- **글꼴은 둘까지**: 표제용 하나(개성 있는 세리프나 디스플레이 산세리프) + 본문용 하나(가독성 좋은 산세리프). 표제 글꼴로 본문을 쓰지 않아요. +- **반응형 크기**: 웹은 `clamp(56px, 12vw, 240px)` 처럼 폭에 따라 줄이고, 320 폭에서 한 단어가 화면을 넘지 않는지 확인해요. + +## 4. 비대칭 그리드 + +- 12컬럼은 그대로 두고 **배치만 비대칭**으로: 7 : 5, 8 : 4, 또는 1 : 0.5 : 0.5. 무작위 배치가 아니라 같은 컬럼 선에 시작점을 맞춘 비대칭이에요. +- **여백이 요소**예요: 섹션 사이 120~200 (모바일 80~120). 화면의 40% 이상을 비워 두는 섹션이 있어도 돼요. +- **오버랩은 한 군데만**: 표제가 이미지 위로 걸치는 연출은 페이지에 하나. 둘 이상이면 지그재그와 같은 실패예요. +- **스크롤 리듬**: 큰 섹션 · 작은 섹션 · 풀블리드 이미지를 번갈아 두고, 같은 레이아웃 섹션을 세 번 연달아 두지 않아요. +- 모바일에서 비대칭은 대개 1컬럼으로 접혀요. 접힌 뒤에도 정보 순서(표제 → 핵심 가치 → 증거 → CTA)가 유지되는지 확인해요. + +## 5. 이미지 + +- **한 장을 크게**: 작은 이미지 여러 장보다 풀블리드 한 장. 16:9 · 3:2 · 세로 4:5 혼용은 매거진에선 허용 — 커머스 목록에선 여전히 금지. +- **톤을 맞춰요**: 색온도·채도·대비를 한 결로. 결이 다른 사진이 섞이면 브랜드가 흩어져요. +- **글자 위 가독성**: 이미지 위 표제는 하단 0 → 30~50% 딤, 또는 글자를 이미지의 빈 영역에 배치. 대비는 큰 글자 3:1 이상. +- **누끼 깊이**: 배경 – 표제 – 인물 3층 연출은 페이지에 한 번. +- 비율이 안 맞으면 늘이지 말고 리터치로 확장하거나 다시 크롭해요. +- 무거운 이미지는 `loading="lazy"` · 적절한 해상도 · 최신 포맷으로. 첫 화면 히어로만 즉시 로드. + +## 6. 색 + +- 파격 모드여도 **강조는 하나**. 강한 색을 넓은 면(배경 전체)에 쓰는 대신 다른 곳은 무채색으로 비워요. +- 배경을 흰색이 아닌 색(크림 · 먹색 · 강한 단색)으로 바꾸는 건 허용 — 그때 그 배경 위 글자 대비를 다시 재요. +- 그라디언트는 하나의 방향, 두 색까지. 무지개·메시 그라디언트를 섹션마다 바꾸지 않아요. + +## 7. 모션 — 절제 + +- **한 페이지에 대표 모션 하나**: 히어로 등장, 스크롤에 따른 표제 이동, 이미지 마스크 열림 중 하나. 섹션마다 다른 등장 효과를 주지 않아요. +- 길이: 부품 200ms 이하, 섹션 등장 300~600ms, 그 이상은 건너뛸 수 있게 (`principles.md` B9 · C7). +- **reduced-motion 에선 끄고 최종 상태로** 보여 줘요. 모션이 있어야만 읽히는 정보는 없어야 해요. +- 스크롤 하이재킹(스크롤 속도·방향을 빼앗는 것) 금지. 패럴랙스는 이동량 작게 (20~40px). +- 자동재생 영상은 음소거 + 정지 버튼. + +## 8. 그래도 지키는 게이트 + +SKILL.md "## 6. 게이트" 의 G1~G20 은 하나도 면제되지 않아요. 파격 모드에서 모양이 달라지는 것만 적어요. + +| 게이트 | 파격 모드에서의 모양 | +|---|---| +| G2 가장 큰 글자 ÷ 본문 ≥ 1.75 | 오히려 훨씬 넘어요. 넘지 못하면 파격이 아니라 평평한 화면이에요 | +| G3 간격 스케일 안 | 스케일을 위로 늘려요 (96 · 120 · 160 · 200). 스케일 밖 임의값은 여전히 금지 | +| G7 터치 44 | 큰 화살표 칩·텍스트 링크 CTA 도 터치 영역 44 | +| G6 포인트 하나 | 넓게 쓰더라도 하나 | +| G20 대비 | 이미지 위 글자 · 컬러 배경 위 글자 모두 재요 | +| G10 · G11 문구 · 금지어 0건 | 슬로건은 카피라이팅이어도 되지만, 버튼·오류·폼 문구는 UX 라이팅 규칙 그대로 | +| G19 320 · 긴 문자열 | 대형 표제가 320 에서 넘치지 않게 | +| 첫 화면 (파격 모드에만 더하는 행) | 1080 세로(모바일 첫 화면) 안에 표제 + 키비주얼 + CTA 가 한눈에 | + +## 9. 흔한 실패 + +- 기법을 다 쓰기 — 대형 글자 + 3D + 글래스 + 지그재그 + 그라디언트. +- 컨셉보드 한 장을 실무 시안으로 내기 — 스크롤·상태·320·반응형이 없음. +- 대형 Bold 표제로 투박하게. +- 모든 섹션에 다른 등장 모션. +- 표제가 멋있어서 CTA 를 잊기 — 랜딩도 다음 행동이 하나 있어야 해요. +- 이미지를 늘려 찌그러뜨리기. + +## 10. 리포트에 적을 것 + +- 모드 판정 근거 한 줄 ("브랜드 랜딩 — 파격 허용"). +- 고른 방향 하나와 이유 ("타이포 주도 — 슬로건이 제품 정체성"). +- 게이트 표는 절제 모드와 같은 행 그대로. 파격이라 면제한 행은 없어요. diff --git a/skills/screen/references/capabilities.md b/skills/screen/references/capabilities.md new file mode 100644 index 0000000..7cd2f8c --- /dev/null +++ b/skills/screen/references/capabilities.md @@ -0,0 +1,76 @@ +# 도구 슬롯 — 있으면 빨라지고, 없어도 끝까지 가요 + +screen skill 은 **외부 의존 0** 으로 돌아요. 보는 눈 · 수치 · 부품 규격 · 문구 절차 · 기본 토큰 세트가 전부 이 skill 의 references 안에 있어요. MCP · 브라우저 자동화 · 시뮬레이터 · 이미지 라이브러리는 **있으면 빨라지는 가속기**예요. 무엇이 있는지는 기억으로 가정하지 않고 스크립트로 찾아요: + +```bash +bash .ax/scripts/bash/design-caps.sh --json +``` + +`result.stack` (web · react-native · expo · lynx · flutter · swiftui · android · unknown), `result.slots.<슬롯>[]` (`provider` = `file` · `mcp:<서버>` · `cli:<명령>` · `python:pillow`), `result.spec` (지금 있는 design-spec 경로), `result.empty` (비어 있는 슬롯)를 읽어요. + +MCP 는 설정 파일에 **등록된 이름**만 보여요. 이 세션에 실제로 떠 있는지는 자기 도구 목록에 `mcp__<서버>__` 로 시작하는 도구가 있는지로 한 번 더 확인해요 — 설정에만 있고 도구가 없으면 그 provider 는 빈 슬롯으로 쳐요. + +## 목차 + +1. 슬롯 표 — 찾으면 무엇에 쓰나 · 없으면 무엇으로 대신하나 · 2. 슬롯별 상세 (tokens · components · reference · render · measure) · 3. 우선순위 규칙 · 4. 리포트에 적는 법 + +--- + +## 1. 슬롯 표 + +| 슬롯 | 찾으면 (provider 예) | 무엇에 쓰나 | 없으면 (대체 수단) | 리포트 표기 | +|---|---|---|---|---| +| **tokens** | `file`: tokens.* · theme.* · tailwind.config.* · colors.xml · design-system 문서 / `mcp:`: 디자인 시스템 MCP · Figma MCP (변수 정의) | 표 0 의 진실 원천. 이름·값을 그대로 가져와요 | `color-type.md` §6~§11 기본 세트를 토큰 파일로 새로 만들어요 (§12 토큰 파일로 옮기는 법) | "tokens: src/theme/tokens.ts" / "tokens: 없음 → 기본 세트로 신설" | +| **components** | `file`: components.json(레지스트리) · .storybook · components/ui · design-system 디렉터리 / `mcp:`: 디자인 시스템 · 레지스트리 MCP | 이미 있는 부품을 재사용 — 새로 만들기 전에 찾아요 | `components.md` 16종 규격 + `component-systems.md` §12 기본값으로 공용 부품을 만들어요 (Button · Row · Chip … 하나씩, size prop) | "components: Storybook 18종 재사용" / "components: 없음 → Button·Row 신설" | +| **reference** | `mcp:`: 화면 레퍼런스 갤러리 MCP · Figma MCP (스크린샷) | 처음 나오는 화면 유형에서 같은 업종 화면 3~5개의 "왜" 를 봐요 | `screens.md` 의 정보 순서·핵심 정보 + 사용자가 준 스크린샷·URL + (있으면) 웹 검색. 레퍼런스 단계를 건너뛰어도 게이트는 그대로예요 | "reference: N건 — 가져온 이유 2~3줄" / "reference: 건너뜀 (슬롯 비어 있음, screens.md §N 기준)" | +| **render** | `cli:` 브라우저 자동화 CLI · playwright · simctl · adb / `mcp:` 브라우저 자동화 · 시뮬레이터 MCP | 스크린샷을 찍어요 (`measure.md` §1) | 사용자가 직접 띄워 캡처를 주도록 다음 행동을 적고, 코드 정적 검사로 게이트를 대신 돌려요 | 리포트 첫 줄 `verdict: 실측 아님` | +| **measure** | `python:pillow` (`screen-measure.sh`) · 정적 UI 검사 CLI | 스캔라인으로 pt 를 재고, 전후 diff | 스크린샷을 눈으로 보고 코드값과 대조 — 숫자는 "코드값 (실측 아님)" | 리포트 첫 줄 `verdict: 실측 아님` | + +## 2. 슬롯별 상세 + +### tokens + +- 프로젝트 파일이 늘 MCP 보다 먼저예요. 코드가 실제로 쓰는 값이 진실이고, 디자인 도구 변수는 어긋나 있을 수 있어요. +- 디자인 시스템 MCP 가 있으면 **토큰 이름을 찾는 데** 써요. 이름을 지어내지 않아요. 값은 프로젝트 토큰 파일에서 확인해요. +- Figma MCP 의 변수 정의는 파일 키(URL)가 있어야 해요. 사용자가 링크를 주지 않았으면 쓰지 않아요. +- 토큰 파일이 둘 이상이면 (예: theme.ts 와 tailwind.config) 어느 쪽이 런타임에서 이기는지 import 를 따라가 확인하고 리포트에 적어요. + +### components + +- 레지스트리(components.json)가 있으면 새 부품을 만들기 전에 레지스트리에서 찾고, 있는 부품은 그대로 설치·재사용해요. +- Storybook 이 있으면 부품의 size · variant 목록을 거기서 읽어요. 스토리의 기본 size 를 그대로 쓴 호출부가 "화면마다 다른 높이" 의 1순위 원인이에요. +- 공용 부품이 없으면 **같은 역할은 같은 부품 하나**로 만들어요 — 화면 파일 안에 버튼 스타일을 복사하지 않아요. + +### reference + +- 트리거: design-spec 에 아직 없는 화면 유형이 처음 나올 때만. 규칙이 이미 있는 화면 유형이면 건너뛰어요. +- 한 번의 호출에 조건(업종 · 화면 유형 · 지역)을 모두 걸어요. 3~5개면 충분해요. +- 뽑는 건 **정보 순서 · 핵심 정보 위치 · CTA 위치 · 상태 처리** 뿐이에요. 색 · 라운드 · 그림자는 옮기지 않아요 (`process.md` §6). +- 레퍼런스 MCP 가 유료·로그인 제한으로 일부만 보여 줘도, 보인 것만 적고 진행해요. + +### render + +- 웹: 브라우저 자동화로 375 · 390 · 430 (+ 데스크톱 1280) 뷰포트. +- RN · Expo · Lynx · Flutter · SwiftUI: iOS 시뮬레이터 (`xcrun simctl`), 없으면 Android 에뮬레이터 (`adb`). +- 시뮬레이터 캡처 MCP 가 있으면 그걸 써도 되지만, 재는 건 **원본 PNG 파일**이에요 — 도구가 줄여 보여 준 이미지로 재지 않아요. +- 빌드가 실패하면 고치는 건 이 skill 의 일이 아니에요. 실패 로그 첫 줄을 적고 §1 의 대체 수단으로. + +### measure + +- `screen-measure.sh` 는 python3 + Pillow 만 있으면 돌아요. exit 2 = 없음. +- 정적 UI 검사 CLI 가 있으면 보조로 돌려 결과를 리포트 "파일" 절에 붙여요. 그 결과가 스크린샷 측정을 대신하지는 않아요. + +## 3. 우선순위 규칙 + +1. **프로젝트 자체 > 외부.** 프로젝트 토큰·부품·design-spec 이 있으면 MCP 보다 먼저예요. +2. **있는 걸 쓰고, 없는 걸 기다리지 않아요.** 빈 슬롯 때문에 멈추거나 사용자에게 설치를 요구하지 않아요 — 대체 수단으로 진행하고 리포트에 적어요. +3. **이름을 기억으로 가정하지 않아요.** `design-caps.sh` 가 보고하지 않은 도구는 없는 거예요. +4. **실측 표기는 정직하게.** render 나 measure 가 빠지면 리포트 첫 줄은 `verdict: 실측 아님` 이에요. 스크린샷 경로와 px 숫자 없이 "측정했다" 고 쓰지 않아요. + +## 4. 리포트에 적는 법 + +리포트 머리의 **도구** 줄에 슬롯마다 한 칸: + +``` +도구: tokens=src/theme/tokens.ts · components=없음→신설 · reference=건너뜀(빈 슬롯) · render=cli:simctl · measure=python:pillow +``` diff --git a/skills/screen/references/color-type.md b/skills/screen/references/color-type.md new file mode 100644 index 0000000..7f5b336 --- /dev/null +++ b/skills/screen/references/color-type.md @@ -0,0 +1,199 @@ +# 색 · 글자 · 간격 — 규칙과 디자인 시스템이 없을 때 쓰는 기본 세트 + +프로젝트에 디자인 시스템(토큰 파일·테마·Figma 변수)이 있으면 **그 값이 이겨요.** 이 파일의 앞쪽(§1~§5)은 어느 시스템 위에서도 통하는 판단 규칙이고, 뒤쪽(§6~§11)은 시스템이 **없을 때 그대로 토큰 파일로 옮겨 쓰는 기본 세트**예요. 이 세트만으로 외부 도구 없이 화면 하나를 끝까지 만들 수 있어야 해요. + +## 목차 + +1. 글꼴 굵기 규칙 · 2. 팔레트 구성 원칙 · 3. 그라디언트 · 4. 어두운 배경·다크 규칙 · 5. 이미지 다듬기 +6. 기본 팔레트 — 무채색 스케일 하나 + 포인트 하나 +7. 역할 체계 — brand / neutral / critical / warning / positive / informative × solid / weak / contrast + pressed +8. 레이어 표면 — basement / default / elevated / floating (라이트 · 다크) +9. 포커스 링 +10. 타입 램프 · 11. 간격 · 반경 · 그림자 스케일 +12. 토큰 파일로 옮기는 법 (웹 CSS 변수 · RN/Expo · Flutter · SwiftUI · Android) + +--- + +## 1. 글꼴 굵기(Weight) + +| 자리 | 굵기 | 비고 | +|---|---|---| +| 메인·코너 타이틀 | Bold / Semi-bold | 시각적 무게(visual weight)를 확실히. 단 120~400 급 대형은 Regular/Light 라야 투박하지 않아요 (`bold-mode.md`) | +| 서브 타이틀·탭·CTA | Semi-bold / Medium | 클릭 가능성과 구획 인지 | +| 본문·보조 | Regular | | + +- 영문 타이틀: 대문자 + Regular/Light 가 볼드보다 정교해 보여요. +- 폰트 종류보다 **크기·두께 대비**가 위계를 만들어요. 한 화면 굵기는 2~3단 (Regular · Semi-bold · Bold). +- 주의: 모든 글자에 Medium 만 연달아 쓰면 대비(contrast)가 없어 강조점이 사라져요. + +## 2. 팔레트 구성 원칙 + +- 기초는 **흰 바탕**(또는 연한 무채색) + 글자·여백으로 구획. +- **포인트 컬러 1개** — 가장 중요한 CTA / 선택 탭 / 강조 영역에만. +- 브랜드색을 카드·아이콘·탭·배너 사방에 칠하면 포인트 효과가 사라져요. +- 원색 남용·고채도 과다(oversaturation) 자제. 단정한 대안은 채도를 조금 낮춘 한 가지 색. +- 대비가 강한 이미지(고채도·레드/보라·화려한 인물)가 이어지면 중간에 미니멀·클래식 이미지로 리듬 조절. +- 색은 hex 가 아니라 **역할 이름**으로 지정해요 (`principles.md` A2). 아래 §7 의 역할 체계를 그대로 쓰면 라이트·다크가 같이 풀려요. + +## 3. 그라디언트 + +**쓰는 조건**: 글래스 블러 대체 · 동영상/이미지 위 흰 글자 가독성 · 배경 깊이감. + +| 용도 | 수치 | +|---|---| +| 방향 | 45° 대각선 | +| 카드·버튼 미세 | 위 2% ~ 아래 6% | +| 배경 딤(남색·어두운 톤) | 0% ~ 20~30% | +| 동영상·대형 비주얼 하단 가독성 | 흰/검정 10~30% 또는 30~80% | + +## 4. 어두운 배경 · 다크 규칙 + +- 어두운 바탕엔 **검정 배경 블러**. 흰 블러는 지저분해져요. +- 바텀시트·하단 탭바(독바): 불투명도 60~70% + 배경 블러 20~30. +- 대형 미디어 위 GNB 는 배경 박스를 제거하고 **투명**. +- 어두운 바탕 위 팝업·바텀시트는 **밝은 톤**(또는 다크 테마에선 한 단계 밝은 표면)으로 확 띄워야 "떴다" 가 인지돼요. 같은 어두운 톤이면 구분이 안 돼요. +- 다크 테마는 **층이 올라갈수록 밝아져요** (§8). 그림자는 어두운 바탕에서 거의 안 보이니 깊이는 명도 단계로 만들어요. 인접 층 사이 RGB 차이가 8 안팎이면 눈으로 안 갈려요 — 10~16 이상 벌려요 (`measure.md` 면 명도 단계). + +## 5. 이미지 다듬기 + +- **톤다운**: 배경을 깔끔히 후퇴시키고 오브젝트 위주 미니멀 이미지를 섞어 피로를 줄여요. +- **그림자 안착**: 공중에 뜬 상품 아래에 찌그러진 원형 레이어 + 레이어 블러 (불투명도 40% 근처) 로 바닥에 붙여요. +- **누끼 원근감**: 인물·상품 배경을 따고 대형 타이틀을 인물 뒤로 넘겨 배경-타이틀-인물 깊이를 만들어요 (매거진·랜딩 한정). + +--- + +## 6. 기본 팔레트 — 무채색 스케일 하나 + 포인트 하나 + +디자인 시스템이 없으면 이 값으로 시작해요. 대비는 흰 바탕(#FFFFFF) 기준 WCAG 2 대비율이에요 (APCA 기준은 `principles.md` C2). + +### 6.1 무채색 스케일 (neutral) + +| 단계 | 값 | 흰 바탕 대비 | 쓰는 자리 | +|---|---|---|---| +| n0 | #FFFFFF | — | 기본 표면 | +| n50 | #F7F8F9 | — | 눌림 오버레이 바탕, 아주 약한 면 | +| n100 | #F1F3F5 | — | basement 표면, 약한 채움(보조 버튼·칩) | +| n200 | #E6E8EB | — | 구분선(불투명 대체), 약한 채움 pressed | +| n300 | #D1D5DA | — | 입력 테두리, 체크박스 빈 칸 | +| n400 | #ADB3BA | 2.1:1 | 비활성 아이콘 · 비활성 테두리 (글자엔 쓰지 않아요) | +| n500 | #868E96 | 3.3:1 | placeholder · 비활성 글자 (본문엔 쓰지 않아요) | +| n600 | #6B7280 | 4.8:1 | 보조 글자 (캡션·날짜·단위) — 본문 크기에서도 4.5:1 통과 | +| n700 | #4B5563 | 7.6:1 | 강한 보조 글자, 아이콘 기본 | +| n800 | #2B2F36 | — | 다크 elevated 표면 근처, 반전 표면 | +| n900 | #16181C | 17.8:1 | 본문 글자 · 제목 · 무채색 주 버튼 채움 | + +### 6.2 포인트 (accent = brand) + +| 토큰 | 값 | 대비 | 비고 | +|---|---|---|---| +| accent | #2563EB | 흰 글자 5.2:1 | 채도를 조금 낮춘 파랑 — 프로젝트 브랜드색이 있으면 그 색으로 바꿔요 | +| accent-pressed | #1D4FC4 | | accent 를 명도 약 10% 내린 값 | +| accent-weak | #EAF1FE | | 선택 행·강조 배경 | +| accent-weak-pressed | #D9E6FD | | | +| accent-contrast | #FFFFFF | | accent 채움 위 글자·아이콘 | + +포인트를 바꿀 땐 **흰 글자 대비 4.5:1 이상**(채움 버튼 글자)과 **흰 바탕 위 3:1 이상**(아이콘·테두리)만 지키면 이 표의 나머지 구조는 그대로 써요. + +**브랜드색이 글자 대비 4.5:1 에 못 미칠 때**: +- 브랜드 색상(hue)을 말없이 바꾸지 않아요. 브랜드색은 글자가 아닌 채움(일러스트 · 배지 바탕 · 선택 표시)과 큰 글자(24 이상 또는 19 Bold 이상, 3:1)에만 그대로 써요. +- 같은 색상에서 명도만 내린 진한 값(예: 브랜드 #FF7A1A → 글자용 #C2410C)을 후보로 만들어 리포트 "결정 필요" 에 올려요. +- 진행은 **새 토큰 안에서만** 해요 — `accent-text` 처럼 새 이름을 만들어 그 진한 값을 담고, 이번 화면의 글자 · 채움 버튼 글자 자리만 그 토큰을 써요. +- 다른 화면이 쓰는 **기존 토큰 값을 진한 값으로 바꾸는 건 ⛔** — 바꾸기 전에 사용자에게 물어요. + +## 7. 역할 체계 + +색은 `{역할}-{강도}` 로 불러요. 역할은 여섯, 강도는 셋 + 눌림이에요. + +| 역할 | solid (채움) | solid-pressed | weak (약한 배경) | weak-pressed | contrast (solid 위 글자) | fg (흰 바탕 위 글자·아이콘) | +|---|---|---|---|---|---|---| +| brand | #2563EB | #1D4FC4 | #EAF1FE | #D9E6FD | #FFFFFF | #2563EB | +| neutral | #16181C | #2B2F36 | #F1F3F5 | #E6E8EB | #FFFFFF | #16181C | +| critical | #D92D3A | #B42330 | #FDECEE | #FAD9DD | #FFFFFF | #D92D3A | +| warning | #F59F00 | #D98C00 | #FFF6E0 | #FFEDC2 | #16181C | #B45309 | +| positive | #0B7F45 | #086637 | #E7F6EE | #CFEDDD | #FFFFFF | #0B7F45 | +| informative | #0369A1 | #025683 | #E6F3FA | #CCE6F4 | #FFFFFF | #0369A1 | + +규칙 +- **화면당 brand solid 는 하나** (주 CTA 또는 선택 상태). 두 번째 강조가 필요하면 neutral solid 로. +- **critical solid 는 되돌릴 수 없는 행동의 확인 버튼에만**. 일반 화면의 삭제 행·에러 문구는 critical fg. +- warning solid 는 밝은 색이라 contrast 가 **어두운 글자**예요 (흰 글자는 2.1:1 로 탈락). +- 상태 역할(critical · warning · positive · informative)은 **아이콘이나 문장과 함께** (`principles.md` C1). +- pressed 는 배경만 바꾸고 글자색(contrast · fg)은 유지해요 (`principles.md` B5). +- 비활성은 역할과 상관없이 하나로: 배경 n100, 글자 n500, 테두리 n300 (`component-systems.md` §0). + +## 8. 레이어 표면 + +깊이는 네 층으로만 나눠요 (`principles.md` B10). 같은 층에서 겹치면 층을 올리지 말고 선으로 구분해요. + +| 층 | 쓰는 자리 | 라이트 | 다크 | 깊이 표현 | +|---|---|---|---|---| +| basement | 묶음 목록 뒤 바탕, 설정 화면 배경 | #F1F3F5 | #0B0C0E | 없음 | +| default | 기본 페이지 표면 | #FFFFFF | #16181C | 없음 | +| elevated | 카드·하단 독바·고정 헤더 | #FFFFFF | #1F2227 | 라이트: 1px 선 10% 또는 그림자 Y 4 / 블러 12 / 6% · 다크: 명도 한 단계 | +| floating | 바텀시트·다이얼로그·팝오버·토스트 | #FFFFFF | #2A2E34 | 라이트: 그림자 Y 8 / 블러 24 / 12% + 딤 20~30% · 다크: 명도 두 단계 + 딤 50% | + +다크 글자: 본문 #F1F3F5, 보조 #9AA1A9 (#16181C 위 6.8:1), placeholder·비활성 #6B7280. 다크 포인트는 #5B8EF5 (#16181C 위 5.6:1) — 라이트 포인트를 그대로 쓰면 3.4:1 로 어두운 바탕에서 묻혀요. + +반전 표면(토스트): 배경 n900 / 글자 n0 / 액션 글자 다크 포인트(#5B8EF5). + +## 9. 포커스 링 + +- 키보드 포커스(`:focus-visible`)에서만 보여요. 마우스·터치로 눌렀을 땐 숨겨요. +- **2px 실선, 포인트색, 요소 바깥 2px 오프셋**. 입력 필드는 테두리 자체를 2px 포인트색으로 바꾸는 방식(안쪽 링)도 허용. +- 포인트 위에 놓인 요소(포인트 채움 버튼)는 링이 묻히니 흰 2px 안쪽 + 포인트 2px 바깥 이중 링. +- 비텍스트 대비 3:1 이상 — #2563EB 는 흰 바탕 5.2:1 로 통과. +- 플랫폼: 웹 `outline` + `outline-offset`, iOS/Android 는 시스템 포커스 표시(하드웨어 키보드·스위치 제어)를 끄지 않는 것이 먼저예요. + +## 10. 타입 램프 + +모바일 본문 16 기준이에요. 행간은 px 로 고정해 두면 큰 글씨 설정에서 비율이 유지돼요 (단위는 웹 rem · RN 의 `allowFontScaling` · iOS Dynamic Type · Android sp 로 사용자 설정을 따라가요 — `principles.md` A4). + +| 역할 토큰 | 크기 / 행간 | 굵기 | 자간 | 쓰는 자리 | +|---|---|---|---|---| +| caption-xs | 12 / 16 | Regular·Medium | 0 | 칩·뱃지 안 글자 전용 (본문 문장엔 쓰지 않아요) | +| caption | 13 / 18 | Regular | 0 | 날짜·보조 설명·단위 | +| label | 14 / 20 | Regular·Medium | −0.5% | 보조 본문·라벨·텍스트 버튼 | +| body | 16 / 24 | Regular | −1% | 본문·인풋·리스트 제목 (기준) | +| body-strong | 16 / 24 | Semi-bold | −1% | 버튼 글자·강조 본문 | +| subtitle | 18 / 26 | Semi-bold | −1.5% | 카드 제목·섹션 소제목 | +| title-sm | 20 / 28 | Bold | −2% | 섹션 제목 | +| title | 24 / 32 | Bold | −2% | 시트·다이얼로그 제목 | +| title-lg | 28 / 36 | Bold | −2.5% | 화면 타이틀 (= body × 1.75) | +| number | 32 / 40 | Bold | −2.5% | 핵심 숫자 (가격·잔액) | +| number-lg | 40 / 48 | Bold | −3% | 결과 화면 큰 숫자 | +| display | 48 / 56 | Bold | −3% | 웹 히어로 (모바일 앱에선 쓰지 않아요) | + +규칙 +- **화면(시트)마다 가장 큰 글자 ÷ body ≥ 1.75** — title-lg(28) 이상이 하나는 있어야 해요. 없으면 number 를 앵커로. +- 한 화면에 쓰는 크기는 3~4단. 표의 12단을 다 쓰라는 게 아니에요. +- 11 이하는 쓰지 않아요. 썸네일이 작아져도 본문은 줄이지 않아요. +- 자간 값은 한글·라틴 혼용 산세리프 기준이에요. 라틴 전용 본문이면 0 으로. +- 데스크톱 웹은 body 15~16 그대로, 타이틀만 32·40·48 로 한 단계씩 올려요. + +## 11. 간격 · 반경 · 그림자 스케일 + +| 종류 | 토큰 → 값 | +|---|---| +| 간격 | space-1 4 · space-2 8 · space-3 12 · space-4 16 · space-5 20 · space-6 24 · space-8 32 · space-10 40 · space-12 48 · space-16 64 · space-20 80 | +| 이름 붙은 간격 | gutter(화면 좌우) 20 · component-gap(부품 안) 8 · group-gap(그룹 사이) 24 · section-gap(섹션 사이) 40 · bottom-safe(하단 CTA 아래) 16 + 안전 영역 | +| 반경 | radius-xs 4 · radius-sm 8 · radius-md 12 (버튼·인풋) · radius-lg 16 (카드) · radius-xl 24 (시트 상단) · radius-full 9999 (칩·토글) | +| 그림자 | shadow-1 Y 4 / 블러 12 / 4% (세그먼트 선택 · 칩처럼 면 안에서 살짝) · shadow-2 Y 4 / 블러 12 / 6% (elevated) · shadow-3 Y 8 / 블러 24 / 12% (floating) — 전부 규칙 범위 **Y 4~8 / 블러 12~24 / 3~12%** 안 | +| 선 | border 1px · 검정 10~20% 또는 n200 | +| 모션 | duration-fast 150ms (눌림·토글) · duration-base 200ms (부품) · duration-slow 300ms (시트·화면 전환) | + +간격 스케일 밖의 값(−4 · 10 · 14 · 18 · 60)은 쓰지 않아요. **안 여백 < 바깥 여백** 이라야 그룹핑이 생겨요. + +## 12. 토큰 파일로 옮기는 법 + +토큰은 **한 파일**에 두고, 화면 파일은 이름으로만 가져다 써요 (매직 넘버 금지). 이미 토큰 파일이 있으면 거기에 없는 역할만 추가해요. + +| 스택 | 파일 | 모양 | +|---|---|---| +| 웹 (CSS) | `src/styles/tokens.css` | `:root { --color-brand-solid: #2563EB; --space-4: 16px; … }` + `@media (prefers-color-scheme: dark)` 또는 `[data-theme="dark"]` 에서 같은 이름을 다시 정의 | +| 웹 (Tailwind) | `tailwind.config.*` 의 `theme.extend` | CSS 변수를 가리키게: `colors: { brand: { solid: 'var(--color-brand-solid)' } }` | +| React Native · Expo · Lynx | `src/theme/tokens.ts` | `export const color = { brand: { solid: '#2563EB', … } } as const; export const space = { 4: 16, … }` + 다크용 객체, `useColorScheme` 로 고름 | +| Flutter | `lib/theme/tokens.dart` | `ThemeExtension` 으로 역할 색, `TextTheme` 으로 타입 램프 | +| SwiftUI | `Theme/Tokens.swift` + Asset Catalog 색 (Any/Dark) | `Color("brandSolid")`, `Font.custom(size:relativeTo:)` 로 Dynamic Type 연동 | +| Android | `res/values/colors.xml` + `values-night/colors.xml`, Compose 면 `ColorScheme` 확장 | 글자 크기는 sp | + +`spec-template.md` 표 0 의 값과 이 파일의 값이 이름까지 같아야 해요. diff --git a/skills/screen/references/component-systems.md b/skills/screen/references/component-systems.md new file mode 100644 index 0000000..cbea6ae --- /dev/null +++ b/skills/screen/references/component-systems.md @@ -0,0 +1,704 @@ +# 디자인 시스템별 부품 값 비교 — 합의 규칙 · 시스템마다 다른 값 · 상태 · 접근성 + +공개 디자인 시스템과 표준(shadcn/ui · Material 3 · Chakra UI v3 · Apple HIG · WAI-ARIA APG · WCAG)을 직접 읽고 **공급자 중립 규칙**만 다시 쓴 비교표예요. 이 skill 은 이 시스템들에 **의존하지 않아요** — 이름과 출처는 "왜 이 숫자인가" 의 근거로만 남겨요. 프로젝트에 자기 디자인 시스템이 있으면 그 값이 이기고, 이 표는 그 값이 실무 범위 안인지 보는 잣대예요. + +- 숫자마다 출처 키(`[키]`)를 달았어요. URL 은 맨 아래 **출처**. 직접 확인하지 못한 값은 **(미확인)** 이에요. +- 출처 없이 "(관행)" 이라 적힌 규칙은 여러 실무 팀에서 반복되지만 위 공개 문서엔 명시가 없는 것이에요. +- `components.md`(16종 규격)가 "어떻게 그리나" 라면, 이 파일은 "시스템들은 몇으로 정했고 어디서 갈리나" 예요. + +단위 메모 +- shadcn·Chakra 는 Tailwind/Panda 스페이싱이에요. 1단위 = 4px(0.25rem) — `h-9` = 36px, `size-4` = 16px, Chakra `h: "10"` = 40px. +- M3 모양 단계: extra-small 4 · small 8 · medium 12 · large 16 · extra-large 28 · full [M3-shape]. dp ≈ CSS px. +- Chakra 반경: l1 = xs = 2px, l2 = sm = 4px, l3 = md = 6px [CH-radii]. +- HIG pt ≈ CSS px (1x 기준). + +## 목차 + +- §0 모든 부품에 걸리는 공통 규칙 (터치 · 대비 · 비활성 · 포커스 · 눌림 · 상태 레이어 수치) +- §1 Button · §2 Text field · §3 List row · §4 Card · §5 Chip · §6 Tabs·Segmented · §7 Bottom sheet·Dialog · §8 Toast·Snackbar · §9 Checkbox·Radio·Switch · §10 Top app bar + - 부품마다: 합의된 규칙 → 시스템마다 다른 값 → 상태 체크리스트 → 접근성·키보드 → 문구 → 안티패턴 +- §11 토큰 역할 이름 대응표 (역할 → 각 시스템 이름) +- §12 디자인 시스템이 없을 때 기본값 (한 장 요약) +- 출처 + +--- + +## 0. 모든 부품에 걸리는 공통 규칙 + +### 0.1 합의된 규칙 + +- **최소 터치 영역**: 모바일은 44–48. HIG iOS 기본 44×44pt, 최소 28×28pt [HIG-a11y]. M3 칩 min touch target 48dp [M3-chip-doc], 텍스트필드 아이콘 min 48dp [M3-tf-doc]. WCAG 2.2 AA 24×24 CSS px (간격 예외 있음) [WCAG-258], AAA 44×44 [WCAG-255]. 아주 작은 스위치도 터치 영역은 24 이상을 보장해요 (관행). +- **보이는 모양이 작아도 터치 영역은 커야 해요**: M3 체크박스는 18px 상자에 40px 상태 레이어 [M3-cb]. 라벨을 포함한 영역 전체가 타깃이고, 리스트에서는 행 전체가 타깃이에요 (관행 · `principles.md` C3). +- **대비**: 본문 텍스트 4.5:1, 18pt 이상 큰 텍스트 3:1 [HIG-a11y]. 비텍스트 UI(테두리·포커스 링·아이콘)는 3:1 (WCAG 1.4.11, 미확인 — 페이지 미조회). APCA 로 재면 역할별 기준이 달라요 — `principles.md` C2. +- **비활성 처리**: shadcn·Chakra 는 `opacity: 0.5` 하나 [SH-button][CH-layer]. M3 는 컨테이너 12%, 내용 38% [M3-fb]. 전용 disabled 배경·글자 토큰을 두는 시스템도 있어요. → 비활성은 투명도 하나가 아니라 **"배경과 글자 둘 다 약해진다"** 로 정의하고, 비활성이어도 글자는 읽혀야 해요. +- **포커스 표시**: shadcn 은 `focus-visible:ring-[3px] ring-ring/50` + 테두리 `border-ring` [SH-button], Chakra 는 `focusVisibleRing: outside`(입력은 inside) [CH-button][CH-input]. → 마우스 클릭이 아니라 **키보드 포커스(`:focus-visible`)에서만** 링을 보여 줘요. +- **눌림 피드백은 필수예요**: HIG 는 커스텀 버튼에 press 상태를 반드시 넣으라고 해요 [HIG-btn]. M3 는 pressed 상태 레이어 12% [M3-state]. 모든 인터랙티브 부품에 pressed 정의를 두고, 필요하면 축소(scale) 피드백도 더해요 (`principles.md` B2–B8). +- **브랜드 색은 아껴 써요**: HIG 는 prominent 버튼을 뷰마다 가장 가능성 높은 액션 하나에만 쓰라고 해요 [HIG-btn][HIG-toolbar]. 브랜드 색은 핵심 액션·사용자 간 연결에만 (`principles.md` A3). + +### 0.2 상태 레이어 수치 (다른 시스템이 따로 정하지 않았을 때의 기본값은 M3) + +| 상태 | M3 [M3-state] | shadcn [SH-button] | Chakra [CH-button] | +|---|---|---|---| +| hover | 8% 오버레이 | `bg-primary/90` | `colorPalette.solid/90` | +| focus | 12% | 3px 링 50% | ring outside | +| pressed | 12% | (hover 와 같음) | — | +| dragged | 16% | — | — | +| disabled | 컨테이너 12% / 내용 38% | opacity 50% | opacity 50% | + +모바일 우선 시스템은 hover 를 두지 않고 pressed 전용 토큰 + 축소를 써요 (관행). + +--- + +## 1. Button + +### 1.1 합의된 규칙 + +1. **화면당 가장 강한 버튼은 하나예요.** HIG: 가장 가능성 높은 액션 하나에만 prominent 스타일, 툴바도 primary 액션은 하나만 [HIG-btn][HIG-toolbar]. +2. **위계는 크기가 아니라 스타일로 나눠요.** HIG: 선택지 여러 개를 같은 크기로 두고 선호하는 것만 더 강한 스타일로 [HIG-btn]. Solid + Weak, 또는 Outline 둘을 조합 (관행). +3. **강조 3단계**: 강(solid fill) · 중(약한 fill — tonal/secondary/subtle) · 약(outline/ghost/text). shadcn `default/secondary/outline/ghost/link` [SH-button], Chakra `solid/subtle/surface/outline/ghost/plain` [CH-button], M3 filled/tonal/outlined/elevated/text [M3-btn-doc]. +4. **파괴적 액션은 따로 표시하고 기본 선택으로 두지 않아요.** shadcn `destructive` [SH-button]. HIG 는 파괴적 버튼에 primary role 을 주지 말라고 해요 — 사람들이 읽지 않고 누를 수 있어서 [HIG-btn]. 위험 채움은 삭제·초기화처럼 되돌릴 수 없는 작업에만, 주로 확인 대화상자 안에서 (관행). +5. **라벨은 동사로 시작하고 결과를 말해요.** HIG "Add to Cart" 처럼 동사로 시작 [HIG-btn], 알림 버튼은 1–2단어 [HIG-alert]. '다음' 이 아니라 '시작하기' 처럼 무엇을 하는지 (관행 · `ux-writing.md`). +6. **아이콘은 앞이나 뒤 중 한쪽에만 둬요.** 앞 = 의미 보조, 뒤 = chevron 같은 동작 보조 — 둘을 함께 쓰지 않아요 (관행). M3 도 아이콘은 라벨 앞 [M3-btn-doc]. +7. **아이콘만 있는 버튼에는 접근성 라벨이 필수예요.** 꼭 필요할 때만 쓰고, 정사각 크기를 따로 둬요 (shadcn `size-9`/`icon`, Chakra `minW`=h) [SH-button][CH-button]. +8. **로딩 상태는 버튼 안에서 보여 줘요.** HIG 는 바로 끝나지 않는 액션이면 버튼 안에 activity indicator 를 두라고 해요 [HIG-btn]. 스피너 14–18, 너비 유지 (관행). +9. **나란히 두는 버튼은 2개까지예요.** 3개 이상은 넘치는 액션을 icon-only 나 더보기로. 닫기 성격의 Weak + Solid 조합은 3:7 비율 (관행). HIG 알림은 버튼 최대 3개 [HIG-alert]. + +### 1.2 시스템마다 다른 값 + +| 시스템 | 크기별 높이 | 가로 패딩 | 반경 | 아이콘 | 글자 | +|---|---|---|---|---|---| +| M3 expressive [M3-bxs] | XS 32 · S 40 · M 56 · L 96 · XL 136 | 12 · 16 · 24 · 48 · 64 | round = full / square = 12·12·16·28·28 | 20 · 20 · 24 · 32 · 40 | label-large | +| shadcn [SH-button] | sm 32 · default **36** · lg 40 (xs 24) | 12 · 16 · 24 (아이콘 있으면 10/12/16) | `rounded-md` | 16 (xs 12) | 14 medium | +| Chakra [CH-button] | 2xs 24 · xs 32 · sm 36 · **md 40** · lg 44 · xl 48 · 2xl 64 | 8 · 10 · 14 · 16 · 20 · 20 · 28 | l2 = 4px | 14 · 16 · 16 · 20 · 20 · 20 · 24 | xs/sm/md/lg, medium | +| HIG | iOS 터치 최소 44×44pt [HIG-a11y]. 고정 높이 단계는 visionOS 만 공개: 28·32·44·52·64pt [HIG-btn] | — | 캡슐/라운드 사각 | SF Symbols | 대표 텍스트 스타일 | + +정리 +- 웹 대시보드 기본 높이는 **36–40** (shadcn 36, Chakra 40, M3 S 40). +- 모바일 하단 CTA 는 **52–56** (M3 M 56, 모바일 앱 관행 52 — `locale-ko.md`). +- 32 이하(M3 XS, shadcn sm)는 눈에 보이는 크기일 뿐이에요. 터치 영역은 따로 44–48 을 확보해요. + +### 1.3 상태 체크리스트 + +- [ ] default (enabled) +- [ ] hover — 포인터 환경에서만 (shadcn `/90`, Chakra `/90`) +- [ ] pressed — 색을 바꾸거나 축소 (HIG 필수) +- [ ] focus-visible — 링 3px 또는 외곽 링 +- [ ] disabled — 배경과 글자 모두 약하게, `pointer-events: none`. 이유를 알려야 하면 `aria-disabled` + 설명 [APG-btn] +- [ ] loading — 라벨 자리에 스피너. 너비는 유지하고 중복 클릭은 막아요 +- [ ] (토글 버튼) selected — `aria-pressed`, 라벨은 상태가 바뀌어도 그대로 [APG-btn] +- [ ] invalid — shadcn `aria-invalid:border-destructive` [SH-button] + +### 1.4 접근성·키보드 + +- `role=button` 이고 Enter 와 Space 로 실행해요 [APG-btn]. 토글은 `aria-pressed` 를 쓰고 라벨은 바꾸지 않아요 ("Mute" 그대로) [APG-btn]. 메뉴를 여는 버튼은 `aria-haspopup` [APG-btn]. +- 실행한 뒤 포커스는 다이얼로그를 열었으면 다이얼로그 안으로, 닫았으면 원래 버튼으로 돌아가요 [APG-btn]. + +### 1.5 문구 규칙 + +- 동사로 시작하고 사용자 관점에서 결과를 써요. 같은 액션은 어디서나 같은 단어로 [HIG-btn]. +- 1–2단어, 끝에 마침표 없음. 영어는 Title Case (HIG) [HIG-alert]. 한국어는 '~하기' 처럼 동사형으로 (`locale-ko.md`). +- macOS 에서 다른 창이나 뷰를 여는 버튼은 라벨 끝에 말줄임(…) [HIG-btn]. +- 'OK/확인/다음' 처럼 모호한 라벨은 피해요 [HIG-alert]. + +### 1.6 안티패턴 + +- 브랜드 색 버튼을 여러 개 두는 것 (관행 · [HIG-btn]) +- 앞뒤 아이콘을 동시에 쓰는 것 (관행) +- 버튼 3개 이상을 나란히 두는 것 — 큰 글씨 설정에서 잘려요 (관행) +- 크기로 위계를 나누는 것 [HIG-btn] +- 파괴적 액션을 primary 로 두는 것 [HIG-btn] +- Outline 버튼을 Solid 와 한 줄에 섞는 것 (관행) +- 커스텀 버튼에 press 상태가 없는 것 [HIG-btn] +- 액션 대신 정보나 선택 상태를 보여 주는 데 버튼을 쓰는 것 → Chip 을 써요 (관행) + +--- + +## 2. Text field / Input + +### 2.1 합의된 규칙 + +1. **라벨은 placeholder 와 따로 둬요.** HIG 는 placeholder 가 입력하면 사라지니 별도 라벨을 두라고 하고 [HIG-tf], MDC 는 hint 를 바깥 레이아웃에 두라고 해요 [M3-tf-doc]. 입력은 라벨·설명·에러를 담는 Field 안에서 써요 (관행). `components.md` §8 의 "플레이스홀더 통합" 은 항목이 1–2개인 짧은 폼에 한정하고, 그때도 접근성 라벨은 남겨요. +2. **테두리는 기본 1px, 포커스·에러 때 2px**: M3 outlined 1→2px (포커스 `primary`, 에러 `error`) [M3-otf]. shadcn 은 테두리 1px 에 링 3px [SH-input]. +3. **에러는 색만으로 알리지 않아요.** M3 는 에러 상태에서 라벨·테두리·보조 텍스트·trailing 아이콘을 모두 `error` 로 바꿔요 [M3-otf]. 접근성 라벨이 달린 에러 메시지를 함께 보여 줘요 [M3-tf-doc]. +4. **값이 있으면 Clear 버튼을 줘요**: HIG (트레일링 끝) [HIG-tf]. +5. **형식이 정해진 값(전화번호·주민번호)은 칸을 나누지 말고 자동으로 포맷해요** (관행). 숫자는 formatter 와 맞는 키보드 타입을 써요 [HIG-tf]. +6. **검증 시점**: 이메일은 다음 필드로 넘어갈 때, 아이디·비밀번호는 넘어가기 전에 [HIG-tf]. +7. **비활성 / 읽기 전용을 구분해요**: 둘 다 배경이 약해지지만 읽기 전용은 글자를 보조 글자색으로 유지해 복사·확인할 수 있게 (관행). +8. **필드 너비는 예상 입력 길이에 맞춰요** [HIG-tf]. M3 기본 최대 너비 488dp, 라벨이 있으면 최소 88dp [M3-tf-doc]. + +### 2.2 시스템마다 다른 값 + +| 시스템 | 높이 | 가로 패딩 | 반경 | 글자 | 아이콘 | +|---|---|---|---|---|---| +| M3 outlined [M3-otf] | 56dp (미확인 — 토큰 파일에 height 없음) | — | extra-small 4 | body-large | 24 | +| shadcn [SH-input] | **36** (`h-9`) | 12 | `rounded-md` | 16 (모바일) / 14 (md 이상) | — | +| Chakra [CH-input] | 2xs 28 · xs 32 · sm 36 · **md 40** · lg 44 · xl 48 · 2xl 64 | 8 · 8 · 10 · 12 · 16 · 18 · 20 | l2 = 4px | xs·xs·sm·sm·md·md·lg | — | +| 모바일 앱 관행 | 48–52 (40 은 데스크톱 전용) | 14–16 | 8–12 | 16 | 20 | +| 여러 줄 입력 (관행) | 자동 높이 최소 3줄 / 고정이면 최소 2줄 + 최대 높이 필수 | | | | | + +**모바일 입력 글자는 16px 이상**이에요. shadcn 은 모바일에서 `text-base`(16) 를 쓰고 md 이상에서만 14 로 줄여요 [SH-input]. iOS Safari 는 16px 미만 입력에 포커스하면 화면을 확대해요 (미확인 — 공식 문서 미조회). + +### 2.3 상태 체크리스트 + +- [ ] enabled (빈 값 / 값 있음) +- [ ] hover (M3 hover outline 1px on-surface) [M3-otf] +- [ ] focused (2px 강조 테두리 또는 링) +- [ ] error / error + focused [M3-otf] +- [ ] disabled (글자·placeholder·아이콘 모두 disabled 색) +- [ ] read-only + +### 2.4 접근성·키보드 + +- 보이는 라벨을 `