# UI Validate

> Unified UI validation skill — hierarchy (Playwright DOM saliency + WCAG AA), motion (CSS transition vs RESPECT.md drift), drift (pHash N-screen consistency), mobile (375/768/1440px breakpoint). Each check is independently runnable and independently failable. --check 인자 미명시 시 에러 출력 후 사용 가능한 check 목록 안내 — auto-run 절대 금지., tc-gate (QA_CHECKLIST.md TC-ID별 Playwright 스크린샷 증거 생성 → harness/ui-evidence/)

- Skill: `kimsanguine/ui-validate` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add kimsanguine/ui-validate`
- Raw SKILL.md: https://api.skillmd.com/api/skills/kimsanguine/ui-validate/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: kimsanguine (https://skillmd.com/u/kimsanguine)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/kimsanguine/ui-validate

---


## Core Goal

4종 UI 검증 check를 단일 인터페이스로 통합한다:

| check | 책임 | 출력 파일 | LLM |
|---|---|---|---|
| `--check hierarchy` | Playwright + DOM saliency + WCAG AA 시각 계층 측정 | `.design/hierarchy-report.json` | ❌ |
| `--check motion` | CSS transition vs RESPECT.md motion_language 일관성 | `.design/motion-drift.md` | ❌ |
| `--check drift` | N 화면 pHash + DOM 구조 시각 drift 감지 | `.design/ui-drift-report.md` | ❌ |
| `--check mobile` | 375/768/1440px 브레이크포인트 Playwright 검증 | `mobile-check-report.md` | ❌ |
| `--check tc-gate` | QA_CHECKLIST.md TC-ID별 Playwright 스크린샷 → `harness/ui-evidence/` | `harness/ui-evidence/summary.json` | ❌ |

> ⚠️ **`--check` 인자 필수**: 미명시 시 에러 출력 후 사용 가능한 check 목록 안내. auto-run 절대 금지.

---

## Rule 5 준수 — 모든 측정이 결정론

| 검증 | 도구 | LLM |
|---|---|---|
| fold_density, type_hierarchy, color_60_30_10, whitespace, cta_count | Playwright DOM + pixel | ❌ |
| WCAG AA contrast | WebAIM relative luminance 알고리즘 | ❌ |
| CSS transition duration/easing 추출 | 정규식 + CSSOM | ❌ |
| framer-motion props | AST 파서 | ❌ |
| pHash 비교 | imagehash library | ❌ |
| 색상 palette | KMeans on screenshot | ❌ |
| 브레이크포인트 Playwright | npx playwright test | ❌ |

---

## 실패 처리 원칙

- **각 check는 독립 실패 가능** — 한 check 실패가 다른 check를 막지 않음
- **baseline 없으면 SKIP** (FAIL 아님) — baseline 부재는 데이터 미비이지 위반이 아님
- **타임아웃**: 30초/check — 초과 시 해당 check SKIP + warning
- **respect (--mode checkpoint·--mode brief)**는 이 스킬에 통합하지 않음

---

## Trigger Gate

### Use This Skill When
- craft-lint 정적 검증 통과 후 런타임 측정 → `--check hierarchy`
- 리디자인 후 motion 일관성 검증 → `--check motion`
- 5+ 화면 생성 후 일관성 확인 → `--check drift`
- ship 직전 모바일 브레이크포인트 검증 → `--check mobile`
- qa-checklist 실행 후 TC-ID별 브라우저 스크린샷 증거 생성 → `--check tc-gate [URL]`

### Route to Other Skills When
- RESPECT.md 갱신 → `deliver/respect --mode brief`
- ship 직전 종합 게이트 → `deliver/respect --mode checkpoint`
- 디자인 토큰 갱신 → 프로젝트 DESIGN.md

### Boundary Checks
- `--check` 미명시 → 즉시 에러 + 사용 가능한 check 목록 출력 (auto-run 금지)
- `harness/QA_CHECKLIST.md` 부재 → fail loud + "qa-checklist 먼저 실행하세요"
- Playwright 미설치 → fail loud + 설치 안내
- RESPECT.md 부재 시 hierarchy/motion → fail loud + "respect --mode brief 먼저"
- DESIGN.md 부재 시 mobile → fail loud + "design-token 먼저"

---

## Inputs

| 입력 | 출처 | 처리 |
|---|---|---|
| `--check` | `$ARGUMENTS` | hierarchy/motion/drift/mobile 분기 |
| target (URL/path/dir) | `$ARGUMENTS` (check 이후 나머지) | 각 check 내부 처리 |
| RESPECT.md | `.design/RESPECT.md` | hierarchy/motion 기준 |
| DESIGN.md | 프로젝트 루트 또는 `.design/DESIGN.md` | mobile 브레이크포인트 |

---

## Instructions

You are running ui-validate with arguments: **$ARGUMENTS**

### 공통 Step 0 — check 파싱

```
args = parse("$ARGUMENTS")
check = args.get("--check", None)
target = args remainder after --check value
```

`--check` 미명시 시:
```
❌ 에러: --check 인자가 필요합니다.

