From Ohouse Design Plugin
Figma 디자인을 번들된 ODS 디자인 시스템(design-tokens.js + icons.js + ods-components-index.json) 자산으로 실제 DOM/CSS/JS 기반 HTML 프로토타입을 빌드하고, pixel-diff로 픽셀 단위 정확도를 검증한다. 43개 ODS 컴포넌트 전수 사용법은 references/components.md 참조. 사용자가 "Figma로 프로토타입", "ODS로 정확히 그려줘", "토큰·아이콘·컴포넌트 써서 빌드", "pixel-diff로 검증" 같은 요청을 할 때 트리거.
How this skill is triggered — by the user, by Claude, or both
Slash command
/ohouse-design-plugin:new-ods-ui-builderThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
ods-ui-build 스킬의 후속 버전. 두 가지가 다르다:
assets/design-tokens.jsassets/icons.jsassets/ods-components-index.jsonpixel-diff/README.mdpixel-diff/checklist.jspixel-diff/diff-flat.jspixel-diff/diff.jspixel-diff/diff.url.test.cjspixel-diff/gen-tables.jspixel-diff/gen-tables.test.cjspixel-diff/hub.jspixel-diff/inspect.jspixel-diff/match-ods.jspixel-diff/match-ods.test.cjspixel-diff/package-lock.jsonpixel-diff/package.jsonpixel-diff/resolve-ods.jspixel-diff/resolve-ods.test.cjsreferences/components.mdreferences/example-home.htmlods-ui-build 스킬의 후속 버전. 두 가지가 다르다:
assets/design-tokens.js, assets/icons.js, assets/ods-components-index.json. Downloads 경로 의존 없음.references/components.md에 모든 컴포넌트의 node_id · variant props · 권장 DOM이 정리됨. Figma 인스턴스를 만나면 이 문서에서 매핑 찾고 시작.fileKey/nodeId 추출, Figma REST API로 노드 트리 + 실측 좌표/fill/font 수집componentId/componentSetId를 assets/ods-components-index.json에서 조회 → references/components.md의 DOM 스니펫으로 렌더assets/design-tokens.js의 color.light → CSS 변수, typography → 자동 생성 CSS 클래스assets/icons.js의 SVG를 [data-icon="name"] 속성으로 런타임 주입, 없는 아이콘은 Figma API로 export 후 ASSETS 객체에 인라인 등록<img class="bg"> 없이 실제 요소 기반 HTML 작성diff.png + report.json 생성세 가지 파일이 스킬 폴더의 assets/에 번들되어 있다. 항상 이 경로에서 로드 — 외부 Downloads 경로 참조 금지.
assets/design-tokens.js — window.TOKENS.color.light (33 semantic), .typography (64 styles), .primitive (대부분 undefined, 사용 X)assets/icons.js — window.ICONS[name] 184개 SVG (snake_case key)assets/ods-components-index.json — 43개 ODS 컴포넌트 스펙 (node_id + variant properties + variants 배열)HTML 로드 패턴:
<script src="{스킬경로}/assets/design-tokens.js"></script>
<script src="{스킬경로}/assets/icons.js"></script>
빌드 결과물 폴더로 복사해서 상대 경로로 로드하는 게 일반적. 절대 경로 그대로 쓰면 file:// fetch 이슈 없음.
pixel-diff 도구(checklist.js/diff.js/diff-flat.js/inspect.js/hub.js)는 puppeteer·pixelmatch·pngjs가 필요하다. 어떤 pixel-diff 스크립트를 돌리기 전에 node_modules가 없으면 먼저 설치한다 (있으면 건너뜀):
PD="${CLAUDE_PLUGIN_ROOT}/skills/new-ods-ui-builder/pixel-diff"
[ -d "$PD/node_modules" ] || (cd "$PD" && npm install)
puppeteer Chromium(~200MB)은
~/.cache/puppeteer에 1회만 받는다(이후 사용자 전체 공유). 플러그인 버전이 올라가면 캐시 경로가 바뀌므로 위 가드가 새 버전에서 다시 설치를 수행한다.
references/components.md 참고<img class="bg" src="screens/....png"> 패턴 절대 사용 X#141414 직접 쓰지 말고 window.TOKENS.color.light['FG/Neutral/foreground'].value → CSS 변수window.ICONS[name]를 써야 함. icons.js에 없는 경우만 Figma API에서 exportreferences/components.md의 권장 DOM을 따른다. 색·간격을 마음대로 바꾸지 않음 (디자이너 override는 따로 처리)https://figma.com/design/{fileKey}/{fileName}?node-id={nodeId}
→ fileKey = {fileKey}
→ nodeId = {nodeId} (하이픈은 콜론으로 변환: "1512-38967" → "1512:38967")
const url = `https://api.figma.com/v1/files/${fileKey}/nodes?ids=${nodeId}&depth=20&geometry=paths`;
// X-Figma-Token 헤더로 Personal Access Token 전송
각 노드에서 추출:
absoluteBoundingBox → 스크린 기준 상대 좌표 (x - screen.x, y - screen.y)fills[0].color → 16진수 hex 변환style.fontFamily, fontWeight, fontSize, lineHeightPxcharacters (텍스트 노드)componentId / componentSetId (인스턴스) → assets/ods-components-index.json 매칭매칭은 pixel-diff/match-ods.js 의 matchOdsComponent(node, odsIndex) 로 수행한다. 3단계: componentId 직접 → variant node_id → 이름 폴백.
const { matchOdsComponent, collectMatches } = require('./pixel-diff/match-ods.js');
// 또는 트리 전체를 한 번에:
// node pixel-diff/match-ods.js --node={workdir}/node.json --root={nodeId}
// → JSON [{ id, name, ods, via:'componentId'|'variant'|'name' }]
왜 이름 폴백이 필수인가 (드라이런 발견): ods-components-index.json 의 componentId 는 ODS 마스터 라이브러리 파일의 node_id 다. 실제 화면/프로토타입 파일의 인스턴스는 자기 파일 로컬 component id 를 가지므로 componentId 만으로는 매칭 실패한다. 인스턴스 이름(🌀 Tab 등)은 마스터 컴포넌트 이름을 따르므로 이름(🌀·이모지 정리 후 대소문자 무시)으로는 안정적으로 매칭된다.
매칭되면 references/components.md에서 해당 컴포넌트의 DOM 스니펫·CSS 클래스 네이밍 적용.
pixel-diff와 inspect.js는 픽셀 통계만 봄 → "요소 자체가 빠짐"은 절대 못 잡음 (예: 시트 핸들 바 32×4 누락 = 0.01%만 기여, 통계에 묻힘). 빌드 시작 전에 반드시 핵심 요소 체크리스트를 자동 생성:
node ${CLAUDE_PLUGIN_ROOT}/skills/new-ods-ui-builder/pixel-diff/checklist.js \
--node={workdir}/node.json \
--root={nodeId} \
--out={workdir}/checklist.md
산출물 checklist.md는 다음 카테고리로 정리됨:
→ 이 파일 옆에 두고 빌드하면 누락 거의 0. 끝나면 - [ ] → - [x]로 체크하면서 한 번 더 훑기.
references/example-home.html 의 패턴을 기본으로:
assets/design-tokens.js, assets/icons.js 로드applyTokens() → CSS 변수, applyTypography() → .text-* 클래스, applyIcons() → SVG 주입<div class="screen" data-id="...">checklist.md 항목을 1:1로 구현. 카테고리 순서대로 진행하면 빠뜨릴 확률이 크게 줄어듦.
필수 컨벤션: <title>과 헤더 주석에 Figma 프레임명·node-id·URL 자동 포함 (식별·추적용):
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>{Figma 프레임명 그대로}</title>
<!--
Figma frame : {프레임명}
Node ID : {nodeId}
File : {fileKey}
Figma URL : https://www.figma.com/design/{fileKey}?node-id={nodeId-with-dash}
Built : {YYYY-MM-DD}
-->
프레임명은 node.json → nodes[rootId].document.name 에서 그대로 가져옴 (한글/공백 보존). 브라우저 탭에서 식별 + 시간 지나도 출처 추적 가능.
상세 패턴은 references/tokens-usage.md 참조.
기본: diff-flat.js 사용 권장 (diff.js를 한번 돌린 뒤 figma의 투명 영역을 흰색으로 평탄화하고 재계산. Figma export는 frame bbox 밖을 alpha=0으로 두는데, HTML body는 보통 흰색이라 그게 가짜 diff로 잡힘.):
node ${CLAUDE_PLUGIN_ROOT}/skills/new-ods-ui-builder/pixel-diff/diff-flat.js \
--file={fileKey} \
--node={nodeId} \
--html={출력 HTML 경로} \
--token={Figma 토큰} \
--viewport=375x812 \
--scale=2 \
--threshold=0.2 \
--out={workdir}/diff-{화면명}
산출물: figma.png, actual.png, diff.png, figma-flat.png, actual-flat.png, diff-flat.png, report.json, report-flat.json.
판정은 report-flat.json의 diffRatio 기준. fullPage: true 화면(긴 스크롤)은 --fullPage=true 추가.
pixel-diff만으론 부족함 — 픽셀 평균은 모서리 둥글기 누락(차이 ~0.01%)·요소 시프트(겹치는 픽셀이 같은 흰색이라 차이 0)·작은 모양 변화(60×30 영역이 전체 168만 픽셀의 0.1%) 같은 구조적 이슈를 묻어버린다. inspect.js를 반드시 함께 돌려 시각화 + 셀별 분석을 받는다:
node ${CLAUDE_PLUGIN_ROOT}/skills/new-ods-ui-builder/pixel-diff/inspect.js \
--out={workdir}/diff-{화면명} \
--cell=40 \
--topN=15
산출물 3종:
overlay-stripe.png — figma와 actual을 20px 가로 줄무늬로 교차 합성. 요소 위치/크기 차이가 줄무늬 경계에서 단절로 보임.region-grid.png — 화면을 40×40 render px(=20×20 design px) 그리드로 나눠 셀별 diff%를 빨간 강도로 칠함. 국소적 문제 영역을 한눈에.region-report.json — diff% 상위 N개 셀의 디자인 좌표 + diff% 리스트. "이 좌표를 보세요" 가이드.읽는 법: region-report.json의 worst 셀을 좌표로 짚어 → overlay-stripe.png에서 줄무늬 단절 확인 → 해당 영역 figma 노드 실측과 비교 → 수정.
report-flat.json의 diffRatio 확인 (flat 값이 진짜 시각 차이)region-report.json의 worst 셀 좌표 ≤ 5%가 모두면 + diffRatio ≤ 3%: 합격, 보고region-report.json에서 worst 셀 좌표 추출 → Figma 노드 데이터에서 해당 좌표의 요소 찾기 → bbox/색/모양 실측 비교 → 수정overlay-stripe.png에서 줄무늬 단절을 시각적으로 추가 확인🚨 이 단계는 옵션입니다. 사용자가 명시적으로 "여러 화면 같이 만들어줘" / "허브 페이지 만들어줘" / "한 번에 N개 빌드" 같은 요청을 한 경우에만 사용. 단일 화면 빌드에는 적용하지 말 것.
여러 노드를 한 번에 빌드하고 카드 그리드 인덱스 페이지로 묶을 때 사용 (시퀀스 화면: 인트로 1→2→3, 온보딩 step1~step5 등).
표준 패턴:
# 1. 데이터 병렬 다운로드 (한 API 호출에 여러 노드 ID 함께)
curl -s -H "X-Figma-Token: $TOKEN" \
"https://api.figma.com/v1/files/$FILE/nodes?ids=NODE1,NODE2,NODE3&depth=20" -o /tmp/nodes.json
# 2. 토큰 만료 검증 (필수)
if head -c 60 /tmp/nodes.json | grep -q "Token expired"; then
echo "ERROR: Figma 토큰 만료. 새 토큰 받아서 재실행"; exit 1
fi
# 3. 각 노드별로 Step 3-1 ~ Step 6 진행 (병렬 또는 순차)
# 4. 모든 빌드 완료 후, hub 인덱스 페이지 생성
node ${CLAUDE_PLUGIN_ROOT}/skills/new-ods-ui-builder/pixel-diff/hub.js \
--builds=build-NODE1,build-NODE2,build-NODE3 \
--title="{시퀀스 이름}" \
--out={hub 폴더명}/
hub.js가 자동으로:
<title> (프레임명) 추출Node ID + Figma URL 파싱figma-render.png 썸네일 사용diff-*/report-flat.json 의 diff% → 색상 배지 (≤3% 초록, ≤7% 주황, >7% 빨강)산출물: {out}/index.html (카드 그리드) + {out}/{buildName}/ (각 빌드 복사본). Vercel에 통째로 deploy하면 한 URL에서 모든 화면 접근 가능.
🚨 이 단계는 옵션입니다. 사용자가 명시적으로 플로우 정보를 줬을 때만 실행 (이미지에 박스+화살표 표시 또는 텍스트 "1→2→3"). 사용자가 언급 안 하면 절대 자동 wire-up 하지 말 것 — 단일 빌드 / 독립 화면으로 두기.
여러 화면 빌드 후, 사용자가 플로우(화면 → 화면 이동)를 알려준 경우 버튼을 wire-up해서 클릭하면 다음 화면으로 가게 만든다.
사용자 입력 형식:
표준 wire-up 패턴 — <button>을 <a> 태그로 교체:
<!-- BEFORE -->
<button class="btn-start">시작하기</button>
<!-- AFTER -->
<a class="btn-start"
href="../build-{nextNodeId}/index.html"
style="text-decoration:none;display:inline-flex;align-items:center;justify-content:center">시작하기</a>
href는 hub 폴더 기준 상대 경로 (../build-XXX/index.html)style은 인라인으로 text-decoration:none + flex align만 추가 (기존 .btn-start 클래스 스타일은 보존)../index.html) / 외부 URL / 또는 클릭 안 되게 그대로 button 유지적용 후 hub sync 필수: 원본 빌드 폴더를 수정한 뒤 hub 폴더 내부 복사본도 같이 갱신
for build in build-A build-B build-C; do
cp /path/to/$build/index.html /path/to/hub/$build/index.html
done
이렇게 만들면 사용자가 hub의 1번 카드 → 인터랙티브하게 1→2→3→hub 순회 가능. Figma prototype 정식 연결과 동일한 사용감.
✅ Build 완료 — {출력 경로}
검증 결과:
| 화면 | diff % | 합격 |
|------|--------|------|
| home | 2.1% | ✓ |
사용한 자산:
- 토큰 키 N개
- 아이콘 M개
- ODS 컴포넌트 K개 (이름 + variant)
불가피 차이: {폰트 렌더링 차이 / OS 키보드 / 상태바 시스템 아이콘 등}
references/components.md — 43개 ODS 컴포넌트 전수 사용법 (Figma 인스턴스 매칭 시 첫 참조)references/tokens-usage.md — TOKENS/ICONS 상세 사용 패턴references/gotchas.md — 자주 실수하는 포인트 (매 작업 시작 시 점검)references/example-home.html — 실제로 빌드된 홈 화면 예시pixel-diff/README.md — diff 도구 파라미터pixel-diff/checklist.js — 빌드 전 필수. Figma node.json → 핵심 요소 마크다운 체크리스트 자동 생성pixel-diff/diff.js — 기본 diff (raw figma↔actual)pixel-diff/diff-flat.js — 권장. diff.js + alpha 평탄화 후 재계산pixel-diff/inspect.js — 구조적 inspect (stripe overlay + region grid + worst-cell report)pixel-diff/hub.js — 여러 빌드를 카드 그리드 인덱스 페이지로 묶음 (배치/시퀀스 빌드용)--fullPage=true.primitive는 피하고 color.light(semantic)만 사용.assets/의 세 파일을 재추출해서 교체. references/components.md도 같이 업데이트 필요.Provides a checklist for code reviews covering functionality, security, performance, maintainability, tests, and quality. Use for pull requests, audits, team standards, and developer training.
npx claudepluginhub ohouse-product-design/ohouse-design --plugin ohouse-design-plugin