suta · git:20261106.252c040 · 2026-11-06 · sha256 412f64c82adea607
suta git:20261106.252c040A
Immutable. This exact content never changes and is served at /api/v1/blob/412f64c82adea607.
---
name: suta
description: 웹·React Native 앱의 UI를 만들거나 고칠 때 AI 슬롭(AI slop) — 모든 것을 감싼 카드와 제각각인 여백, 기본 파랑·연노랑을 뿌린 탁한 색, 아무 데나 붙인 transition, 레이아웃을 흔드는 애니메이션, 동작 줄이기 설정 무시, 키보드로 못 쓰는 컴포넌트처럼 AI가 만든 티가 나는 UI — 을 없앤다. 이를 막는 레이아웃·색·글꼴·문구·모션 기준과 검증된 UI 패턴 57종의 코드(모달·바텀시트·토스트·드롭다운·캐러셀·탭·폼·제스처)를 제공한다. "로그인·목록·상세·설정·채팅 화면 만들어줘, 페이지 구현, 컴포넌트 추가, 레이아웃·여백 다듬어줘, 버튼 애니메이션 넣어줘, 문구 다듬어줘, 모바일 제스처, AI 티 안 나게" 요청과 UI, landing page, screen, layout, spacing, component, modal, dropdown, toast, animation, keyboard accessibility 작업에 쓴다. 화면이 없는 백엔드·데이터·문서 작업에는 쓰지 않는다.
---
# suta — AI 슬롭 없는 UI
AI에게 UI를 맡기면 어디서 본 듯한 결과가 나온다. 모든 구역을 카드로 감싸고, 프레임워크 기본 파랑을 쓰고, `div`에 `onClick`을 달아 키보드로 쓸 수 없게 만들고, `height`를 움직여 화면을 흔들고, 좁은 화면에서 가로로 넘친다. 이 스킬은 세 가지로 그것을 막는다.
- **기준** — 키보드·포커스·넘침·동작 줄이기 같은 사용 문제의 선(이 문서), 디자인 기준이 없는 프로젝트에서 쓰는 레이아웃·색·글꼴·문구 기본값(`references/defaults.md`).
- **패턴 코드 57종** — 모달·바텀시트·토스트·드롭다운·폼·제스처. 프레임워크와 무관한 코어(.ts, 의존성 0) + React 래퍼 + CSS를 프로젝트에 복사해 맞춘다.
- **검사** — `scripts/audit.mjs`가 소스에서 레이아웃·모션 문제를 file:line으로 찾는다. 플러그인으로 설치하면 파일을 고칠 때마다 돈다.
수타(手打)는 기계가 아니라 손으로 친다는 뜻이다.
## 우선순위
판단이 부딪히면 위에 있는 것을 따른다.
1. **사용자가 명시한 요구.** 요청한 화면·기능·상태(오류·빈 상태·로딩·비활성 등)를 하나도 빼지 않는다. 요청하지 않은 기능·구역·메뉴 항목·데이터는 더하지 않는다. 더하면 좋을 것은 보고에 제안으로 적는다.
2. **프로젝트의 기존 디자인 체계.** 토큰(색·간격·글자·반경)·컴포넌트·브랜드색·글꼴·이미 있는 화면의 구조를 그대로 쓴다. 카드를 쓸지, 색을 몇 개 쓸지, 설정 목록을 어떤 모양으로 둘지, 강조색이 얼마나 맑은지, 어떤 말투인지는 취향이다. suta 기본값으로 바꾸지 않는다.
3. **실제 사용 문제.** 아래 "사용 문제" 표에 해당하면 기존 스타일이라도 고친다. 무엇이 문제인지(측정값)와 어디까지 바꿨는지를 보고하고, 그 범위 밖은 건드리지 않는다. 예: 브랜드 회색 글자가 흰 면에서 3.9:1이면 그 글자 색 하나만 4.5:1이 되게 어둡게 하고 팔레트의 나머지는 그대로 둔다. 명시한 요구가 사용 문제를 만들면 요구를 따르고, 문제와 대안을 보고에 적는다.
4. **suta 기본값.** 디자인 기준이 없는 프로젝트(새 프로젝트, 토큰·컴포넌트·브랜드 가이드가 없는 화면)에서만 `references/defaults.md`의 값을 쓴다. 사용자가 "AI 티를 없애 달라"고 하면 요청한 화면에 적용하되, 브랜드색·글꼴·컴포넌트는 유지하고 바꾸고 싶은 것은 제안한다.
### 사용 문제와 취향
| 사용 문제 — 기존 디자인이어도 고친다 | 취향·관례 — 기준이 없을 때만 suta 기본값 |
|---|---|
| 키보드로 닿지 않는 컨트롤, 보이지 않는 포커스, 열고 닫을 때 포커스 이동·복귀·Esc가 없는 모달·시트·메뉴 | 카드·상자를 쓸지, 몇 겹인지 |
| 글자 대비 4.5:1 미만, 12px 미만 글자, 작은 누르는 영역(모바일 44px, 최소 24px) | 강조색과 그 맑기, 유채색 수, 옅은 색 면, 무채색의 온도 |
| 320·390px 폭의 가로 넘침, 어느 폭에서든 칸 밖으로 잘리는 글자·숫자, 좁은 화면에서 사라지는 표의 열, 내용을 가리는 하단 고정 바, 모바일에서 아래가 잘리는 `100vh`, 확대되는 16px 미만 입력칸 | 글꼴, 말투(해요체·합쇼체), 버튼 문구 길이 |
| 동작 줄이기 설정 무시, 레이아웃 속성(`height`·`top`)을 움직이는 애니메이션, `transition: all`, 장식용 무한 반복 | 설정·랜딩·홈의 구성(폭 가득 목록과 둥근 묶음, 구역 수, 번호 목록) |
| 스크린 리더에 전달되지 않는 상태 변화, 보이는 글자와 다른 접근 이름, 색만으로 전하는 정보 | 간격 척도, 글자 크기·굵기·반경의 종류 수 |
| 확대 막기(`user-scalable=no`), 호버로만 열리는 기능, 연타로 두 번 제출되는 버튼, 늘어난 사진 | 아이콘 모양, 이모지 아이콘, 그라데이션·그림자, 시간·이징 값 |
검사(`scripts/audit.mjs`)도 이 선으로 나뉜다.
- **error는 모두 사용 문제다.** 11px 미만 글자, 그림(`role="img"`) 안의 버튼, 넘치는 grid 틀, 스크롤 칸 밖으로 새는 숨김 글자, 레이아웃 애니메이션, `transition: all`, 동작 줄이기 누락 등이다. 기존 코드에서도 고친다.
- **warn 중 사용 문제**는 레이아웃 검사의 `tiny-text`(11px대)·`accent-contrast`·`viewport-height`·`input-zoom`·`zoom-disabled`·`image-distort`와 모션 검사의 `infinite-loop`(장식용 반복일 때)·`js-interval-anim`·`will-change-global`·`no-reduced-motion`(React Native)이다. 기존 체계에서도 고친다.
- **나머지 warn은 취향·관례다**(`nested-card`·`hue-count`·`tinted-surface`·`muddy-accent`·`spacing-off-scale`·`emoji-icon`·`numbered-markers`·`duration-range`·`ease-in-exit` 등). 디자인 기준이 없는 화면에서만 고친다. 기존 체계의 값에서 난 것은 그대로 두고 보고에 적는다.
- `meta-copy`(화면에 남은 개발·데모 문구)는 아래 "사실 정보"를 따른다.
## 요청 범위와 사실 정보
- **요청을 목록으로 적고 끝에 대조한다.** 요청한 상태(오류·빈 상태·로딩 등)는 실제 조작으로 그 상태가 되어야 하고, 확인할 수 있어야 한다. 코드에만 있고 그 상태가 될 길이 없으면 빠진 것이다. 요청이 상태를 "보여 달라"고 하면(퍼블리싱·시안) 상태별 화면을 한 벌 더 두거나 주소 매개변수(`?state=error`)로 열리게 한다. 제품 화면 안에 상태 미리보기 버튼은 두지 않는다.
- **화면의 틀은 더하는 요소가 아니다.** 하위 화면(글쓰기·상세·목록의 다음 단계)을 나가는 길(뒤로·닫기)과 화면 제목, 웹 페이지의 사이트 머리(로고 + 주 메뉴)는 요청에 없어도 둔다. 주 메뉴 항목은 요청·프로젝트에 있는 것과 지금 화면이 속한 메뉴만 두고 새 메뉴를 지어내지 않는다. 공유·구독·찜 같은 기능은 틀이 아니다 — 제안한다.
- **관례는 배치에 쓰고, 더하는 근거로 쓰지 않는다.** 화면 유형의 관례에 흔한 요소(설정의 고객센터·약관·버전, 랜딩의 후기·로고 띠, 홈의 검색창)라도 요청에 없으면 더하지 않고 제안한다. 패턴이 있다고 요청에 없는 기능(담기 애니메이션·좋아요)을 더하지 않는다.
- **사실 정보는 지어내지 않는다.** 고객사 로고·사용자 수·후기·성과 수치·수상·인증처럼 서비스의 실적을 말하는 정보다.
| | 실제 서비스(기본으로 이렇게 본다) | 명시적 가상 목업 |
|---|---|---|
| 판단 | 사용자가 시안·목업·데모·포트폴리오라고 하지 않았다 | 사용자가 그렇게 말했거나, 작업 자체가 지어낸 서비스를 보이는 데모다 |
| 고객사 로고·사용자 수·후기·성과 수치·수상 | 사용자가 준 자료만 쓴다. 없으면 그 구역을 만들지 않고, 보고에 필요한 자료를 요청한다 | 지어낼 수 있다. 실존 회사·사람의 이름과 로고는 쓰지 않는다. 그 구역 안에 "예시"임을 화면에 보이게 적는다(README·주석에만 두지 않는다) |
| 요청한 구역의 값(요금·사양·연락처) | 받은 값을 쓴다. 받지 못했으면 값을 데이터 한 곳에 모으고 `확인 필요` 주석을 달고, 보고에 바꿔야 할 값을 나열한다 | 지어낼 수 있다. 화면에 예시 표시를 둔다 |
| 화면을 보이기 위한 콘텐츠(메뉴·상품·사람 이름, 일정, 제품 화면 그림 속 데이터) | 지어낼 수 있다. 데이터는 한 곳에 모아 실제 데이터로 바꾸기 쉽게 두고, 화면의 숫자끼리 맞게 그 데이터에서 계산한다(지표·차트·표의 합계가 같고, 처리 건수가 접수 건수보다 많지 않다) | 같다 |
| 실제 개인정보(대화·git 설정에 보이는 이름·이메일·전화번호), 개발용 계정·비밀번호 | 화면에 두지 않는다 | 화면에 두지 않는다 |
## 작업 절차
1. **요청을 목록으로 적는다.** 화면·기능·상태·문구를 빠짐없이 적는다.
2. **프로젝트를 읽는다.** 프레임워크(React·Vue·Svelte·순수 JS·React Native), TypeScript 여부, 스타일 방식(CSS·CSS Modules·Tailwind·StyleSheet)을 확인한다. 디자인 기준(토큰·컴포넌트·브랜드 가이드·일관된 기존 화면)이 있는지 판단하고, 있으면 그 값과 구조를 출처로 삼는다.
3. **읽을 문서를 고른다**(아래 표).
4. **구조를 정한다.** 요청한 요소에 순위를 매기고 1순위 하나를 정한다. 기준이 없는 새 화면이면 화면 유형의 뼈대에서 시작한다.
5. **패턴을 고른다.** 아래 색인과 `references/catalog.md`에서 요청의 컴포넌트·상호작용에 맞는 패턴을 모두 고르고, 고른 패턴의 `PATTERN.md`를 끝까지 읽는다. 주의사항에 접근성·성능 함정이 있다. 맞는 패턴이 없으면 이 문서의 선만으로 구현한다.
6. **코드를 옮긴다.** PATTERN.md 파일 표의 "복사 대상"을 따른다.
- 첫 줄이 `@shared-core`인 파일은 그 헤더를 지우지 않는다. 같은 코드가 다른 패턴에도 들어 있다는 표시다.
- JavaScript 프로젝트면 타입 표기만 벗긴다. 로직은 그대로다.
- React가 아니면 코어(.ts)와 CSS만 쓰고, 상태 연결은 그 프레임워크의 방식으로 한다.
- 다른 패턴의 코드가 필요하면 import 경로로 엮지 말고 그 파일을 함께 복사한다.
- 패턴의 공개 변수(`--sheet-duration` 같은)는 프로젝트 토큰에 잇는다. 프로젝트에 기준이 없으면 `patterns/layout-principles/assets/layout-tokens.css`와 `patterns/motion-principles/assets/motion-tokens.css`를 함께 넣는다.
7. **검사한다.** `node <이 스킬 폴더>/scripts/audit.mjs <바꾼 파일·폴더>`를 실행해 error가 0이 될 때까지 고친다. warn은 위 우선순위대로 판단한다. Node 22.18 이상이 필요하다. 실행할 수 없으면 `patterns/layout-audit/PATTERN.md`·`patterns/motion-audit/PATTERN.md`의 규칙표로 직접 확인한다.
- 플러그인으로 설치했으면 파일 편집 도구(Write·Edit)로 고칠 때마다 같은 검사가 돌고, 방금 바꾼 줄의 error만 돌려받는다. 파일에 원래 있던 문제로는 멈추지 않는다.
- 셸(`cat > 파일 <<EOF`)로 쓴 파일에는 자동 검사가 돌지 않는다. 화면 파일은 편집 도구로 쓰고, 끝에 통합 검사를 직접 돌린다.
- 검사는 소스 텍스트만 읽는다. error 0이어도 요청 누락·겹친 정보·실제 동작은 따로 확인한다.
8. **화면에서 확인한다.** 브라우저나 기기를 쓸 수 있으면 키보드만으로 조작, 열고 닫을 때의 포커스, 320·390px 폭의 넘침, 동작 줄이기를 직접 본다. 쓸 수 없으면 확인하지 못했다고 보고한다.
9. **보고한다.** 쓴 패턴과 이유 1~2줄, 고친 사용 문제와 범위, 기존 체계라 그대로 둔 것, 요청에 없어 더하지 않은 제안, 받아야 할 자료(사실 정보), 확인하지 못한 것을 적는다.
## 읽을 문서
경로는 이 스킬 폴더 기준이다. 맞는 줄을 모두 읽는다.
| 이럴 때 | 읽는다 |
|---|---|
| 모든 UI 작업 | 이 문서 전체 — 우선순위, 사용 문제, 사실 정보, 늘 지키는 선, 끝내기 전에 |
| 프로젝트에 디자인 기준이 없다 · 새 화면의 여백·색·글꼴·문구를 정한다 · "AI 티 안 나게", "밋밋해", "색이 많아" | `references/defaults.md` — AI가 흔히 만드는 틀과 기본 대안, 레이아웃·모션 값 |
| 기존 화면을 다듬는다 | `references/defaults.md`의 1절 "덜어내기"(요청한 범위 안에서) |
| 유형이 있는 화면 — 홈·목록·상세·검색·필터·채팅방·알림·프로필·폼·글쓰기·설정·장바구니·대시보드·리포트·기사·랜딩·AI 채팅·쇼핑·은행·증권·게이미피케이션·시작 안내 | `patterns/layout-principles/references/screen-conventions.md`의 그 유형 절과 "모든 화면에 공통". 앱이면 "하단 탭 바(GNB)"·"콘텐츠 그림", 웹뷰면 "모바일 웹뷰·웹앱"도 |
| 랜딩·서비스 소개 | 위 문서의 "랜딩" 절과 이 문서의 "사실 정보" |
| 색·글꼴 | `patterns/layout-principles/references/tone.md` |
| 화면 문구 | `patterns/layout-principles/references/copy.md` |
| React Native·Expo | `patterns/layout-principles/references/react-native.md`부터 — 웹 패턴 코드를 붙이지 않고 값·동작·접근성을 네이티브로 옮긴다 |
| 컴포넌트·상호작용을 만든다 | `references/catalog.md` → 고른 패턴의 `PATTERN.md` |
| 애니메이션 시간·이징 | `references/defaults.md`의 6절 "모션 값", 토큰은 `patterns/motion-principles/PATTERN.md` |
| 끝내기 전 | 이 문서의 "끝내기 전에" + `references/checklist.md`의 해당 절 |
## 늘 지키는 선 — 사용 문제
기존 디자인 체계와 상관없이 지킨다. 맞는 패턴이 없는 UI에도 적용된다.
### 접근성
| AI가 흔히 하는 것 | 대신 | 근거 패턴 |
|---|---|---|
| `div`에 `onClick` | `button`·`a`·`input`·`dialog` 같은 기본 요소 — 키보드와 스크린 리더가 그냥 된다 | `switch`, `checkbox-radio`, `modal-dialog` |
| 포커스 표시를 지움 | `:focus-visible`로 키보드 사용자에게만 보인다 | `dropdown-menu`, `quantity-stepper` |
| 모달·시트를 열어도 포커스·Esc·스크롤이 그대로 | 열면 포커스를 옮기고, Esc로 닫고, 배경을 `inert`로 막고, 닫으면 원래 자리로 포커스를 돌려준다 | `modal-dialog`, `bottom-sheet` |
| 알림·로딩 결과를 눈으로만 알림 | `role="status"`·`aria-live`로 읽어 준다 | `toast-stack`, `infinite-scroll` |
| 매초 바뀌는 값(걸음 수·타이머)이나 카운트업을 `role="status"`·`aria-live` 안에 둔다 — 바뀔 때마다 읽힌다 | 낭독 영역에는 사건(받았어요·목표 달성)만. 카운트업은 굴러가는 숫자를 숨기고 최종값만 읽힌다 | `count-up`, `celebration` |
| 버튼·행의 이름을 보이는 글자와 다른 문장의 `aria-label`로 덮는다(호가 행·목록 행) | 이름은 보이는 글자로 시작한다. 덧붙일 설명은 뒤에 붙이거나 `aria-describedby`로(WCAG 2.5.3) | `layout-principles` |
| 칸 이름이나 필수 표시를 안내 문구(placeholder) 안에만 둔다 — 입력을 시작하면 사라진다 | 칸 이름은 보이는 라벨이나 늘 보이는 제목 자리에, 필수는 라벨 옆이나 버튼 위 한 줄에 둔다. 안내 문구는 예시만. 예외: 제목·본문뿐인 글쓰기는 큰 제목 줄과 본문 영역의 자리가 이름을 대신한다 — `aria-label`로 이름을 주고, 필수는 버튼 위 한 줄로 늘 보인다 | `floating-label`, `layout-principles` |
| 마우스 호버로만 열리는 기능 | 키보드 포커스와 터치로도 열린다(길게 누르기·포커스) | `tooltip`, `long-press-menu` |
| 입력칸 확대를 막으려고 viewport에 `user-scalable=no`·`maximum-scale=1`을 넣는다 | 확대는 막지 않는다. 입력칸 글자를 16px 이상으로 하면 iOS가 확대하지 않는다 | `layout-audit` |
### 조작·피드백
| AI가 흔히 하는 것 | 대신 | 근거 패턴 |
|---|---|---|
| 눌러도 아무 반응이 없음 | 누르는 순간 눌린 상태가 보인다 — 기존 체계의 눌림 스타일, 없으면 살짝 줄었다 돌아온다 | `press-feedback` |
| 로딩은 스피너 하나, 연타하면 두 번 제출 | 자리를 잡는 스켈레톤, 진행 중에는 버튼을 잠근다 | `skeleton`, `loading-button` |
| 끌어서 닫기가 손가락을 따라오지 않음 | 드래그 중에는 손가락을 따라가고, 거리·속도로 닫을지 판정한다 | `bottom-sheet`, `swipe-dismiss-viewer` |
| 늦게 온 검색 응답이 최신 결과를 덮음 | 입력이 멈춘 뒤 한 번만 요청하고, 옛 응답은 버린다 | `search-suggest` |
| 무한 스크롤의 중복 호출·연쇄 로딩 | 미리 불러오되 중복·연쇄·실패 폭주를 막고, "더 보기" 버튼을 함께 둔다 | `infinite-scroll` |
| 제스처와 페이지 스크롤이 충돌 | `touch-action`으로 제스처 방향을 지정한다 | `bottom-sheet`, `swipe-to-delete` |
| 화면을 옮겨도 앞 화면의 토스트가 남는다. 토스트에 "보러 가기" 같은 버튼을 단다 | 화면이 바뀌면 토스트를 비운다(`clearToasts`). 토스트는 알림만 — 장바구니로 가는 길은 머리의 장바구니 아이콘(개수 배지가 바뀐다)이 맡는다 | `toast-stack` |
| 키보드가 올라오면 하단 탭 바가 따라 올라와 입력칸·제안 목록을 가린다 | 입력칸에 초점이 있는 동안 탭 바를 숨긴다 | `bottom-nav` |
### 넘침·크기
| AI가 흔히 하는 것 | 대신 | 근거 패턴 |
|---|---|---|
| 좁은 화면에서 열을 유지하고 글자를 9~11px로 줄인다 | 열을 줄인다. 글자 12px 이상, 누르는 영역 44px(최소 24px) | `layout-audit` |
| 짧은 영어 더미로만 확인한다 | 가장 긴 값·빈 값·320·390px 폭으로 확인한다 | `layout-principles` |
| 지표 칸을 같은 폭으로 나누고 큰 금액을 크게 둔다 — 가장 긴 값이 데스크톱에서도 칸 밖으로 잘린다 | 가장 긴 값으로 넓은 폭과 좁은 폭을 모두 본다. 금액은 단위를 줄이거나(7.9억 원) 칸에 `min-width: 0`을 두고 글자 크기를 칸에 맞춘다 | `layout-principles` |
| 좁은 화면에서 표의 열을 빼거나, 가로로 넘기는 표의 핵심 값 열(매출·금액)을 첫 화면 밖에 둔다 | 값은 빼지 않는다. 행마다 "이름 + 핵심 값 + 나머지 값 한 줄" 목록으로 바꾸거나, 이름·핵심 값 두 열을 고정하고 나머지를 넘긴다. 첫 화면 폭 안에 이름과 핵심 값이 함께 보여야 한다 | `layout-principles` |
| 화면 높이를 `100vh`로 둔다 | `100dvh` — 모바일 브라우저·웹뷰에서 아래가 잘리지 않는다 | `layout-audit` |
| 입력칸 글자를 16px 미만으로 둔다 | 16px 이상 — iOS가 초점을 받을 때 화면을 확대하지 않는다 | `layout-audit` |
| 화면 높이 grid 틀(행 둘 이상 + `fr`)에 열을 적지 않는다 | `grid-template-columns: minmax(0, 1fr)` — 입력칸 기본 폭·줄임표 글자가 틀을 화면보다 넓게 민다 | `layout-audit` |
| 하단 고정 바가 마지막 내용을 가린다 | 본문 아래 여백에 바 높이와 `env(safe-area-inset-bottom)`을 더한다 | `layout-principles` |
| 사진에 `object-fit: fill` | `aspect-ratio` + `object-fit: cover` — 늘이지 않고 잘라 채운다 | `layout-principles` |
| 한국어 문구가 단어 중간에서 줄바꿈된다 | `word-break: keep-all` | 한국어 조판 |
### 모션
| AI가 흔히 하는 것 | 대신 | 근거 패턴 |
|---|---|---|
| `transition: all` | 움직일 속성을 이름으로 나열한다 | `motion-audit` |
| `width`·`height`·`top`·`left`·`margin` 애니메이션 | `transform`·`opacity`만 움직인다. 높이는 `grid-template-rows: 0fr → 1fr`, 위치 변화는 FLIP | `accordion`, `flip-list` |
| 장식용 무한 반복 | 하루에 100번 보는 것은 움직이지 않는다. 모션은 "무슨 일이 일어났는지" 알릴 때만 쓴다 | `motion-principles` |
| `setInterval`로 스타일 갱신 | CSS transition, 필요하면 `requestAnimationFrame` | `motion-audit` |
| 상시 `will-change` | 움직이기 직전 상태에서만 | `motion-audit` |
| 동작 줄이기 설정을 무시한다 | 모든 CSS에 `@media (prefers-reduced-motion: reduce)` — 이동·확대를 끄고 짧은 페이드만 남긴다 | 모든 패턴 |
## 끝내기 전에
- [ ] 요청 목록의 화면·기능·상태가 모두 있고, 상태마다 화면에서 확인할 수 있다. 요청에 없는 기능·구역·항목은 더하지 않았다.
- [ ] 기존 디자인 체계의 토큰·컴포넌트·구조를 그대로 썼다. 바꾼 곳은 사용 문제 때문이고, 문제와 범위를 보고에 적었다.
- [ ] 실제 서비스 화면에 지어낸 고객사·사용자 수·후기·성과 수치가 없다. 가상 목업이면 그 구역에 예시 표시가 보인다. 실제 개인정보가 없다.
- [ ] 클릭할 수 있는 것은 모두 키보드로 닿고, 포커스가 보인다.
- [ ] 열리고 닫히는 것(모달·시트·메뉴)은 열면 포커스가 안으로 가고, Esc로 닫히고, 닫으면 포커스가 돌아온다.
- [ ] 상태 변화(저장됨·오류·로딩 끝)는 스크린 리더에도 전달되고, 매초 바뀌는 값은 낭독 영역 밖에 있다.
- [ ] 글자는 12px 이상·대비 4.5:1 이상이고, 누르는 영역은 44px(최소 24px)이다.
- [ ] 320·390px 폭에서 가로로 넘치거나 잘리는 요소가 없고, 넓은 폭에서도 가장 긴 값이 칸 안에 있으며, 하단 고정 바가 내용을 가리지 않는다.
- [ ] 움직이는 속성이 `transform`·`opacity`뿐이고(예외는 근거를 주석으로 남겼다), 새로 쓴 CSS마다 reduced-motion 블록이 있다.
- [ ] 통합 검사(`scripts/audit.mjs`) error가 0이고, 위 "사용 문제" warn이 없다.
- [ ] 브라우저에서 확인한 것과 확인하지 못한 것을 나눠 보고했다.
- [ ] 디자인 기준이 없는 화면이거나 유형이 있는 화면이면 `references/checklist.md`의 해당 절을 확인했다.
## 패턴 색인
이름과 데모 제목만 있다. 한 줄 설명과 고르는 예는 `references/catalog.md`에 있고, 고른 패턴은 `patterns/{패턴}/PATTERN.md`를 끝까지 읽는다.
<!-- index:start -->
| 묶음 | 패턴 |
|---|---|
| 등장과 전환 | `enter-exit` 진입/퇴장 애니메이션, `scroll-reveal` 스크롤 리빌, `sticky-header` 스티키 헤더 전환, `flip-list` 리스트 재배치 (FLIP), `zoom-lightbox` 확대 전환 라이트박스, `modal-dialog` 모달 다이얼로그, `spring-physics` 스프링 물리 모션, `theme-toggle` 다크모드 전환, `card-expand` 카드 확장, `card-stack` 카드 묶음, `stretchy-header` 늘어나는 이미지 헤더 |
| 로딩과 진행 | `skeleton` 스켈레톤 시머, `count-up` 숫자 카운트업, `story-progress` 스토리 프로그레스, `loading-button` 로딩 버튼, `infinite-scroll` 무한 스크롤, `virtual-list` 가상 스크롤, `progress-ring` 원형 진행 링 |
| 피드백 | `press-feedback` 프레스 피드백, `toast-stack` 토스트 스택, `like-pop` 좋아요 팝 + 더블탭 버스트, `celebration` 축하 오버레이, `cart-fly` 카트 플라이, `tooltip` 툴팁, `form-shake-error` 폼 에러 흔들림 |
| 내비게이션 | `tab-indicator` 탭 인디케이터 슬라이드, `carousel` 스냅 캐러셀, `hamburger-menu` 햄버거 메뉴, `page-transition` 화면 전환, `dropdown-menu` 드롭다운 메뉴, `glass-nav` 스크롤 반응 유리 GNB, `bottom-nav` 하단 탭 바(GNB) |
| 제스처 | `bottom-sheet` 바텀시트, `pull-to-refresh` 당겨서 새로고침, `swipe-to-delete` 밀어서 삭제, `pinch-zoom` 피드 핀치줌, `swipe-dismiss-viewer` 끌어내려 닫는 뷰어, `drag-to-reorder` 끌어서 순서 바꾸기, `long-press-menu` 길게 눌러 메뉴, `edge-swipe-back` 가장자리 스와이프 뒤로가기 |
| 컨트롤 | `select` 커스텀 셀렉트, `accordion` 아코디언, `switch` 토글 스위치, `floating-label` 플로팅 라벨 입력, `checkbox-radio` 체크박스 · 라디오, `otp-input` 인증번호 입력, `search-suggest` 검색어 자동완성, `range-slider` 범위 슬라이더, `quantity-stepper` 수량 스테퍼, `file-upload` 파일 업로드, `segmented-control` 세그먼트 컨트롤, `wheel-picker` 휠 피커 |
| 표면과 스타일 | `glass-surface` 유리판 (글라스모피즘) |
| 원칙과 검토 | `motion-principles` 모션 원칙과 토큰, `motion-audit` 모션 검사, `layout-principles` 레이아웃 원칙과 토큰, `layout-audit` 레이아웃 검사 |
<!-- index:end -->
## 쓰지 않는 경우·한계
- 화면이 없는 작업(API·데이터·문서·배포)에는 쓰지 않는다.
- 이 스킬은 레이아웃·글자·색·글꼴·문구·모션·상호작용·접근성을 다룬다. 프로젝트에 디자인 체계가 있으면 그 값과 구조를 쓰고, 이 스킬에서는 사용 문제의 선과 패턴 코드만 가져간다. 체계가 없을 때만 이 스킬의 토큰과 기본값을 쓴다.
- 기본값의 수치는 국내 앱 31곳·웹사이트 19곳·SaaS 랜딩 42곳에서 쟀다. 다른 나라 서비스의 관례와는 다를 수 있다.
- 패턴 코드는 React와 순수 TypeScript로 쓰여 있다. 다른 프레임워크는 코어와 CSS를 가져가 연결 부분만 새로 쓴다. React Native에는 코드가 없다 — `react-native.md`의 대응표로 옮긴다.
- 문구 검사는 코드 속 한국어 문자열의 끝맺음과 개발용 낱말만 본다. 40자 넘는 설명 문단과 약관 문장은 세지 않고, 용어가 맞는지·문장이 쉬운지는 사람이 읽는다.
- 검사는 소스 텍스트를 읽는 정적 검사다. 글자 대비·누르는 영역·좁은 폭 넘침과 실제 동작은 브라우저에서 확인한다. React Native는 `StyleSheet.create`의 값과 Reanimated 애니메이션까지 읽고, 나머지는 기기에서 확인한다.