사용 가능한 check:
  --check hierarchy   Playwright DOM saliency + WCAG AA 시각 계층 측정
  --check motion      CSS transition vs RESPECT.md motion 일관성
  --check drift       N 화면 pHash 비교 시각 drift 감지
  --check mobile      375/768/1440px 브레이크포인트 검증

예시: ui-validate --check hierarchy https://habix.ai
     ui-validate --check mobile http://localhost:3000
```

---

### check: hierarchy

**대상**: URL 또는 HTML 경로

**Step 1 — Playwright + RESPECT.md 로드**
- `npx playwright install` 확인
- `.design/RESPECT.md` yaml 파싱 → hierarchy_rules 섹션
- RESPECT.md 부재 시 fail loud

**Step 2 — 5 룰 + WCAG 결정론 측정 (LLM 0)**

```python
# 측정 알고리즘 (의사코드)
page = playwright.new_page(viewport={"width": 1440, "height": 1080})
page.goto(target)

# Rule 1 — fold_density
elements = page.eval("...above-fold elements count...")
fold_pass = elements <= rules["max_elements_above_fold"]

# Rule 2 — type_hierarchy
sizes_px = sorted(unique font sizes from CSSOM, reverse=True)
ratios = [sizes_px[i] / sizes_px[i+1] for i]
type_pass = len(sizes_px) <= rules["max_type_scale"] and all(r >= 1.25 for r)

# Rule 3 — color_60_30_10
screenshot → KMeans(3) → percentages
color_pass = all(abs(pct[i] - target[i]) <= 10 for i in range(3))

# Rule 4 — whitespace_ratio
light_mask = pixels > 240; ratio = light_mask.sum() / total
whitespace_pass = ratio >= rules["whitespace_min"]

# Rule 5 — cta_count_above_fold
ctas_above = [c for c in page.locator("button, a[role='button'], .cta").all() if y < 1080]
cta_pass = len(ctas_above) == rules["cta_count_above_fold"]

# WCAG AA contrast
all text elements → (foreground, background) → relative luminance ratio
wcag_violations = [e for e if ratio < 4.5 (normal) or < 3.0 (large 18pt+)]
```

**Step 3 — `.design/hierarchy-report.json` 출력**

**Step 4 — 통과/실패 판정**
- 모두 pass → "hierarchy check PASS"
- 실패 시 element selector + fix 권유

---

### check: motion

**대상**: src 디렉터리 또는 컴포넌트 파일 경로

**Step 1 — RESPECT.md motion_language 로드**
- hover_transition_ms / page_transition_easing / scroll_reveal
- 부재 시 fail loud

**Step 2 — 파일 결정론 스캔 (LLM 0)**
```bash
grep -rn -E "transition:|duration-[0-9]+|cubic-bezier\(" $target \
  --include="*.css" --include="*.scss" --include="*.tsx" --include="*.jsx" --include="*.vue"
```

**Step 3 — framer-motion props 추출 (AST)**
- `transition={{ duration: N }}` 패턴 → 단위 변환 (s → ms)

**Step 4 — RESPECT 명세와 비교**
- hover_transition_ms ± 50ms tolerance
- easing 함수 문자열 완전 일치
- 위반 사항 리스트

**Step 5 — `.design/motion-drift.md` 출력**

**Step 6 — 통과/실패 판정**
- drift 0 → PASS
- drift ≥ 1 → FAIL + 파일·라인 명시

---

### check: drift

**대상**: URL 리스트 (쉼표 구분) 또는 스크린샷 디렉터리 경로

**Step 1 — 화면 캡처 또는 스크린샷 로드**
- URL 입력 → Playwright 다중 navigate + screenshot
- 디렉터리 입력 → PNG 파일 로드

**Step 2 — 각 화면 메트릭 결정론 추출**
- pHash (16-bit perceptual hash)
- color palette top 5 (KMeans)
- font-family set (DOM scrape)
- spacing 모드값 (margin/padding 빈도 top 3)
- DOM tree depth + node count

**Step 3 — baseline 대비 drift score 계산 (LLM 0)**
```python
drift = {
    "phash": hamming_distance(baseline.phash, current.phash),
    "color": palette_distance(baseline.palette, current.palette),
    "font": jaccard_distance(baseline.fonts, current.fonts),
    "spacing": rmse(baseline.spacing, current.spacing),
    "structure": tree_edit_distance(baseline.dom, current.dom),
}
```

**Step 4 — drift 임계치 비교**
- phash > 40: drift
- color: palette 변경 1개 이상 + 비중 > 10%: drift
- font: 기존 family 외 새 font: drift
- spacing: rmse > 4px: drift
- structure: edit_distance > 30: drift

**Step 5 — `.design/ui-drift-report.md` 출력**

**Step 6 — 종합 verdict**
- drift 0 → PASS
- drift ≥ 1 화면 → fix 권유 + design-system 회귀 의심

---

### check: mobile

**대상**: URL 또는 기본값 `http://localhost:3000`

