feat(skill): route fixes, delivery report, composition diversity, platform refresh
- route table: extension route for products with an existing visual language, feedback row, derived state tokens stay local - completion: blocking 0 plus reported unverified; hard-gate surface is the touched scope; delivery report starts with what and how to open - design.md created at step 0 with a skeleton, fingerprint line and change log; product-surface rubric reading; direction delegation states the chosen card and reason - layout: hero families, nav forms, footer forms; art-direction page convergence 6 axes; genre sections; image treatments; placeholder conventions; multi-screen order; Tailwind theme mapping - platform: scroll-driven examples, anchor positioning, canvas OT, three/R3F pins, shadcn style prefixes, Safari 26 notes, subgrid - design-gate: four result states, exit codes, cwd module resolution, --url/--out-dir/--strict, oklch contrast, dead selector word match - Korean font budget split from the Latin baseline
This commit is contained in:
parent
9bcb45cbd3
commit
88c11682b1
51 changed files with 5834 additions and 2259 deletions
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
| 상황 | 읽을 곳 |
|
||||
|---|---|
|
||||
| 입력에 반응이 굼뜨다는 지적을 받았다 | §0 |
|
||||
| 입력에 반응이 굼뜨다는 지적을 받았다 / 로딩 표시를 고른다 | §0 |
|
||||
| hover·focus·active 같은 상태를 정리해야 한다 | §1 |
|
||||
| 버튼·카드 누름 피드백만 빨리 정하면 된다 | §2 |
|
||||
| 직접 드래그·슬라이더·캐러셀을 만든다 | §3 |
|
||||
|
|
@ -33,6 +33,17 @@
|
|||
- **전환 대기**: 이전 트랜지션이 끝나야 다음 입력을 받는 코드가 있는가 — 이는 §4 인터럽트 가능성이 막는 문제와 같다
|
||||
- **탭 지연**: `touch-action: manipulation`으로 더블탭 줌 판정 지연을 제거했는가(`mobile-app-ux.md`가 이미 전역 적용을 권한다)
|
||||
|
||||
**관찰 후보 — 기다림 표시는 예상 대기 시간으로 고른다.** 응답 시간 한계 세 구간(0.1초 즉시, 1초 흐름 유지, 10초 주의 한계 — Nielsen, *Usability Engineering* 1993)을 시작값으로 쓴다.
|
||||
|
||||
| 예상 대기 | 표시 | 이유 |
|
||||
|---|---|---|
|
||||
| 약 300ms 미만 | 표시하지 않는다(누름 피드백만) | 짧게 번쩍이는 스피너가 오히려 느려 보인다 |
|
||||
| 300ms ~ 1초 | 누른 컨트롤 안의 작은 스피너·비활성 표시 | 입력이 접수됐다는 것만 알리면 된다 |
|
||||
| 1초 ~ 10초 | 콘텐츠 자리에 스켈레톤(§8) 또는 진행 표시 | 화면 구조를 미리 보여 흐름을 유지한다 |
|
||||
| 10초 이상 | 진행률(가능하면 %)·남은 단계·취소 수단 | 주의가 떠나므로 통제권을 돌려준다 |
|
||||
|
||||
경계값은 실측으로 조정한다. 표시가 짧게 깜빡이는 것을 막으려면 표시 시작에 약 300ms 지연을 두고, 일단 보이면 최소 표시 시간을 준다.
|
||||
|
||||
**관찰 후보 — 체감 성능은 실제 로딩 시간과 다른 축이다.** 같은 대기 시간이라도 스피너 등장이 빠르면 더 짧게 느껴지고, 연속 조작(셀렉트 옵션 전환 등)은 트랜지션 자체를 줄이면 반응이 가벼워진다. 이징이 이 지각을 증폭시킨다는 점도 염두에 둔다 — 도착이 급정거처럼 보이는 이징은 빠른 반응도 무겁게 만든다 [SKILL-EMIL-DESIGN-ENG].
|
||||
|
||||
---
|
||||
|
|
@ -41,7 +52,7 @@
|
|||
|
||||
인터랙티브 요소는 여러 상태를 동시에 가진다. 상태마다 시각 신호·모션·접근성 속성을 따로 정해야 어느 하나가 비어도 사용자가 상태를 잃지 않는다.
|
||||
|
||||
**하드 게이트 — 모션은 유일한 신호가 되면 안 된다.** `prefers-reduced-motion`에서 모션이 꺼졌을 때 그 상태를 알려줄 정적 단서(색·아이콘·라벨·테두리)가 남아 있어야 한다. 모션이 유일한 신호였다면 감소 모션 사용자에게는 상태 자체가 사라진다 — `motion.md` §5 "접근성 — 타협 없음"과 같은 이유다 [SKILL-BETTER-UI].
|
||||
**하드 게이트 — 모션은 유일한 신호가 되면 안 된다.** `prefers-reduced-motion`에서 모션이 꺼졌을 때 그 상태를 알려줄 정적 단서(색·아이콘·라벨·테두리)가 남아 있어야 한다. 모션이 유일한 신호였다면 감소 모션 사용자에게는 상태 자체가 사라진다 — 정본은 [accessibility.md](accessibility.md) 부록 A다 [SKILL-BETTER-UI].
|
||||
|
||||
| 상태 | 시각 신호 | 모션 | 접근성 속성 |
|
||||
|---|---|---|---|
|
||||
|
|
@ -161,7 +172,7 @@ if (Math.abs(dragDistance) >= DISTANCE_THRESHOLD || velocity > VELOCITY_THRESHOL
|
|||
|
||||
## 6. 스프링
|
||||
|
||||
> 언제: 브리프·0단계 인터뷰([brief-interview.md](brief-interview.md))에서 스프링감(경쾌함·플레이풀함)이 톤으로 선택됐을 때, 또는 프로젝트가 이미 스프링 기반 라이브러리를 쓰고 있을 때만 연다. 절제된 톤에서는 스프링을 쓰지 않는다.
|
||||
> 언제: 두 가지 경우에 연다. (a) 드래그·던지기·시트 스냅처럼 **도중에 다시 조작될 수 있는 전환**을 만들 때 — 임계감쇠 스프링(ζ = 1, 오버슈트 없음)은 톤과 무관한 기본 후보다. 중단·재조준에서 현재 속도를 이어받기 때문이다(§4 인터럽트 원칙). (b) 브리프·0단계 인터뷰([brief-interview.md](brief-interview.md))에서 경쾌함·플레이풀함이 톤으로 선택돼 **바운스(ζ < 1)** 를 검토할 때. 스프링은 바운스와 같은 말이 아니다.
|
||||
|
||||
같은 "스프링감"을 세 가지 표기 체계가 서로 다른 언어로 규정한다. 병기하고, 프로젝트가 이미 쓰는 라이브러리가 있으면 그 표기를 우선한다.
|
||||
|
||||
|
|
@ -185,7 +196,7 @@ bounce = 1 - ζ # ζ ≤ 1 일 때. ζ > 1(과감쇠
|
|||
|
||||
**CSS `linear()`로 라이브러리 없이 근사한다.** `motion.md` §3이 이미 "무한 루프, `linear()` 스프링 근사"를 CSS 네이티브로 분류해 둔 것과 같은 방법이다 — damping/response 값을 표본점 배열로 뽑아 `animation-timing-function: linear(...)`에 채운다. 표본값은 프로젝트마다 도구로 생성하는 것이지 여기서 고정값을 두지 않는다.
|
||||
|
||||
**톤과 연결한다.** 플레이풀·장식적 톤일 때만 스프링에 바운스를 허용하고, 절제된 톤(스위스·럭셔리 계열 등)에서는 스프링 자체를 쓰지 않는다 [SKILL-EMIL-DESIGN-ENG].
|
||||
**톤과 연결한다 — 톤이 정하는 것은 스프링 여부가 아니라 바운스 여부다.** 플레이풀·장식적 톤일 때만 바운스(ζ < 1, `bounce > 0`)를 허용한다. 절제된 톤(스위스·럭셔리 계열 등)에서는 바운스를 쓰지 않는다. 임계감쇠 스프링(ζ = 1, `bounce 0`)은 오버슈트가 없어 눈으로는 감속 곡선과 구분되지 않으므로 톤과 무관하게 인터럽트 가능한 전환의 기본 후보다 — 위 표 첫 행의 "기본 UI: `damping 1.0`"이 이 뜻이다 [SKILL-EMIL-DESIGN-ENG][SKILL-APPLE-DESIGN].
|
||||
|
||||
**감소 모션에서는 바운스가 없다.** `prefers-reduced-motion`에서는 오버슈트·바운스를 모두 제거하고 임계감쇠(`damping 1.0`/`bounce 0`)로만 전환한다 — `motion.md` 코드 H와 같은 원칙이다.
|
||||
|
||||
|
|
@ -220,7 +231,7 @@ bounce = 1 - ζ # ζ ≤ 1 일 때. ζ > 1(과감쇠
|
|||
|
||||
### 토글 스위치
|
||||
|
||||
`role="switch"`, `aria-checked` 필수 — ARIA Authoring Practices Guide의 스위치 패턴을 따른다 [ARIA-APG]. 손잡이 이동은 `translate`(저비용 기본 선택, `motion.md` §2)로 하고, `transition: translate var(--dur-instant) var(--ease-out)`가 기본이다. 스프링감이 필요하면 §6의 조건(톤이 확인됐을 때)에서 `linear()` 근사를 컴포넌트에 로컬로 쓴다 — 전역 토큰을 새로 만들지 않는다.
|
||||
`role="switch"`, `aria-checked` 필수 — ARIA Authoring Practices Guide의 스위치 패턴을 따른다 [ARIA-APG]. 손잡이 이동은 `translate`(저비용 기본 선택, `motion.md` §2)로 하고, `transition: translate var(--dur-instant) var(--ease-out)`가 기본이다. 바운스 있는 스프링감이 필요하면 §6의 조건(톤이 확인됐을 때)에서 `linear()` 근사를 컴포넌트에 로컬로 쓴다 — 전역 토큰을 새로 만들지 않는다.
|
||||
|
||||
### 스켈레톤 시머
|
||||
|
||||
|
|
@ -294,7 +305,7 @@ bounce = 1 - ζ # ζ ≤ 1 일 때. ζ > 1(과감쇠
|
|||
|
||||
## 10. 엄지 영역
|
||||
|
||||
모바일 폭에서 1차 조작(주로 쓰는 버튼·주요 CTA)은 엄지가 자연스럽게 닿는 영역에 배치한다. 하단이 상단보다 도달하기 쉽다는 원칙은 `mobile-app-ux.md`가 이미 하단 시트·바텀내비의 근거로 쓰고 있다(썸존 = 화면 하단 약 35%) [SKILL-IMPECCABLE]. 이 문서에서 새로 정의하지 않고 자세한 레이아웃 규칙은 [mobile-app-ux.md](mobile-app-ux.md)를 따른다.
|
||||
모바일 폭에서 1차 조작(주로 쓰는 버튼·주요 CTA)은 엄지가 자연스럽게 닿는 영역에 배치한다. 화면 중앙~하단 중앙과 측면 중단은 닿기 쉽고, 상단 모서리와 큰 폰의 하단 극단 모서리는 닿기 어렵다(Hoober의 휴대 자세 관찰). 고정 비율("하단 몇 %")로 단정하지 않고, 기기 크기·한 손/두 손 사용을 고려해 실기기에서 확인한다. 이 원칙은 `mobile-app-ux.md`가 하단 시트·바텀내비의 근거로 쓴다 [SKILL-IMPECCABLE]. 이 문서에서 새로 정의하지 않고 자세한 레이아웃 규칙은 [mobile-app-ux.md](mobile-app-ux.md)를 따른다.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -311,16 +322,18 @@ bounce = 1 - ζ # ζ ≤ 1 일 때. ζ > 1(과감쇠
|
|||
|
||||
---
|
||||
|
||||
## 12. 체크리스트
|
||||
## 12. 체크리스트 (구현 중 자기 점검 — 납품 판정은 preflight.md X10)
|
||||
|
||||
- [ ] 입력 경로에 불필요한 디바운스·타이머·전환 대기가 없다(§0)
|
||||
- [ ] hover 전용 스타일이 `@media (hover: hover) and (pointer: fine)`로 게이트돼 있다(§1)
|
||||
- [ ] 상태 변화(hover·active·selected·error 등)마다 모션이 꺼져도 남는 정적 신호가 있다(§1)
|
||||
- [ ] 프레스 피드백이 `scale(0.96~0.98)` 범위이고 `--dur-instant`를 쓴다. 새 duration 리터럴을 만들지 않았다(§2)
|
||||
- [ ] 커스텀 드래그 요소가 `setPointerCapture`·그랩 오프셋 보존·멀티터치 가드를 갖췄다(§3)
|
||||
- [ ] 재트리거되는 UI에 `@keyframes` 대신 `transition`을 썼다(§4)
|
||||
- [ ] 스와이프·당겨서 새로고침에 단일 포인터 대체 조작이 있다(§8, WCAG 2.5.7)
|
||||
- [ ] 토스트가 `role="status"`/`role="alert"`이고, 행동이 있는 토스트는 자동 소멸하지 않는다(§8)
|
||||
- [ ] 스프링을 썼다면 브리프·인터뷰로 톤이 확인됐고, 감소 모션에서는 바운스가 사라진다(§6)
|
||||
- [ ] 사운드는 기본 꺼짐이고, 햅틱은 단독 신호로 쓰이지 않는다(§9)
|
||||
- [ ] 커스텀 컨트롤을 실기기·엔진 근거로 검증했거나, 못 했다면 미검증으로 보고에 남겼다(§11)
|
||||
납품 판정은 [preflight.md](preflight.md) X10이 하고, 스와이프·당겨서 새로고침의 단일 포인터 대체는 A21, 토스트의 role은 A18이 판정한다.
|
||||
|
||||
- 입력 경로에 불필요한 디바운스·타이머·전환 대기가 없다(§0)
|
||||
- hover 전용 스타일이 `@media (hover: hover) and (pointer: fine)`로 게이트돼 있다(§1)
|
||||
- 상태 변화(hover·active·selected·error 등)마다 모션이 꺼져도 남는 정적 신호가 있다(§1)
|
||||
- 프레스 피드백이 `scale(0.96~0.98)` 범위이고 `--dur-instant`를 쓴다. 새 duration 리터럴을 만들지 않았다(§2)
|
||||
- 커스텀 드래그 요소가 `setPointerCapture`·그랩 오프셋 보존·멀티터치 가드를 갖췄다(§3)
|
||||
- 재트리거되는 UI에 `@keyframes` 대신 `transition`을 썼다(§4)
|
||||
- 행동이 있는 토스트는 자동 소멸하지 않는다(§8)
|
||||
- 바운스(ζ < 1)를 썼다면 브리프·인터뷰로 톤이 확인됐고, 감소 모션에서는 바운스가 사라진다. 임계감쇠 스프링은 톤 확인 없이 쓸 수 있다(§6)
|
||||
- 기다림 표시가 예상 대기 시간 구간에 맞다 — 짧은 대기에 스피너가 번쩍이지 않고, 10초 이상이면 진행률·취소가 있다(§0)
|
||||
- 사운드는 기본 꺼짐이고, 햅틱은 단독 신호로 쓰이지 않는다(§9)
|
||||
- 커스텀 컨트롤을 실기기·엔진 근거로 검증했거나, 못 했다면 미검증으로 보고에 남겼다(§11)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue