$npx -y skills add kwakseongjae/oh-my-design --skill omd-feel디자인·프론트 업계가 '감'으로 쓰던 인터페이스 디테일을 수치화한 규칙으로 적용(APPLY)하거나 감사(AUDIT)한다. Jakub Krehel의 make-interfaces-feel-better 철학을 계승 + HIG/Material/WCAG/DS 토큰/실무자 리서치로 확장한 17축·113규칙(provenance 등급별). 모션 타이밍·이징·동심원 radius·tabular-nums·44px 타깃·focus ring·prefers-reduced-motion 등. 'feel 좋게 다듬어줘', '인
| 1 | <!-- omd:installed-skill — managed by `omd install-skills`. Do not edit; rerun the command to refresh. --> |
| 2 | |
| 3 | |
| 4 | # omd:feel — Interface Feel, Quantified |
| 5 | |
| 6 | 업계가 **감(感)으로 쓰던** 인터페이스 디테일 — "이 정도 속도", "이 정도 그림자", "이 정도 여백" — 을 **출처 등급이 매겨진 체크 가능한 숫자**로 바꿔 적용하고 감사하는 스킬. Jakub Krehel의 *"great interfaces are a collection of small things that compound"* 명제를 계승하되, **어떤 숫자를 맞춰야 하는지 / 언제 틀렸는지**까지 수치로 답한다. |
| 7 | |
| 8 | 핵심 통찰: **품질은 수십 개 작은 제약을 각자의 band 안에 가둔 것의 적분**이다. 600ms짜리 체크박스 틱, 1px focus ring, 본문 2.85:1 대비, `@media (hover:hover)` 게이트 없는 hover lift — 하나하나가 "feel"에서 측정 가능한 차감이다. 이 스킬은 그 차감을 막는다. |
| 9 | |
| 10 | 전체 규칙(17축·113개)은 [`reference.md`](./reference.md)에, 각 숫자의 출처·tier·방법론은 [`provenance.md`](./provenance.md)에 있다. 아래 **cheat-sheet**는 항상 로드되는 핵심이고, 깊은 감사·엣지 축은 reference를 읽는다. |
| 11 | |
| 12 | ## 언제 쓰나 |
| 13 | |
| 14 | - **APPLY** — UI를 새로 만들거나 고칠 때 정량 feel 규칙을 주입. ("feel 좋게", "인터랙션 손봐줘", "디테일 적용") |
| 15 | - **AUDIT** — 기존 HTML/JSX/TSX/CSS를 규칙표 대비 점검해 feel-score + BLOCK/WARN/FYI 리포트. ("feel 점검", "이 화면 감사해줘") |
| 16 | |
| 17 | UI 작업이 아니면 트리거하지 않는다. 카피·정보구조만 다루는 작업은 대상 아님. |
| 18 | |
| 19 | ## 0. 출처 등급 → 강제 강도 (이 스킬의 심장) |
| 20 | |
| 21 | 모든 규칙은 tier를 달고 있고, **tier가 강제 강도를 정한다.** 이게 "감을 정직하게 수치화"하는 방법 — spec 숫자는 게이트, 취향 숫자는 제안. |
| 22 | |
| 23 | | Badge | Tier | APPLY | AUDIT severity | |
| 24 | |---|---|---|---| |
| 25 | | 🟢 **SPEC** | spec-authoritative (W3C/WCAG·HIG·normative) | 강제 | **BLOCK** | |
| 26 | | 🟢 **DS** | ds-token (커밋된 디자인시스템 토큰) | 강제 (브랜드가 덮지 않으면) | **BLOCK** | |
| 27 | | 🟡 **CONV** | industry-convention (독립 실무자 ≥2 수렴) | 기본값으로 적용 | **WARN** | |
| 28 | | ⚪ **OP** | single-opinion (신뢰할 만한 1인) | 제안만 | **FYI** | |
| 29 | | ⚪ **FOLK** | folklore-unverified (근거 추적 불가) | 제안 + tier 명시 | **FYI** | |
| 30 | |
| 31 | 분포: SPEC 61 · DS 24 · CONV 19 · OP 7 · FOLK 2 → **85/113이 spec·token 근거(강제 가능)**. |
| 32 | |
| 33 | > **DESIGN.md가 항상 이긴다.** 브랜드 `DESIGN.md` 토큰이 규칙과 충돌하면 브랜드 토큰이 권위. 이 규칙들은 브랜드가 **침묵할 때의 기본값**이지 override가 아니다. 프로젝트 루트에 DESIGN.md가 있으면 `omd:apply`로 먼저 토큰을 읽고, 빈 축만 feel 규칙으로 채운다. |
| 34 | |
| 35 | ## 1. Cheat-sheet — 항상 적용하는 핵심 숫자 |
| 36 | |
| 37 | > tier 표기: 🟢=강제, 🟡=기본값, ⚪=제안. 충돌 시 DESIGN.md > 🟢 > 🟡 > ⚪. |
| 38 | |
| 39 | ### Motion & Timing |
| 40 | - **일상 UI 전환 ~200ms** (hover ≤150 · popover ~200 · overlay/modal ~300). 범위 150–300ms, **>400ms = 굼떠 보임**. 🟡 |
| 41 | - duration은 **50ms 그리드**에 양자화 (220/333ms 금지). 🟢 |
| 42 | - **exit ≈ 0.8 × enter** (열 때보다 닫을 때 빠르게). 🟢 |
| 43 | - 한 개의 전역 duration 금지 — 이동 면적/거리에 비례. 🟢 |
| 44 | |
| 45 | ### Easing & Springs |
| 46 | - **enter/사용자 트리거 = ease-out** `cubic-bezier(0,0,0.2,1)` · **exit = ease-in/accelerate** · 이미 보이는 morph = ease-in-out · 스피너/progress = linear. **enter에 ease-in 금지.** 🟡 |
| 47 | - Apple = spring 기본 `(response 0.55, damping 0.825, bounce 0)`, 기능적 UI는 bounce <0.4. 🟢 |
| 48 | - **interruptible**: 인터랙티브 상태는 transition/spring(재타깃 가능), one-shot 연출만 `@keyframes`. 🟢 |
| 49 | |
| 50 | ### Spacing · Radius · Rhythm |
| 51 | - **8px base, 4px half-step** (4·8·12·16·24·32·40·48·64). 🟢 |
| 52 | - **내부 padding ≤ 형제 간 외부 gap.** 🟡 |
| 53 | - 동심원 radius: **outer = inner + padding.** 🟢 |
| 54 | - UI 타입 스케일 ~**1.25**(major third). 🟡 |
| 55 | |
| 56 | ### Shadow & Depth |
| 57 | - **레이어드 그림자 3–5겹**, 광원은 위 하나. 저고도=tight+sharp, 고고도=diffuse+spread (Tailwind md→lg 램프). 🟡/DS |
| 58 | - **다크모드는 그림자보다 밝은 표면**으로 깊이를 낸다. 🟡 |
| 59 | |
| 60 | ### Typography |
| 61 | - 본문 **line-height 1.5**, **measure ~66ch** (45–75). 🟢 |
| 62 | - display tracking **−0.02~−0.03em**, all-caps **+0.05~0.1em**, 본문 ~0. 🟡 |
| 63 | - 변하는 숫자엔 **tabular-nums**; 제목 **text-wrap: balance**, 본문 **pretty**; 레이아웃에 **antialiased**. (seed/jakub) 🟡 |
| 64 | |
| 65 | ### Color & Contrast |
| 66 | - 텍스트 **4.5:1**(large 3:1) AA · 비텍스트/UI 경계 **3:1** · focus ring **3:1 + 2px + ~2px offset**. 🟢 |
| 67 | - M3 state layer: **hover 8% · focus 10% · pressed 10% · dragged 16%.** 🟢 |
| 68 | - disabled ~**38% opacity**. 🟡 |
| 69 | |
| 70 | ### States & Feedback |
| 71 | - 데이터 컴포넌트는 **5 상태**(empty/loading/error/content/skeleton) 전부 정의. 🟡 |
| 72 | - pressed: **scale 0.96–0.97, 150ms ease-out** (바닥 0.95). DS/⚪ |
| 73 | - 아이콘 상태 스왑: scale+opacity+blur 4px→0, spring ~0.3. 🟡 |
| 74 | |
| 75 | ### Targets & Pointer |
| 76 | - 탭 타깃 **44pt(iOS/WCAG AAA) / 48dp(Material) / 24px(WCAG AA 최소)**, 인접 타깃 간 **≥8pt**. 🟢 |
| 77 | - **cursor**: 인터랙티브=pointer · 비활성=not-allowed · 드래그=grab→grabbing · 편집=text. 🟡 |
| 78 | |
| 79 | ### Scroll & Gesture |
| 80 | - 오버레이/중첩 스크롤러에 **`overscroll-behavior: contain`** (스크롤 체이닝·PTR 차단). 🟢 |
| 81 | - 스와이프 해제 임계 **~30% 거리 또는 velocity flick**. DS |
| 82 | - scroll-snap: 롱폼=proximity · 캐러셀=mandatory. 🟢 |
| 83 | |
| 84 | ### Response Time & Perceived Perf |
| 85 | - **100ms = 즉각 · 1s = 사고 흐름 한계 · 10s = 주의 한계(% progress 표시).** Doherty **400ms** ceiling. 🟢 |
| 86 | - **INP ≤200ms p75, 프레임 예산 16.6ms.** 🟢 |
| 87 | - 스피너는 예상 대기 **>1s일 때만**; 띄웠으면 **최소 500ms 유지**(깜빡임 금지). 레이아웃을 알면 skeleton. 🟡/DS |
| 88 | |
| 89 | ### Layout Stability (CLS) |
| 90 | - **CLS ≤0.1 p75.** 미디어는 width/height 또는 **aspect-ratio**로 자리 예약. **`scrollbar-gutter: stable`**. `font-display: optional`(또는 swap)+metric override. 입력 500ms 창 밖에서 콘텐츠 위쪽 삽입 금지. 🟢 |
| 91 | |
| 92 | ### Modals · Overlays · Z-index |
| 93 | - `role=dialog` + **`aria-modal=true`**, 열 |