**Step 1 — DESIGN.md 존재 확인**
- 없으면 fail loud + "design-token 먼저"

**Step 2 — Playwright 환경 확인**
```bash
npx playwright --version 2>/dev/null || echo "not installed"
```

**Step 3 — Playwright 테스트 파일 확인/생성**
- `tests/design/mobile-check.spec.ts` 없으면 references/playwright-setup.md 기반 생성

**Step 4 — 3 뷰포트 실행**
```bash
TARGET_URL=${target:-http://localhost:3000} \
  npx playwright test tests/design/mobile-check.spec.ts --reporter=line 2>&1
```

**Step 5 — ASCII 결과 출력**
```
┌─── mobile-check 결과 ────────────────────────────┐
│  375px   ✅/❌ N/3                               │
│  768px   ✅/❌ N/3                               │
│  1440px  ✅/❌ N/1                               │
│  이슈: (있을 때만)                               │
│  수정 후 재실행: ui-validate --check mobile      │
└──────────────────────────────────────────────────┘
```

**Step 6 — 리포트 저장**
- 이슈 1개 이상 → `mobile-check-report.md` 생성
- 이슈 없음 → 파일 생성 안 함

---

### check: tc-gate

**대상**: URL (배포된 앱 또는 localhost)

**Step 1 — QA_CHECKLIST.md 로드**

```bash
ls harness/QA_CHECKLIST.md 2>/dev/null || echo "CHECKLIST_MISSING"
```

CHECKLIST_MISSING 시:
```
❌ 에러: harness/QA_CHECKLIST.md 없음.
/qa-checklist 먼저 실행하세요.
```
즉시 종료.

**Step 2 — TC 목록 추출 (결정론)**
```bash
python3 deliver/skills/ui-validate/scripts/pw_runner.py --parse-only harness/QA_CHECKLIST.md
```
TC-ID / severity / 시나리오 목록 출력. critical → major → minor 순 정렬.

**QA_CHECKLIST Expected State 컬럼 (선택)**

TC에 Playwright assertion을 추가하려면 8번째 컬럼으로 `Expected State`를 추가한다.
컬럼이 없거나 `—`이면 기존 스크린샷 전용 모드로 동작한다.

```markdown
| TC-ID | 시나리오 | 환경 | 전제조건 | 기대결과 | PRD출처 | Expected State | Severity |
|-------|----------|------|----------|----------|---------|----------------|----------|
| TC-001 | 로그인 성공 | Chrome | 유효한 자격증명 | 대시보드 이동 | §11 | url_contains:/dashboard | critical |
| TC-002 | 오류 메시지 표시 | Chrome | 잘못된 비밀번호 | 에러 메시지 노출 | §14 | element_text:#error-msg:입력 오류 | major |
| TC-003 | 버튼 상태 | Chrome | — | 버튼 활성화 | §11 | element_exists:#submit-btn | minor |
| TC-004 | 화면 일반 점검 | Chrome | — | 레이아웃 정상 | §11 | — | minor |
```

**지원 assertion 타입 3가지:**
- `url_contains:<path>` — 현재 URL에 path가 포함되는지
- `element_exists:<selector>` — CSS selector 요소 존재 여부
- `element_text:<selector>:<text>` — 요소 텍스트에 text가 포함되는지

gstack /browse 대비 차별점: assertion이 TC-ID → QA_CHECKLIST → PRD §11/§14로 추적 가능.

**Step 3 — 출력 디렉터리 준비**
```bash
mkdir -p harness/ui-evidence
```

**Step 4 — Playwright 스크린샷 실행**
```bash
python3 deliver/skills/ui-validate/scripts/pw_runner.py \
  --url "$TARGET_URL" \
  --checklist harness/QA_CHECKLIST.md \
  --output harness/ui-evidence
```

각 TC-ID별:
- Playwright 브라우저 → URL 네비게이트
- 전체 화면 스크린샷 → `harness/ui-evidence/[TC-ID].png`
- 결과 기록 (captured / error)

**Step 5 — summary.json 생성**

`pw_runner.py`가 자동 생성:
```json
{
  "generated": "YYYY-MM-DD",
  "url": "...",
  "total": N,
  "critical_captured": X,
  "critical_total": X2,
  "major_captured": Y,
  "major_total": Y2,
  "minor_captured": Z,
  "minor_total": Z2,
  "tc_results": [
    {"id": "TC-001", "severity": "critical", "screenshot": "harness/ui-evidence/TC-001.png", "status": "captured"},
    {"id": "TC-002", "severity": "critical", "screenshot": "harness/ui-evidence/TC-002.png", "status": "error", "error": "timeout"}
  ]
}
```

