$npx -y skills add kwakseongjae/oh-my-design --skill omd-experiment-galleryN개 brand experiment를 한 화면에서 비교하는 gallery index.html을 생성. 각 카드는 brand name, wow rating, multi-turn refinement deltas, iframe scaled preview, standalone link 포함. '결과물 한 번에 보여줘', '갤러리 만들어', '5개 비교 뷰', 'experiment 결과 정리' 류 트리거. omd:harness가 N개 brand batch 작업 끝낸 직후 자동 호출되거나 사용자가 명시적으로
| 1 | <!-- omd:installed-skill — managed by `omd install-skills`. Do not edit; rerun the command to refresh. --> |
| 2 | |
| 3 | |
| 4 | # omd:experiment-gallery — N-brand comparison index |
| 5 | |
| 6 | ## 왜 이 스킬 |
| 7 | |
| 8 | 여러 brand experiment를 돌리고 나면 결과물 검수가 산만 — 각 폴더 따로 열고 비교하기 번거로움. 이 skill은 **단일 `index.html`** 에 N개 결과를 iframe으로 박아서 한 화면에서 wow ratings + deltas + 실제 렌더를 동시 확인. |
| 9 | |
| 10 | ## 트리거 |
| 11 | |
| 12 | - 명시: "결과물 한 번에 보여줘", "갤러리 만들어", "N개 비교 뷰" |
| 13 | - 묵시: 사용자가 omd:harness로 ≥2개 brand experiment를 한 batch에서 끝낸 직후 ("이제 비교해보자" 라는 흐름) |
| 14 | - 다른 skill 안에서 호출: omd:batch-launch가 5개 brand 끝낸 후 Phase 5로 호출 |
| 15 | |
| 16 | ## 입력 |
| 17 | |
| 18 | 다음 둘 중 하나: |
| 19 | |
| 20 | 1. **명시 디렉토리 list**: 사용자가 `/tmp/omd-gallery/{brand1,brand2,brand3}` 같은 list 제공 |
| 21 | 2. **부모 디렉토리 + glob**: `/tmp/omd-gallery/*` 또는 `web/experiments/2026-05/*` 등 — skill이 자동 enumerate |
| 22 | |
| 23 | 각 brand 디렉토리는 다음 파일들 있어야 함 (있으면 사용, 없으면 fallback): |
| 24 | - `landing.html` (필수) — iframe src |
| 25 | - `DESIGN.md` (선택) — brand display name 추출 |
| 26 | - `assets/_reference/<id>/structure.json` (선택) — composition 메타 |
| 27 | - `assets/_reference/<id>/.live-inspect-proof.json` (선택) — live tag 표시 |
| 28 | - `screenshots/after.png` (선택) — fallback preview |
| 29 | - `experiment-meta.json` (선택, 권장) — wow rating + round-2 deltas + 카테고리 — sub-agent가 작성 |
| 30 | |
| 31 | ## experiment-meta.json 스키마 (sub-agent가 작성) |
| 32 | |
| 33 | 각 experiment sub-agent는 work 종료 직전 다음 파일을 작성: |
| 34 | |
| 35 | ```json |
| 36 | { |
| 37 | "brand_id": "toss", |
| 38 | "brand_korean": "토스", |
| 39 | "brand_category": "Fintech", |
| 40 | "brand_color_hex": "#3182f6", |
| 41 | "wow_rating": 7.5, |
| 42 | "lines": 603, |
| 43 | "live_inspect": { "ran": true, "raw_samples": 7, "method": "playwright|harness" }, |
| 44 | "round2_deltas": [ |
| 45 | "Reveal failsafe — 2s forwards animation", |
| 46 | "Stat-card narrative upgrade", |
| 47 | "CTA copy 구체화 (시간 약속)", |
| 48 | "Hero shimmer (accent text gradient)", |
| 49 | "Dark marquee band rhythm break", |
| 50 | "Chart stroke-dashoffset draw-in" |
| 51 | ], |
| 52 | "ip_compliance": { |
| 53 | "your_logo_placeholders": 2, |
| 54 | "your_product_name_placeholders": 4, |
| 55 | "brand_logo_embed_count": 0, |
| 56 | "verbatim_brand_copy": 0 |
| 57 | }, |
| 58 | "honest_gaps": [ |
| 59 | "Hero composition은 single character + flat card (3D ornament 부재)", |
| 60 | "Carousel은 dot만 회전 (slide content 미스왑)" |
| 61 | ] |
| 62 | } |
| 63 | ``` |
| 64 | |
| 65 | skill이 이 파일을 읽어 gallery 카드의 wow / lines / deltas 영역 채움. 없으면 brand_id만으로 minimal 카드. |
| 66 | |
| 67 | ## 출력 |
| 68 | |
| 69 | ### 1. `<output-dir>/index.html` (메인 산출물) |
| 70 | |
| 71 | 다음 구조: |
| 72 | |
| 73 | - 헤더: title (사용자가 batch 명령에서 받은 raw — "한국 brand 5선" 등), kicker (date · brand 수), meta (version · skill chain · mode) |
| 74 | - main grid: brand 1개당 card 1개 |
| 75 | - card head: brand mark (CSS-generated initial chip 또는 DiceBear shapes if brand has known color) + 한글명 + id + wow rating |
| 76 | - frame-wrap: aspect-ratio 16:10 iframe (scale 50%로 200%×200% iframe rendering — 모바일은 100%) |
| 77 | - card foot: voice note 1줄 + tags chips + round-2 deltas ol |
| 78 | - footer summary: 모든 brand wow/lines/proof/round-2 핵심 1줄 테이블 |
| 79 | - system-note: 적용된 skill 버전의 시스템 fix들 한 단락 |
| 80 | |
| 81 | ### 2. CSS 스타일 가이드 |
| 82 | |
| 83 | - typography: Pretendard from jsdelivr (SIL OFL). 디스플레이는 본문 헤더에만 (`clamp(36px,4.4vw,56px) / weight 800 / letter-spacing -0.04em`) |
| 84 | - 다크 surface (`#0b0d10`) — gallery는 dark mode가 정석 (검토 환경 자체가 광원이 되도록) |
| 85 | - accent: gradient `linear-gradient(135deg, #7c5cfc 0%, #fa2e5f 50%, #04c584 100%)` — Stripe-like |
| 86 | - card: `bg #13161b`, border `#252b35`, hover `border #7c5cfc` + `translateY -2px` |
| 87 | - 반응형: ≥1100px 2-col grid, <1100px 1-col |
| 88 | |
| 89 | ### 3. iframe scaling |
| 90 | |
| 91 | ```css |
| 92 | .frame-wrap { aspect-ratio: 16/10; background: #fff; overflow: hidden; position: relative; } |
| 93 | .frame-wrap iframe { |
| 94 | position: absolute; top: 0; left: 0; |
| 95 | width: 200%; height: 200%; |
| 96 | transform: scale(.5); transform-origin: top left; |
| 97 | border: 0; |
| 98 | } |
| 99 | .open-link { |
| 100 | position: absolute; top: 12px; right: 12px; |
| 101 | background: rgba(11,13,16,.7); color: #fff; |
| 102 | padding: 6px 12px; border-radius: 999px; |
| 103 | font-size: 11px; backdrop-filter: blur(8px); |
| 104 | } |
| 105 | ``` |
| 106 | |
| 107 | 200% × scale(.5)는 모바일 viewport에서도 데스크탑 layout 그대로 렌더하기 위함 (iframe 본인은 1280-1920px 가정). |
| 108 | |
| 109 | ### 4. 정적 vs 서버 모드 |
| 110 | |
| 111 | - iframe src는 **상대 경로** (`./toss/landing.html`) — 정적 file:// open 안 됨 (CORS), `python3 -m http.server <port>` 추천 |
| 112 | - skill 끝에 사용자에게 "서버 띄우는 명령" 출력: |
| 113 | ``` |
| 114 | cd <output-dir> && python3 -m http.server 8770 |
| 115 | open http://localhost:8770/index.html |
| 116 | ``` |
| 117 | |
| 118 | ## 실행 절차 |
| 119 | |
| 120 | 1. **입력 검증**: 디렉토리 list parsed, 각 디렉토리에 `landing.html` 존재 확인. 없으면 그 brand는 skip + 사용자에게 알림. |
| 121 | |
| 122 | 2. **meta 수집**: 각 brand 디렉토리에서: |
| 123 | - `experiment-meta.json` 있으면 그대로 사용 |
| 124 | - 없으면 `DESIGN.md` frontmatter에서 `brand` 추출, `landing.html`에서 `<title>` 추출 → minimal card |
| 125 | - `structure.json`이 있으면 hero.type / cta.dominant_shape 정도를 tag로 추가 |
| 126 | - `.live-inspect-proof.json` 존재 → "live ✓" tag, raw_samples 수 표시 |
| 127 | |
| 128 | 3. **brand mark 생성**: brand_color_hex가 있으면 색칩 div에 첫 글자. 없으면 회색 fallback. |
| 129 | |
| 130 | 4. **gallery HTML 작성**: 위 CSS + 카드 구조로 출력. `<output-dir>/index.html`에 write. |
| 131 | |
| 132 | 5. **사용자 요약**: |
| 133 | ``` |
| 134 | ✓ Gallery 생성: <output-dir>/index.html |
| 135 | - 카드 N개 · wow 평균 X.X · live-inspect 통과 Y/N |
| 136 | - 서버 띄우는 법: |