**Step 6 — 결과 출력**
```
✅ harness/ui-evidence/ 생성 완료
   Total: N | Critical: X/X2 | Major: Y/Y2 | Minor: Z/Z2
   Critical assertion 실패: K건  ← assertion 실패 있을 때만 출력
   실패: [error 목록 (있을 때만)]
   → harness-build --step quality-gate 에서 자동 검사
```

---

## Failure Handling

| 실패 상황 | 감지 | 대응 |
|---|---|---|
| --check 미명시 | 파싱 결과 None | 에러 출력 + check 목록 안내, 실행 중단 |
| Playwright 미설치 | which playwright 실패 | `npm install playwright && npx playwright install` 권유 |
| RESPECT.md 부재 (hierarchy/motion) | file not found | "deliver/respect --mode brief 먼저" fail loud |
| DESIGN.md 부재 (mobile) | file not found | "design-token 먼저" fail loud |
| baseline 없음 (drift) | 화면 수 < 2 | SKIP (FAIL 아님) + warning |
| 타임아웃 30초 초과 | per-check timeout | 해당 check SKIP + warning, 나머지 계속 |
| 화면 수 < 3 (drift) | argument count | "최소 3 화면 필요" warning + SKIP |
| `harness/QA_CHECKLIST.md` 부재 | ls 실패 | fail loud + "qa-checklist 먼저 실행하세요" 후 종료 |
| Playwright 미설치 (`tc-gate`) | python3 import 실패 | `pip install playwright && playwright install` 안내 |
| URL 응답 없음 | Playwright timeout | 해당 TC status="error" 로 기록, 나머지 계속 진행 |

---

## Quality Gate

- [ ] --check 인자 미명시 시 에러 + 목록 안내 (auto-run 0)
- [ ] hierarchy: 5 룰 + WCAG AA 모두 측정, LLM 호출 0
- [ ] motion: CSS/framer-motion 모두 스캔, motion-drift.md 출력, LLM 호출 0
- [ ] drift: 5 차원 모두 측정, baseline 명시, LLM 호출 0
- [ ] mobile: 3 뷰포트 모두 검증, ASCII 결과 출력
- [ ] 각 check 독립 실패 가능 (한 check FAIL이 다른 check를 막지 않음)
- [ ] tc-gate: QA_CHECKLIST.md 부재 시 즉시 종료 (auto-run 금지)
- [ ] tc-gate: critical TC 스크린샷 모두 시도, summary.json 생성
- [ ] tc-gate: error TC도 summary.json에 기록됨 (silent 실패 금지)

---

## Examples

### Good Example
**입력:** `--check hierarchy https://habix.ai`

**기대 동작:**
1. Playwright 1440×1080 navigate
2. 5 룰 + WCAG AA 결정론 측정
3. hierarchy-report.json 출력
4. 실패 항목 element selector 명시

### Good Example
**입력:** `--check mobile`

**기대 동작:**
1. DESIGN.md 확인
2. 3 뷰포트 (375/768/1440) Playwright 실행
3. ASCII 결과 출력
4. 실패 시 mobile-check-report.md 생성

### Bad Example
**입력:** `ui-validate` (--check 없음)

**기대 동작:**
```
❌ 에러: --check 인자가 필요합니다.
사용 가능한 check: hierarchy | motion | drift | mobile
```
실행 중단. auto-run 금지.

### Bad Example
**입력:** `--check hierarchy` (RESPECT.md 없음)

**기대 동작:** "RESPECT.md 부재 — deliver/respect --mode brief 먼저" fail loud

### Good Example
**입력:** `--check tc-gate http://localhost:3000`

**기대 동작:**
1. harness/QA_CHECKLIST.md 로드 → TC-001~TC-NNN 추출
2. critical TC 먼저 스크린샷
3. harness/ui-evidence/ 생성 + summary.json 작성
4. 통계 출력

### Bad Example
**입력:** `--check tc-gate http://localhost:3000` (QA_CHECKLIST.md 없음)

**기대 동작:**
```
❌ 에러: harness/QA_CHECKLIST.md 없음.
/qa-checklist 먼저 실행하세요.
```
실행 중단.

---

## Contextual Knowledge (auto-loaded)

### Playwright Setup
!`cat references/playwright-setup.md 2>/dev/null || echo ""`

### WCAG Algorithm
!`cat references/wcag-algorithm.md 2>/dev/null || echo ""`

### pHash Calibration
!`cat references/phash-tuning.md 2>/dev/null || echo ""`

### Tailwind Duration Mapping
!`cat references/tailwind-duration.md 2>/dev/null || echo ""`

