← Cockpit Wireframes Pages by level Navigation map
Library — page patterns, scaling, motion▾
Shipped app (old grammar)Components · 공유 키트상세 페이지 — 플랫 언어Session Card · 밴드 없는 카드
홈 · Home
Home · 홈
시작 · Entry
Onboarding · 시작
ArchiveStrip Map · 세션 스트립 전체 시나리오Headers · 헤더 아이콘Card Continuity · 카드 = 상세의 예고편Session Hero · 더 조용한 공유 카드 (제안)Session Share Card · 히어로 = 공유 카드Session List Frame · 날짜 섹션 · 패널 필터 · 2+2 카드Compaction — 일정 · 코트 · 스코어카드Compaction — 클럽 상세 · 홈Compaction — 기록 · 프로필Live anchor — 진행 중 밴드Density · 카드 밀도토스트 — 리디자인 제안Club List · 클럽Clubs · 클럽클럽 상세 — 여섯 탭 플랫운영 섹션 — 플랫 목표위저드 내비게이션 — 결정 보드드래프트 복원 — 무음 복원클럽 조 편성 — 모델 결정 보드게스트 → 회원 — 심사 흐름Page polish — titled postsWeb app — desktop layoutProduct questions — openWeb app — desktop adaptation, round 2Visual tone — pure white, flat rows, one Tag, a colour budgetLibrary — chrome, surfaces, rows, labelsLibrary — controls, overlays, states, formsLibrary — page patterns, scaling, motionLibrary — additions from the pagesEmpty-once — 프로필 빈 섹션Notification groups — 할 일 밴드 그룹화Profile Cockpit · 프로필 압축Session Detail — Redesign · 일정Venue Detail — Redesign · 테니스장Profile Detail — Redesign · 프로필Tournament Detail — Redesign · 대항전Applicant Detail — Redesign · 가입 심사Canvas Migration — 회비 · 멤버 · 규칙Record Detail — Redesign · 기록Venue Finder · 코트 찾기Venue Filters · 코트 필터 재구성Notifications · 알림Panel Placement · 하단 고정
Archived — superseded by Library — overlays and feedback
Interactive kit · grounded in code

Library — page patterns, scaling, motion

Flat v2 · Phase B · proposal

Component library: page patterns · scaling · motion · accessibility · platform

Owner, 2026-10-09: “design all of the components first, including all their interactive behaviours, so that we have shared components used throughout the app.” This is the last of the three component boards. It draws the twelve rows of library section 14 (the components the page audit found missing) and the four things the behaviour addendum changed for every component: how text scales (B6), how things move (B7), what a screen reader hears (B8) and how the chrome sits on the device (B9). Scheme S2 Ball & Ink. The sibling boards are /wireframes/flat-v2-components (chrome, surfaces, rows, labels) and /wireframes/flat-v2-controls (controls, overlays, states, forms).

How to read the board
  • A twelve specimen blocks: name and role, the anatomy at 1:1 with pt rulers, the states row, the variants, the behaviour, used by and replaces. Then the same twelve in the dark values.
  • B nine components at 100 / 130 / 150 / 200%, each frame printing the derived, the table and the measured height.
  • C one frozen frame sequence per motion with its duration and curve, and the reduced-motion column.
  • D the focus order, the spoken name and the headings on six surfaces.
  • E the 390 × 844 chrome stack on iPhone, Android gesture and 3-button navigation, light and dark.
  • F every height measured in a real browser, and every place the text could not be drawn exactly as written.
Rulings carried in (library sections 14 and 15, addendum Q1 to Q10)
  • every fixed height that holds text is a floor (B6.4); the strip draws the caps: reading 2.0, hero 1.7, numeral 1.5, label 1.5, chrome 1.3;
  • PersonRow and ResultRow pitch is 68 (the sibling board is patched), the sheet rises in 300 ms and leaves in 200, the toast lasts 4000 / 6000 / 8000 ms by kind;
  • effects use timing on three curves, springs only for the press and the tab indicator, no layout animation in virtualised lists, one reduced-motion table;
  • one focus stop per row, one visible h1, the name from the row's own model, state in aria-*; predictive back stays off; the chrome offsets are 16 steps over the runtime inset.
APage componentslibrary section 14: twelve rows, each at 1:1 with rulers, states, variants, behaviour, used by and replaces
HeaderTextActionOne labelled word in the header for the thing an official comes to do: 관리, 초대 코드, 편집, 완료.
ghost text 15/22·600 ink · pad 0 12 in a 44 target · the text edge on the right rail (the target bleeds 12) · at most one per header side, left of any icon (the overflow ⋯ is always rightmost: [관리] [⋯], §17 H1) · label of at most four syllables · never destructive · the same geometry as Button ghost md
Anatomy
20370
강남 라켓클럽
관리
12261244
6
44 target
text 15/22·600 ink · icon 24 in its own 44
6
Press
target: scale 0.97 · opacity 0.92 · spring in {20, 400, .7}, out {15, 300, .7} · dim 80 ms in, 150 ms out · UI thread
Hover
web, pointer: opacity .85 + cursor: pointer
Focus
web: 2px ink ring, offset 2
Disabled
opacity .5, not-allowed, inert · aria-disabled
Loading
none: the action disables (.5) while its mutation runs; the spinner lives on the screen's CTA
Slop
none: the target is the 44 itself (the text is 22 tall, the pad is the hit area)
Placement
left of any icon, gap 8; the overflow ⋯ is always the rightmost item ([관리] [⋯], [초대 코드] [+]); a text action alone sits on the right rail; a second text action only in Select mode (취소 on the left)
Text scale
chrome class, cap 1.3 (Apple: Back and Edit stay small); the target stays 44, the header stays 56
A11y
role button; the name is the visible text (label in name); focus returns to it after its sheet closes
Used by
club hub 관리 (IA-4), 클럽 tab 초대 코드, edit mode 편집 / 완료, the 선택 entry of Select mode
Replaces
HeaderTextAction ($primary tint becomes ink); the icon-only gear (U-05)
States
강남 라켓클럽
관리
rest ink 15/22·600, no fill
강남 라켓클럽
관리
pressed scale .97 · opacity .92 on the target
강남 라켓클럽
관리
hover web: opacity .85 + pointer
강남 라켓클럽
관리
focus web: 2px ink ring, offset 2, around the 44 target
강남 라켓클럽
완료
disabled opacity .5 · inert · aria-disabled (완료 before any change)
Not drawn: loading, selected, error, empty: a header action is a command, never a toggle; it has no loading face (see Loading), no error face (errors live in the field or the Dialog) and cannot be empty.
Variants
클럽
초대 코드
tab root, four syllables 클럽 tab: title 20/28·700 · 초대 코드 · icon (§17 H1)
프로필
편집
프로필
완료
edit mode 편집 swaps to 완료; the title stays
Clubs
Invite code
English Invite code: longer, still one line; the target grows, never the header
관리
over a MediaBand proposed: white 600 on an ink α0.4 pill 36 tall (the glyph circles' sibling)
ActionSheetEvery action of an object that does not fit the five rows of the Menu: grouped Cells in a bottom sheet.
ModalPanel bottom · title 17/24·600 = the object's name + close · body = groups of Cells 48 (leading icon 20 optional, label 15/22·400 ink, a chevron only where it opens another screen) · group title 13/18·600 secondary, 8 below · cell hairline inside a group · 24 between groups · destructive group last, in danger · no CTA band · at most 80% of the screen, the body scrolls · padding bottom 20 + inset
Anatomy
20370
9:41
올림픽공원 복식
관리
올림픽공원 복식
운영
참석자 관리
출석 확인3
참가비 관리
경기
경기 규칙
호스트 관리
대진 짜기
공유
공유
단톡방
링크 복사
위험 구역
경기 종료
일정 취소
일정 삭제
sheet 675.2 = 80% of 844
scrim 169
the page stays visible: 844 − 80%
20
24 title row · 17/24·600
16
18 group title · 13/18·600
8
3 × 48 cells
hairline inside the group
24 between groups
18 group 2 title
8
3 × 48 cells
24
group 3, clipped at 790
the body scrolls; 위험 구역 is below
54 = 20 + the 34 inset
no content, no CTA
Press
cell: scale 0.98 · opacity 0.80 · spring in {20, 400, .7}, out {15, 300, .7} · dim 80 ms in, 150 ms out · UI thread
Hover
web, pointer: opacity .85 + cursor: pointer
Focus
web: 2px ink ring, offset 2
Disabled
opacity .5, not-allowed, inert · aria-disabled
Open
rise 300 ms `sheetIn` (enter curve) from y +48 · scrim fades with the platform Modal · reduced motion: no rise
Close
exit 200 ms · scrim tap, the × (24 in 44), Android back and iOS swipe-down on the title row (Q3) · focus returns to the trigger
Scroll
the body scrolls inside the 80% cap (the code's ModalPanel caps at 88%: the ActionSheet passes the tighter cap); a thin indicator shows while scrolling
Destructive
last group, labelled 위험 구역, each cell in danger; a tap opens its own Dialog (confirm consequential mutations), never a second sheet
Chevron
only on a cell that opens another screen; an action that acts at once (공유, 링크 복사) has none; a count is a neutral count tag
A11y
native Modal, aria-modal; the first element is the title heading; each cell is one stop named by its label (count joins: "출석 확인, 3"); close "닫기"
Used by
session host tools (up to nine rows, IA-4), club 관리 (IA-4), any set of more than five actions (the Menu keeps at most five)
Replaces
the host tools hidden in the ⋯ Menu (nine rows) · HeaderActionsMenu above five rows
States
참가비 관리
rest a cell of the group
참가비 관리
pressed scale 0.98 · opacity 0.80
참가비 관리
hover web: opacity .85 + pointer
참가비 관리
focus web: 2px ink ring, offset 2
참가비 관리
disabled opacity .5 · inert (참가비 관리 on a free session)
Not drawn: loading, selected, error, empty: a sheet of commands is never a picker (no selected face; pickers are PickerRow sheets), an action's pending and failed states belong to its Dialog or toast, and an object with no actions hides its trigger.
Variants
9:41
올림픽공원 복식
관리
올림픽공원 복식
운영
참석자 관리
출석 확인3
참가비 관리
경기
경기 규칙
호스트 관리
대진 짜기
공유
공유
단톡방
링크 복사
위험 구역
경기 종료
일정 취소
일정 삭제
scrolled to the end the destructive group in view · 위험 구역
9:41
강남 라켓클럽
관리
강남 라켓클럽
운영
멤버 관리
가입 신청2
회비 관리
설정
클럽 정보 수정
알림 설정
위험 구역
클럽 해산
six actions, no scroll club 관리: 3 groups, the sheet is as tall as its content (60 + 54 + body)
9:41
올림픽공원 복식
관리
일정을 취소할까요?
참석자 8명에게 알림이 가고,입금한 참가비는 환불 대상이 돼요.
닫기
일정 취소
its own Dialog 일정 취소 asks before it commits; the scrim does not dismiss it
운영
참석자 관리
출석 확인3
메모 남기기
cell kinds icon + chevron · icon + count · no icon
CTA band helper lineThe consequence of the one action, said above the button: the fee and the cancellation terms, or why it is disabled.
inside the band, above the button · 13/18·500 secondary · at most 2 lines · left-aligned at the rail · MetaSegments (the 1px break, never a dot) · 8 above the button · band 118 → 144 (one line) / 162 (two lines) at the 34 inset · static, never a second action
Anatomy · one line
20370
참가비 10,000원시작 24시간 전까지 무료 취소
참석하기
20350 · the helper and the button share the rail20
16 above
18 helper · 13/18·500
8
52 button lg
primary, ball + ink
16 below
34 home inset
144 in all
Static
the helper is text, never pressable, never a second action; a reason to act differently is a Button or a Notice
Lines
one fact line by default; a second line only for terms that cannot fit (the refund rule); never three — longer copy is a Cell (취소 규정)
Segments
MetaSegments: each fact its own text node, the 1px MetaBreak between; at 150% the segments wrap, the break stays
Tone
secondary 13/18·500 always; the reason a CTA is disabled is also secondary (an error is a Notice, never red text on the band)
Edge
the band draws #D4D4D4 on its top row only while content lies below; the helper moves with the band
Text scale
reading class: 13/18 grows to 26/36 at 200%, wraps to more lines; the band is a floor, never clipped
A11y
a text stop before the button; the button's name stays its visible label (WCAG 2.5.3); the helper is read, not announced
Code today
BottomCtaBand pads max(16, inset) below and keeps a permanent top border; v2 adds 16 and the inset and draws the hairline on scroll only (B9.6)
Used by
session detail fee and cancellation terms, cooldown and tier gates (IA-3 · primary-action.tsx), 클럽 가입 월 회비, the disabled-CTA reason of the wizards
Replaces
the Text lines above BottomCtaBand in primary-action.tsx; the dues fact row at the CTA
Anatomy · two lines
20370
참가비 10,000원시작 24시간 전까지 무료 취소
이후에 취소하면 참가비의 50%만 환불돼요
참석하기
2035020
16 above
36 two lines
2 × 18, no gap between
8
52 button lg
16 below
34 home inset
162 in all
States
참가비 10,000원시작 24시간 전까지 무료 취소
참석하기
rest helper + CTA
참가비 10,000원시작 24시간 전까지 무료 취소
참석하기
CTA pressed the Button's own .97 / .92; the helper does not react
2시간 후에 다시 신청할 수 있어요
참석하기
CTA disabled Button .5 · the helper says why (cooldown)
참가비 10,000원시작 24시간 전까지 무료 취소
CTA loading spinner in the button, width held · the helper stays
Not drawn: pressed, hover, focus, selected, error, empty: the helper itself is passive text with no interactive faces (the Button beside it carries them); an empty helper is simply no line (the 118 band); an error is a Notice above the band, never red text on it.
Variants
NTRP 3.5 이상만 참석할 수 있어요
참석하기
tier gate disabled CTA · the rating rule as the reason
월 회비 30,000원가입 후 첫 달은 무료
가입하기
join a club the monthly dues as a consequence
시작 시간장소를 정해 주세요
다음
wizard, what is missing disabled until the required fields are set
클럽 멤버 24명에게 알림이 가요
취소
일정 만들기
confirm pair + helper tonal flex 1 + primary flex 2, the helper over both
참석하기
no helper 118: 16 + 52 + 16 + 34, the plain band
MediaBandThe first thing a page shows when it has a place or a face: a cover photo, or a map.
photo: full-bleed 240 including the status bar · cover-fit · scrim ink α0.35 → 0 over the first 100 · the header over it transparent, white 24 glyphs on 36 ink α0.4 circles · COVER-ALWAYS · map: the same 240 with the same scrim and glyph circles (§19.2) · static map, 24 ink pin centred in the visible 140 (y 170) · no Tag or chip · a tap opens 장소 상세
Anatomy · photo
20370
9:41
scrim 100
44 status inset
white glyphs over the photo
56 header
back · share · ⋯, 36 circles in 44 targets
140 photo
240 in all, cover-fit
Scroll
past 240 − 100 = 140 of scroll the header turns white in 150 ms (`fade`): glyphs ink, the title fades in, the edge hairline on; the status bar goes light → dark with it; hysteresis 24
Rest
not pressable; the glyph circles are IconButtons (.92 / .78, focus ring ball on the dark circle)
Loading
gray100 block, no shimmer under the glyphs; the status bar is dark while there is no photo
Error
the default cover (COVER-ALWAYS): the court tile at cover size, never an empty band
Scrim
ink α0.35 → 0 over the first 100 so white glyphs read on a bright photo (a 1:1 gradient, no blur)
Text scale
glyphs and circles do not scale (header action glyphs stay 24 in 44); a header title, once solid, is the chrome class, cap 1.3
A11y
the photo is decorative (hidden); the hero title is the one h1 and the header title is aria-hidden until it shows
Used by
club hub, club preview, venues (photo); session detail when its venue has a map (map; IA-10, owner rulings COVER-ALWAYS and MAP-BAR-INK)
Replaces
the club photo seam and the venue hero tile; a photo-less hub with a bare header
Anatomy · scroll
서초구
강남 라켓클럽
멤버 42명NTRP 3.0–4.0
9:41
scrolled 0 the photo under a transparent header
서초구
강남 라켓클럽
멤버 42명NTRP 3.0–4.0
9:41
scrolled 139 the photo's last 101 still under the header: still transparent
서초구
강남 라켓클럽
멤버 42명NTRP 3.0–4.0
9:41
강남 라켓클럽
scrolled 140 the photo's bottom meets the header's: white in 150 ms, the title fades in, hairline on
Anatomy · map
20370
9:41
scrim 100
140
pin centre y 170
44 status inset
white glyphs over the map
56 header
back · share, 36 circles in 44 targets
140 visible map
240 in all; the pin is centred here
Press
the map layer (§16: the header glyphs keep their own press): dim 0.85, no settle · spring in {20, 400, .7}, out {15, 300, .7} · dim 80 ms in, 150 ms out · UI thread
Hover
web, pointer: opacity .85 + cursor: pointer
Focus
web: the ring is drawn inset 2 (a full-bleed band has no outside); the glyph circles take the ball ring
Scroll
as the photo: past 140 of scroll the header turns white in 150 ms, glyphs ink, the title fades in, the hairline on
Loading
gray100 band, no pin; the status bar is dark while there is no map
Error
the court tile of the venue (hard / clay / grass) with the pin removed
Pin
24 ink, centred in the visible area under the header (y 170 of 240); white dot 7; no label on the map (the page names the venue)
No Tag
MAP-NO-CHIP: the map is its own cue; nothing is drawn on it besides the pin and the glyph circles
A11y
role link, name "장소 상세, 올림픽공원 테니스장"; the map art is hidden; the tap opens 장소 상세 (directions live there)
Used by
session detail with a venue map (IA-10, owner rulings MAP-BAR-INK and MAP-NO-CHIP), venue detail
Replaces
the 160 band under a white header with a 지도 Tag that opened the map app (the shipped SessionHeroSection already draws this: §19.2 aligns the library to it)
Anatomy · map scroll
9:41
scrolled 0 the map under a transparent header
9:41
올림픽공원 테니스장
scrolled 140 white in 150 ms: glyphs ink, title in, hairline on
States
9:41
photo · rest status bar light, scrim on
9:41
photo · loading gray100 (the dark status bar: nothing to contrast with)
9:41
photo · error the default cover: the court tile
9:41
map · rest
9:41
map · pressed dim .85 on the map layer, no settle (a full-bleed image cannot scale)
9:41
map · hover web: opacity .85 + pointer
9:41
map · focus web: ring inset 2
9:41
map · loading gray100, no pin
9:41
map · error the court tile, pin removed
Not drawn: photo disabled, selected, empty: the photo band is not a control and cannot be empty (COVER-ALWAYS); map disabled, selected, empty: a venue without coordinates draws no map band at all (the Cell with its address is the page's fallback).
Variants
9:41
관리
photo + header text 관리 on the pill, proposed (A1)
9:41
other cover the photo is cover-fit: any aspect fills 390 x 240
9:41
Android status inset 24, edge-to-edge: the photo runs under the status bar
PostRowOne post of the club feed: who, what they wrote, what they showed, and how the club reacted.
author line 44 (avatar 40 + 12 · name 15/22·600 · time 13/18·500 secondary · ⋯ IconButton 44) → 12 → body 15/22·400, 4-line clamp + 더보기 15/22·600 secondary → 12 → photos (1: 350 × 262 r12, 4:3 · 2: 171² r8, gap 8 · 3+: two squares with +N 20/28·700 white on ink α0.5 over the second) → 12 → action row 44 (heart 20 + count 13/18·600, comment 20 + count, gap 16) · posts 32 apart, no line
Anatomy
20370
이서연2시간 전
토요일 정기 모임 후기예요. 날씨가 좋아서 코트 세 면이
꽉 찼어요. 복식 두 게임씩 돌고 마지막에 다 같이 단체
사진도 찍었어요. 처음 오신 분들도 금방 적응하셔서
분위기가 정말 좋았어요. 다음 주에도 같은 시간에…더보기
12
3
204012298 text20
44 author line
avatar 40 · name 22 + time 18 · ⋯ 44
12
88 body
4 lines × 22, the 4th ends in 더보기
12
262 photo
350 × 262, 4:3, r12
12
44 actions
heart 20 · comment 20 · gap 16
+ 32 of air before the next post
Press
the body block: scale 0.98 · opacity 0.80 · spring in {20, 400, .7}, out {15, 300, .7} · dim 80 ms in, 150 ms out · UI thread
Hover
web, pointer: opacity .85 + cursor: pointer
Focus
web: 2px ink ring, offset 2
Opens
the body block (text + photos) presses through to the post; the avatar and name open the profile; ⋯ opens the row actions (공지 등록, 삭제); the actions do their own thing
Clamp
4 lines; the 4th ends with 더보기 inline (a passive hint: the row opens the whole post; code today clamps at 5)
Photos
cover-fit; the +N is a count of the rest, over the second square (the code today shows three squares, +N on the third)
Actions
heart: outline → filled ink with the count 600 ink; comment opens the post at the comments; each in a 44 high target, pressed .92 / .78
Skeleton
avatar circle + name 36% + time 22% + two body lines + a 350 × 262 block; opacity .6 ↔ .9
Pinned
a neutral Tag 공지 trailing the author line (one Tag per row); pinned posts lead the feed
Text scale
reading class: the body wraps, the 4-line clamp holds at 200% (the lines get taller), names wrap to 2 lines; the author line grows, the ⋯ target stays 44
A11y
one stop for the body block named by author, time, the body's first sentence, photo count, reactions: "이서연, 2시간 전, 토요일 정기 모임 후기예요, 사진 1장, 좋아요 12개, 댓글 3개"; ⋯ and the two actions are their own stops
Used by
the club 홈 feed (the pane of the club hub), the album post feed
Replaces
PostFeedRow (post-row.tsx) and FeedPhotoStrip: the bottom hairline, the three-square strip, the 5-line clamp
States
이서연2시간 전
토요일 정기 모임 후기예요. 날씨가 좋아서 코트 세 면이
꽉 찼어요. 복식 두 게임씩 돌고 마지막에 다 같이 단체
사진도 찍었어요. 처음 오신 분들도 금방 적응하셔서
분위기가 정말 좋았어요. 다음 주에도 같은 시간에…더보기
12
3
rest · 1 photo the anatomy
이서연2시간 전
다음 주 정기 모임은 시간이 한 시간 당겨졌어요.
오전 8시에 올림픽공원 4번 코트에서 만나요.
12
3
pressed the body block: scale .98 · opacity .80
이서연2시간 전
다음 주 정기 모임은 시간이 한 시간 당겨졌어요.
오전 8시에 올림픽공원 4번 코트에서 만나요.
12
3
hover web: opacity .85 on the body block + pointer
이서연2시간 전
다음 주 정기 모임은 시간이 한 시간 당겨졌어요.
오전 8시에 올림픽공원 4번 코트에서 만나요.
12
3
focus web: 2px ink ring, offset 2, around the body block
loading · .6 avatar, two lines, two body lines, the photo block
loading · .9 the top of the pulse
Not drawn: disabled, selected, error: a post is never inert or picked; a failed publish stays in the composer, not in the feed; an empty feed is the states board's EmptyState, not a row.
Variants
이서연2시간 전
다음 주 정기 모임은 시간이 한 시간 당겨졌어요.
오전 8시에 올림픽공원 4번 코트에서 만나요.
12
3
2 photos two 171 squares, r8, gap 8
이서연2시간 전
다음 주 정기 모임은 시간이 한 시간 당겨졌어요.
오전 8시에 올림픽공원 4번 코트에서 만나요.
+3
12
3
3+ photos two squares, +3 over the second
이서연2시간 전
다음 주 정기 모임은 시간이 한 시간 당겨졌어요.
오전 8시에 올림픽공원 4번 코트에서 만나요.
13
3
text only · liked no photo: 156; the heart filled ink
김민준9월 15일
공지
다음 주 정기 모임은 시간이 한 시간 당겨졌어요.
오전 8시에 올림픽공원 4번 코트에서 만나요.
12
3
pinned a neutral Tag 공지 before the ⋯
삭제된 게시물
deleted 삭제된 게시물 15/22 secondary, in a 48 row
박지훈2시간 전
12
3
photo only no body: author, photo, actions
MessageBubble + ComposerA conversation: bubbles in runs, the lines between them, and the bar that writes the next one.
bubble max 264 · pad 8 12 · r18, the sender-side corner 6 on the last of a run · 15/22 · mine ink #15181A + white, right · theirs gray100 + ink with avatar 32 and the name 13/18·600 secondary on the first · 4 within a run, 12 between runs · time 11/14·500 beside the last · date and system lines 13/18·500 centred, 16 around · composer min 56 + inset: attach 24/44, field gray100 r20 min 40 pad 8 16 (to 5 lines), send 36 ball circle in 44
Anatomy · bubbles
205260370
박지훈
내일 몇 시에 모여요?
9시 코트 앞이에요
오후 2:14
알겠어요
오후 2:16
내일 봐요
32264 max
12
18 name · 13/18·600
4
38 bubble · pad 8 + 22 + 8
4 in a run
38 last · corner 6 · time
12 new run
38 mine · ink, r18
4
38 last, mine · corner 6
Press
long-press a bubble: scale 0.98 · opacity 0.80 · spring in {20, 400, .7}, out {15, 300, .7} · dim 80 ms in, 150 ms out · UI thread
Hover
web, pointer: opacity .85 + cursor: pointer
Focus
web: 2px ink ring, offset 2
Long-press
opens the per-message actions (반응, 복사, 삭제); a bubble has no tap action (a tap on a link opens the link); a tap on the avatar opens the profile
Runs
messages of one sender within a minute are one run; the run ends at the last bubble, which takes the corner 6 and the time
Enter
a new bubble fades in with an 8 rise over 150 ms (`fade`), reduced motion: none; the thread stays bottom-anchored (inverted list: no layout animation)
Sending
optimistic: the bubble at opacity .6 and "보내는 중" in the time slot; failed: a 16 danger glyph, 전송 실패 and a text action 다시 보내기
Text scale
reading class cap 2.0: the bubble keeps max 264 and wraps; 11/14 time is chrome-small (cap 1.3); the avatar stays 32
A11y
one stop per bubble: sender, text, time, in that order ("박지훈, 내일 몇 시에 모여요, 오후 2:14"); mine drops the sender; the date and system lines are plain text stops
Used by
the DM thread and the group thread
Replaces
DmMessageRow ($primary mine, $primarySubtle reactions): mine becomes ink so the ball stays the one action (send)
Anatomy · thread
20370
9:41
토요일 복식 멤버
9월 18일 (금)
박지훈
내일 몇 시에 모여요?
저는 8시 40분쯤 도착할 것 같아요. 코트 예약은 9시부터라서 천천히 와도 돼요
오후 2:14
9시에 코트 앞에서 만나요
오후 2:16
공은 제가 가져갈게요
최유나님이 대화에 참여했어요
최유나
저도 갈게요!
오후 2:20
오후 2:21
좋아요, 내일 봐요
메시지 보내기
44 status
56 header
back · title · ⋯, edge hairline
654 thread
bottom-anchored, 8 above the bar
56 composer
min: 8 + field 40 + 8; grows to 5 lines
34 home inset
the bar rides the keyboard
20370
9:41
토요일 복식 멤버
9월 18일 (금)
박지훈
내일 몇 시에 모여요?
저는 8시 40분쯤 도착할 것 같아요. 코트 예약은 9시부터라서 천천히 와도 돼요
오후 2:14
9시에 코트 앞에서 만나요
오후 2:16
공은 제가 가져갈게요
최유나님이 대화에 참여했어요
최유나
저도 갈게요!
오후 2:20
오후 2:21
좋아요, 내일 봐요
내일 9시에 코트 앞에서 봐요
100 status + header
352 thread
what stays above the bar
56 composer
no inset: the keyboard owns it
336 keyboard
a stand-in block, no real keys
States
박지훈
내일 몇 시에 모여요?
오후 2:14
bubble · rest theirs, one bubble: avatar, name, corner 6, the time beside
박지훈
내일 몇 시에 모여요?
오후 2:14
bubble · pressed long-press: scale .98 · opacity .80
박지훈
내일 몇 시에 모여요?
오후 2:14
bubble · focus web: 2px ink ring, offset 2, follows r18
보내는 중
9시에 코트 앞에서 만나요
bubble · sending opacity .6 · 보내는 중
오후 2:16
9시에 코트 앞에서 만나요
전송 실패다시 보내기
bubble · failed danger glyph + 전송 실패 + 다시 보내기
메시지 보내기
composer · empty placeholder 15/22 secondary · send .5
내일 9시에 코트 앞에서 봐요
composer · filled text 15/22 ink · send ball, 36 in 44
내일 9시에 코트 앞에서 봐요
composer · disabled send text present, offline or sending: send .5 (queued, not lost)
오늘은 비가 와서 코트 사정이 안 좋아요.대신 실내 코트를 한 시간 잡아 놨으니까9시 30분까지 그쪽으로 오세요
composer · 3 lines the field grows 22 a line: 3 lines = 82
오늘은 비가 와서 코트 사정이 안 좋아요.대신 실내 코트를 한 시간 잡아 놨으니까9시 30분까지 그쪽으로 오세요.주차는 지하 2층이 가장 가까워요.늦으면 단톡방에 꼭 남겨 주세요
composer · 5 lines, scrolls max 5 lines = 126, then the field scrolls inside
Not drawn: bubble disabled, selected, empty: a sent message is never inert or picked (a deleted message becomes a system line 삭제된 메시지예요); the empty thread is the states board's EmptyState; the composer's blocked state (the request gate) is its own bar, not a face of this one.
Variants
박지훈
저 오늘 조금 늦을 것 같아요
10분 정도요
먼저 시작하세요
오후 2:14
a run of three 4 apart; the avatar and name once, the corner 6 once
9월 18일 (금)
최유나님이 대화에 참여했어요
date and system lines 13/18·500 secondary, centred, 16 around (50)
박지훈
저는 8시 40분쯤 도착할 것 같아요. 코트 예약은 9시부터라서 천천히 와도 돼요
오후 2:14
a two-line bubble max 264 wraps; the time stays at the last line's end
CalendarChoosing or reading days: the month grid and the week strip.
month: header 44 (month 17/24·600 at the rail + prev / next IconButtons) · weekday row 32 (13/18·500 secondary, 일 danger, 토 $series1) · day cells 50 × 44 (7 × 50 = 350) · numeral 15/22·500 in a 36 circle · today 1.5px ink ring · selected ink fill + white 600 · range ink ends + gray100 band · up to three 4px dots · out-of-month days not drawn · week strip 7 × 50, label 12/16·500 over a 36 circle 15/22·600, height 64
Anatomy · month
20370
2026년 9월
일
월
화
수
목
금
토
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
73676 × 50 more · 350 in all
44 month header
17/24·600 at the rail · ‹ › 44 targets
32 weekday row
13/18·500 · 일 danger · 토 $series1
44 day cell
36 circle + 2 + 4 dots + 2
4 more weeks
5 weeks = 220 · 6 = 264
Press
a day: scale 0.98 · opacity 0.80 · spring in {20, 400, .7}, out {15, 300, .7} · dim 80 ms in, 150 ms out · UI thread
Hover
web, pointer: opacity .85 + cursor: pointer
Focus
web: 2px ink ring, offset 2
Disabled
opacity .5, not-allowed, inert · aria-disabled
Select
a tap selects: ink fill, white 600, instantly (100 ms colour); the picker's CTA commits; a range: the first tap is the start, the second the end
Today
a 1.5px ink ring on the numeral circle; a selected today is the ink fill (the ring vanishes inside it)
Dots
one to three 4px ink dots, the activity of the day (sessions or matches); none is no dot, never a zero
Keyboard
role grid; arrows move by day and week, Home / End the row, PageUp / PageDown the month (web); a cell is aria-selected
Text scale
numerals chrome-small: the cell is a floor (44), the 36 circle grows with its numeral (36 → 40 at 150%, B6.7) and the grid keeps 7 columns; the week strip label scales with the label class
A11y
a cell reads the date and its facts: "9월 19일 토요일, 일정 3개"; today first ("오늘, 9월 18일 금요일"); out-of-month cells are absent
Used by
기간 기록 activity calendar (the dots), the 홈 and create-session date pickers (the wheel stays for times)
Replaces
ActivityMetricCalendar's metric cells and bars (adjustsFontSizeToFit goes, B6.9) and the raw date-time picker for dates
Anatomy · week strip
20370
일
13
월
14
화
15
수
16
목
17
금
18
토
19
507 × 50 = 350
16 label · 12/16·500
36 numeral circle · 15/22·600
64 = 4 + 16 + 4 + 36 + 4. The strip swipes by week (paged, no snap animation under reduced motion); it carries no dots (the 64 has no room for them): the day below the strip lists the day's activity. 일 / 토 colour the label only, as in the month.
States
일
월
화
수
목
금
토
6
7
8
9
10
11
12
rest numeral 15/22·500 in the 36 circle
일
월
화
수
목
금
토
13
14
15
16
17
18
19
today 1.5px ink ring
일
월
화
수
목
금
토
13
14
15
16
17
18
19
selected ink fill, white 600
일
월
화
수
목
금
토
13
14
15
16
17
18
19
pressed the day: scale .98 · opacity .80
일
월
화
수
목
금
토
13
14
15
16
17
18
19
hover web: opacity .85 + pointer
일
월
화
수
목
금
토
13
14
15
16
17
18
19
focus web: 2px ink ring, offset 2, on the circle
일
월
화
수
목
금
토
13
14
15
16
17
18
19
disabled opacity .5 · inert: the days before today in a date picker
일
월
화
수
목
금
토
13
14
15
16
17
18
19
activity dots one, two and three 4px dots; none = no dot
일
월
화
수
목
금
토
loading a grid of gray100 circles, opacity .6 ↔ .9; the header and weekdays stay
Not drawn: selected as a checkbox state, error, empty: a date is picked, not checked; an invalid date is a disabled cell (never an error message); a month with no activity is a grid without dots (the page's EmptyState speaks for the list below it).
Variants
2026년 9월
일
월
화
수
목
금
토
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
range 16 → 23: ink ends, the gray100 band runs through the week break
2026년 9월
일
월
화
수
목
금
토
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
picker, past disabled the days before today .5; the CTA commits the pick
2026년 8월
일
월
화
수
목
금
토
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
six weeks 2026년 8월 starts on a Saturday: 340 in all
일
13
월
14
화
15
수
16
목
17
금
18
토
19
week strip today 18 ringed, 19 selected; labels colour 일 / 토
일
13
월
14
화
15
수
16
목
17
금
18
토
19
week strip, pressed day the same row physics as a month cell
PodiumThe top three of a ranking, before the list that starts at 4위.
three columns over 350 (2위 · 1위 · 3위), centred · 1위 avatar 56 raised 16, 2위 and 3위 avatar 48 · a 2px medal ring (gold #D9A00F, silver #8E8E93, bronze #B2580B) · under each: rank 13/18·600 secondary, name 15/22·600 (2 lines, never cut), ELO 17/24·700 tabular, a level Tag · height 168 (a floor)
Anatomy
20370
2위
박지훈
1,547
3.5
1위
이서연
1,623
4.0
3위
최유나
1,498
3.0
16 raise
2위 · 116.71위 · 116.73위 · 116.7
56 avatar, 1위
medal ring 2 inside the 56 · gaps 4
18 rank · 13/18·600
22 name · 15/22·600
24 ELO · 17/24·700
24 level Tag
8 → 168
Press
a column: scale 0.98 · opacity 0.80 · spring in {20, 400, .7}, out {15, 300, .7} · dim 80 ms in, 150 ms out · UI thread
Hover
web, pointer: opacity .85 + cursor: pointer
Focus
web: 2px ink ring, offset 2
Opens
a column opens that player's profile; the list below starts at 4위 (PersonRow, rank variant, 68 each)
Medal
a 2px ring on the avatar's own edge (no emoji medals, no fill); the rank text carries the order, so the ring is never the only signal
Empty slot
fewer than three ranked: the missing column is a dashed 48 circle (the roster's empty slot) with no text
Loading
three gray100 circles (56 / 48 / 48) over four short bars, the same raise; opacity .6 ↔ .9
Names
wrap to 2 lines (NAME_LINES), 3 from 150%; never cut, never shrunk (adjustsFontSizeToFit goes, B6.9); the height is a floor
Text scale
the ELO figure is the numeral class (cap 1.5), the rest reading; at 150% the columns stay three (116.7 each holds a 7-syllable name per line)
A11y
one stop per column: "1위, 이서연, ELO 1,623, NTRP 4.0"; the ring and the glyphs are hidden; the three read in rank order, 1위 first
Used by
leaderboards (records · club ranking · the session 순위 tab)
Replaces
LeaderboardPodium: the 🥇🥈🥉 emoji, the 40 / 12 / 0 steps (PODIUM_STEP) and the adjustsFontSizeToFit name
States
2위
박지훈
1,547
3.5
1위
이서연
1,623
4.0
3위
최유나
1,498
3.0
rest 2위 · 1위 · 3위
2위
박지훈
1,547
3.5
1위
이서연
1,623
4.0
3위
최유나
1,498
3.0
pressed a column: scale .98 · opacity .80
2위
박지훈
1,547
3.5
1위
이서연
1,623
4.0
3위
최유나
1,498
3.0
hover web: opacity .85 + pointer
2위
박지훈
1,547
3.5
1위
이서연
1,623
4.0
3위
최유나
1,498
3.0
focus web: 2px ink ring, offset 2, around the column
loading · .6 circles 56 / 48 / 48 over four bars
2위
박지훈
1,547
3.5
1위
이서연
1,623
4.0
empty slot two ranked: a dashed 48 circle for the third
Not drawn: disabled, selected, error: a ranking is read, not picked; an error is the page's QueryErrorState; a ranking with no one ranked is the states board's EmptyState.
Variants
2위
Alexander Park
1,547
3.5
1위
이서연
1,623
4.0
3위
최유나
1,498
3.0
a two-line name a name wrapping in a lower column: 190 = 168 + 22 (in the raised 1위 column: 182); the columns stay level
2위
박지훈
1,547
3.5
1위
이서연
1,623
4.0
3위
최유나
1,498
3.0
4
정
정우진
1,4553.5
29경기
5
한소희
1,4303.0
31경기
with the list below 4위 starts the PersonRow rank variant (68 each)
ScoreEntryOne way to enter a result, for the host and for a participant: type it, or tap it.
team rows (names 15/22·600, 2 lines) × set columns of number Inputs 48 × 48 r12, 24/32·700 centred, numeric keypad, auto-advance · a quick-pick Choice row (6-0 … 6-4, 7-5, 7-6) fills the active set · a tiebreak field 36 × 36 appears at 6-6 / 7-6 · validation helper in danger under the table · one CTA 저장 · focus = 2px ink border · error per field, never a toast
Anatomy
20370
1세트
2세트
3세트
김민준, 이서연
6
7
박지훈, 최유나
4
6
2세트 타이브레이크, 진 팀의 점수
5
빠른 입력
6-06-16-26-36-47-57-6
한 세트는 7점까지만 입력할 수 있어요
names 1903 × 48 + 2 × 8 = 160
18 set labels · 13/18·600
8
48 team row
name 15/22·600, 2 lines · inputs 48 × 48
8
48 team row
12
44 tiebreak row
field 36 × 36 · only at 6-6 / 7-6
16
18 label · 13/18·600
8
2 × 36 Choices, gap 8
6-0 … 6-4 / 7-5, 7-6 wrap left-aligned
8
18 helper · danger 13/18·500
Press
a quick-pick Choice: scale 0.96 · opacity 0.85 · spring in {20, 400, .7}, out {15, 300, .7} · dim 80 ms in, 150 ms out · UI thread
Hover
web, pointer: opacity .85 + cursor: pointer
Focus
the active input: a 2px ink border (no ring); the set label above it turns ink
Disabled
opacity .5, not-allowed, inert · aria-disabled
Keypad
numeric, one digit per field; typing the second digit of a pair jumps to the next field (auto-advance), the CTA band rides above the keypad
Quick pick
a Choice fills both fields of the ACTIVE set at once and moves to the next set; picking again replaces; the library does not say which team a pick credits (drawn: the top team gets the first number — an open call)
Tiebreak
at 6-6 or 7-6 the 36 × 36 field appears (250 ms reveal) for the losing side's points; the library gives the size, the row is drawn here
Validation
a helper in danger under the table names the rule ("한 세트는 7점까지만…"), the offending field gets a danger border; the CTA stays tappable and re-validates; never a toast
Names
wrap to 2 lines, 3 from 150%; never cut; the input columns never move
A11y
each field is a spinbutton named by team and set: "1세트, 김민준 팀 점수"; the error is announced when it appears; the Choices are a radio group named "빠른 입력"
Used by
the scorecard and the match board for the host (ruling 14) and for a confirmed participant
Replaces
the ±1 Stepper sheet (ScoreEditSheet) and the inline single-pair inputs of MatchCard
States
6
input · rest 1px #D4D4D4, r12
6
input · focus 2px ink border, caret
8
6
input · error 1px danger border + the helper under the table
6
4
input · disabled opacity .5 · inert (a participant on a finished match)
5
12
tiebreak · focus 36 × 36 r10, 2px ink border
6-47-57-6
Choice · pressed pill: scale .96 · opacity .85
saving CTA 저장 in its loading face; fields inert
Not drawn: loading of the entry itself, empty: the table is built from the match, so it never loads; a match with no result starts with empty fields (the rest face); selected is the Choice's.
Variants
1세트
2세트
3세트
김민준, 이서연
6
6
박지훈, 최유나
4
1
빠른 입력
6-06-16-26-36-47-57-6
best of 3, a quick pick 1세트 6-4 filled, 2세트 active, 6-1 picked
1세트
2세트
3세트
김민준, 이서연
6
7
박지훈, 최유나
4
6
2세트 타이브레이크, 진 팀의 점수
빠른 입력
6-06-16-26-36-47-57-6
a tiebreak at 7-6 the field reveals under the table; the pick 7-6 stays selected
1세트
2세트
3세트
김민준, 이서연
6
8
박지훈, 최유나
4
6
한 세트는 7점까지만 입력할 수 있어요
a validation error 8-6 is not a set: danger border and helper
1세트
2세트
김민준, 이서연, 정우진, 한소희 복식팀
6
박지훈, 최유나, 윤하늘, 강태오 복식팀
4
long names 2 lines each; the inputs hold their columns
1세트
2세트
김민준, 이서연
6
6
박지훈, 최유나
4
3
read only a participant on a closed match: inert, no quick pick, no CTA
In context
9:41
경기 결과
9월 19일 (토) 1코트
점수 입력
1세트
2세트
3세트
김민준, 이서연
6
4
5
박지훈, 최유나
4
6
빠른 입력
6-06-16-26-36-47-57-6
저장
123
456
789
0⌫
the entry on the match board typing the 3rd set: numeric keypad up, the CTA band rides above it (no inset: the keypad owns it)
LiveScoreThe scoreboard of a match in play, and the two buttons that move it.
ink Block 350 × 144 r16, pad 20 · LIVE row 24 · two team rows 32 (names 15/22·600 white α0.85, set scores 24/32·700 white tabular in 32 columns, the current set and the game points 0 / 15 / 30 / 40 / AD in ball, a 6 ball dot before the server) · host controls below: two lg tonal Buttons (팀 A 포인트 / 팀 B 포인트) + an undo IconButton · read-only for participants
Anatomy
20370
LIVE코트 2
김민준, 이서연6340
박지훈, 최유나4230
팀 A 포인트
팀 B 포인트
14name 1832 × 321732
20 pad
24 LIVE row · tag + 코트
8
32 team row
name 15/22·600 · numerals 24/32·700
8
32 team row
20 pad → 144
16
52 controls
two lg tonal Buttons + undo 44
Press
a point Button: scale 0.97 · opacity 0.92 · spring in {20, 400, .7}, out {15, 300, .7} · dim 80 ms in, 150 ms out · UI thread
Hover
web, pointer: opacity .85 + cursor: pointer
Focus
web: 2px ink ring, offset 2
Undo
the IconButton (ink, 44) reverts the last point; .5 and inert when there is nothing to undo; one level per tap, no timer
A point
tap = a haptic outcome (success) and the numeral swaps with a 150 ms cross-fade (`fade`, library section 14; reduced motion: instant); the addendum B7.6 says scores update instantly, an open conflict drawn as the library says; no toast; the server is optimistic
Serving
a 6 ball dot hung before the server's name; it moves after each game; the name column keeps its 14 slot for both teams
Points
0 · 15 · 30 · 40 · AD in ball; AD is wider than the 32 column (overflow visible, centred); in a tiebreak the points are counts
Participants
the block alone, read-only; no controls, no focus stop on the numerals; the host sees the controls only while the match is live
Loading
a gray100 block at 144, opacity .6 ↔ .9; the controls hold their place disabled
Text scale
numerals are the numeral class (cap 1.5): 36 / 48 at 150%, the columns stay 32 and the row grows; names wrap to 2 lines; the Buttons are floors (lg 52 → 64 at 150%)
A11y
the block is one stop: "김민준, 이서연 6 대 4, 3 대 2, 40 대 30, 서브"; a live region politely announces a new game, not each point
Used by
the live match board (host view) and the scoreboard link (participant view)
Replaces
the live-score bar of the match board and the ±1 stepper sheet for live scoring
States
LIVE코트 2
김민준, 이서연6340
박지훈, 최유나4230
팀 A 포인트
팀 B 포인트
host · rest block + controls
LIVE코트 2
김민준, 이서연6340
박지훈, 최유나4230
팀 A 포인트
팀 B 포인트
point button · pressed Button: scale .97 · opacity .92
LIVE코트 2
김민준, 이서연6340
박지훈, 최유나4230
팀 A 포인트
팀 B 포인트
point button · focus web: 2px ink ring, offset 2
LIVE코트 2
김민준, 이서연00
박지훈, 최유나00
팀 A 포인트
팀 B 포인트
undo disabled nothing to undo: opacity .5
LIVE코트 2
김민준, 이서연6340
박지훈, 최유나4230
participant · read only the block alone
팀 A 포인트
팀 B 포인트
loading gray100 block, controls disabled
Not drawn: selected, error, empty: nothing here is picked; a point that fails to save reverts its numeral and raises the error toast (with the haptic error), the block itself has no error face; a match not started yet is the scorecard's, not this block.
Variants
LIVE코트 2
김민준, 이서연6340
박지훈, 최유나4230
the other server the dot moves to team B
LIVE코트 2
김민준, 이서연65AD
박지훈, 최유나4540
advantage AD in ball, wider than its 32 column
LIVE코트 2
김민준, 이서연64315
박지훈, 최유나46230
a third set three set columns: the name column gives up 32
LIVE코트 2
남궁민수, 황보서연, 독고은하 복식팀6340
제갈공명, 사마의, 하후돈4230
long names a name wraps to two lines: its row grows to 44 and the block with it (a floor: 144 → 156)
Select modeActing on several rows at once: tick the ones that apply, then do the one thing.
entered from a HeaderTextAction 선택 (long-press is an accelerator) · header: 취소 (text, left) · 'n명 선택됨' title · 전체 선택 (text, right) · each row gains a leading 24 checkbox (2px #4F5851; checked ink fill + white check) at the rail and its content shifts 36 · a row tap toggles · the trailing element stays · the CTA band carries the bulk action as lg primary, disabled at zero
Anatomy
2056370
9:41
취소
2명 선택됨
전체 선택
확인 대기 2
이서연30,000원
9월 17일 입금 완료 신청
박지훈30,000원
9월 18일 입금 완료 신청
미납 4
최유나30,000원
9월 회비
정
정우진30,000원
9월 회비
한소희30,000원
9월 회비
김민준30,000원
내 회비는 다른 운영진이 확인해요
2명 입금 확인
2024124012text · amount stays right
44 status
56 header
취소 · n명 선택됨 · 전체 선택
8
24 section head · 확인 대기
8
2 × 68 rows, ticked
checkbox 24 + 12; the 확인 대기 rows
32
24 section head · 미납
8
4 × 68 rows, unticked
미납 · the last is own, disabled
118 CTA band
16 + 52 + 16 + 34 · lg primary
Press
a row: scale 0.98 · opacity 0.80 · spring in {20, 400, .7}, out {15, 300, .7} · dim 80 ms in, 150 ms out · UI thread
Hover
web, pointer: opacity .85 + cursor: pointer
Focus
web: 2px ink ring, offset 2
Disabled
opacity .5, not-allowed, inert · aria-disabled
Enter
a HeaderTextAction 선택 in the normal header; a long-press on a row also enters (accelerator, never the only path); the checkboxes appear with a 150 ms fade, the content shifts 36 (reduced motion: instant)
Header
취소 leaves and clears; the title counts ("2명 선택됨", read politely on every change); 전체 선택 ticks every eligible row (two text actions: the HeaderTextAction rule is one per side)
Defaults
ruling 12: only 확인 대기 rows are ticked when the mode opens; 미납 rows start unticked (the 총무 ticks those verified against the bank statement)
Own row
the viewer's own dues stay visible, unticked, the checkbox .5 and a caption in line 2: the payer never self-confirms (migration 00536)
Commit
the CTA asks a Dialog (count, total, who is told) before it writes: a money action confirms (canon: confirm consequential mutations); disabled at zero
Text scale
rows are floors (68 → 125 at 150%, trailing amount under line 2); the checkbox stays 24; the header title is the chrome class (cap 1.3) and stays on one line
A11y
each row role checkbox, aria-checked, named like its PersonRow ("이서연, 회비 30,000원, 확인 대기"); the checkbox is hidden (the row is the stop)
Used by
dues reconcile (flow 8), the session-fee confirm list (flow 3)
Replaces
the one-by-one 확인 대기 confirm and the 'mark all, then undo the exceptions' path
States
최유나30,000원
9월 회비
row · unticked 2px #4F5851 square, r6
이서연30,000원
9월 17일 입금 완료 신청
row · ticked ink fill + a white check 16
최유나30,000원
9월 회비
row · pressed scale .98 · opacity .80: the tap toggles
최유나30,000원
9월 회비
row · hover web: opacity .85 + pointer
최유나30,000원
9월 회비
row · focus web: 2px ink ring, offset 2, around the row; Space toggles
김민준30,000원
내 회비는 다른 운영진이 확인해요
row · disabled the viewer's own: .5 + the caption; never ticked
row · loading the PersonRow skeleton keeps the 36 shift
취소
2명 선택됨
전체 선택
header · rest 취소 · 2명 선택됨 · 전체 선택
취소
2명 선택됨
전체 선택
header · pressed a text action: scale .97 · opacity .92
Not drawn: selected is the ticked row; error: a failed bulk call closes the Dialog, keeps the ticks and raises the error toast; empty: a month with no dues rows is the EmptyState, and select is not offered.
Variants
9:41
회비
선택
확인 대기 2
이서연30,000원
9월 17일 입금 완료 신청
박지훈30,000원
9월 18일 입금 완료 신청
미납 4
최유나30,000원
9월 회비
정
정우진30,000원
9월 회비
한소희30,000원
9월 회비
김민준30,000원
내 회비는 다른 운영진이 확인해요
entry the normal header with 선택; no checkboxes yet
9:41
취소
0명 선택됨
전체 선택
확인 대기 2
이서연30,000원
9월 17일 입금 완료 신청
박지훈30,000원
9월 18일 입금 완료 신청
미납 4
최유나30,000원
9월 회비
정
정우진30,000원
9월 회비
한소희30,000원
9월 회비
김민준30,000원
내 회비는 다른 운영진이 확인해요
입금 확인
zero selected 0명 선택됨 · the CTA disabled .5
9:41
취소
2명 선택됨
전체 선택
확인 대기 2
이서연30,000원
9월 17일 입금 완료 신청
박지훈30,000원
9월 18일 입금 완료 신청
미납 4
최유나30,000원
9월 회비
정
정우진30,000원
9월 회비
한소희30,000원
9월 회비
김민준30,000원
내 회비는 다른 운영진이 확인해요
2명 입금 확인
2명의 입금을 확인할까요?
합계 60,000원이 입금 완료로 기록되고,이서연, 박지훈님에게 알림이 가요.
닫기
입금 확인
the commit Dialog a money action asks first; the scrim does not dismiss it
9:41
취소
2명 선택됨
전체 선택
확인 대기 2
이서연30,000원
9월 17일 입금 완료 신청
박지훈30,000원
9월 18일 입금 완료 신청
미납 4
최유나30,000원
9월 회비
정
정우진30,000원
9월 회비
한소희30,000원
9월 회비
김민준30,000원
내 회비는 다른 운영진이 확인해요
합계 60,000원
2명 입금 확인
with the total the helper line of A3 carries the sum: 합계 60,000원
CountdownNoticeThe clock on a held seat: how long the transfer has, and what happens at zero.
a Notice, warning tone (#F5B301, ink text) · r12 · pad 12 16 · icon 20 + 12 · title 15/22·600 '미납' + the countdown 15/22·600 tabular at the right ('09:41 남음') · body 15/22·400 = the account line · 68 · at zero it becomes danger (title '시간이 지나 자리가 풀렸어요', the action 다시 신청), min 48
Anatomy
20370
미납09:41 남음
카카오뱅크 3333-01-1234567 김총무
162012286 text · the countdown ends the title row16
12 pad
22 title row · 600 + clock 600
22 body · the account line
12 pad → 68
Press
the 다시 신청 action: dim 0.85, no settle · spring in {20, 400, .7}, out {15, 300, .7} · dim 80 ms in, 150 ms out · UI thread
Hover
web, pointer: opacity .85 + cursor: pointer
Focus
web: 2px ink ring, offset 2
Ticks
every second; the digits do not animate (tabular numerals hold the width); the seat is a server deadline, so the clock is essential under WCAG 2.2.1
Last minute
no visual change (the tone is already the warning); the clock keeps ticking in the same place; the screen reader hears 5:00 and 1:00 only
At zero
the Notice swaps to danger in place (no height jump: min 48); the action 다시 신청 re-opens the apply flow; the hold row is gone server-side
Loading
the action shows an ink-on-danger spinner (white) while 다시 신청 runs; the label returns on failure with the error toast
Dismiss
none while running (a deadline is not dismissable); the danger face has no close: the action is the way out
Text scale
reading class: the title and the clock wrap apart from 150% (the clock under the title, left-aligned: B6.5 "Notice: action under the text")
A11y
a text group; the account line is selectable text; a polite live region announces 5:00 and 1:00, then the danger title when it expires
Used by
the payment hold of session detail (flow 3)
Replaces
the status line under the fee fact, and the silent expiry
States
미납09:41 남음
카카오뱅크 3333-01-1234567 김총무
running warning tone · 09:41 남음
미납00:42 남음
카카오뱅크 3333-01-1234567 김총무
last minute same face; the digits count 00:42, no new colour
시간이 지나 자리가 풀렸어요
다시 신청
expired danger tone, min 48, the action 다시 신청
시간이 지나 자리가 풀렸어요
다시 신청
expired · pressed the action: dim .85, no settle
시간이 지나 자리가 풀렸어요
다시 신청
expired · hover web: opacity .85 + pointer
시간이 지나 자리가 풀렸어요
다시 신청
expired · focus web: 2px ink ring, offset 2, on the action
시간이 지나 자리가 풀렸어요
expired · loading the action becomes a spinner; the width is held
Not drawn: disabled, selected, empty: the notice is shown only while a hold exists and is never inert; error: a failed reapply is the error toast, the notice stays danger.
Variants
Waiting for payment09:41 left
카카오뱅크 3333-01-1234567 김총무
English Waiting for payment · 09:41 left
Your spot was released
Reapply
English, expired Your spot was released · Reapply
미납09:41 남음
카카오뱅크 3333-01-1234567 김총무
시간이 지나 자리가 풀렸어요
다시 신청
dark warning #E2B13C / danger #FA9B94, ink text
The twelve in the dark valuescanvas #0F1211, surface #1A201B, block #232824, ink #DFE4DC; the ball and its ink text unchanged
강남 라켓클럽
관리
HeaderTextAction ink #DFE4DC on the canvas
운영
참석자 관리
출석 확인3
위험 구역
일정 삭제
ActionSheet surface #1A201B, hairline white α.09, danger #FA9B94
참가비 10,000원시작 24시간 전까지 무료 취소
참석하기
CTA band helper secondary #A9B3AB, edge white α.14
서초구
강남 라켓클럽
멤버 42명NTRP 3.0–4.0
9:41
강남 라켓클럽
MediaBand the photo is unchanged; the solid header goes dark
이서연2시간 전
다음 주 정기 모임은 시간이 한 시간 당겨졌어요.
오전 8시에 올림픽공원 4번 코트에서 만나요.
12
3
PostRow the photo keeps its colours
박지훈
내일 몇 시에 모여요?
9시 코트 앞이에요
오후 2:14
오후 2:21
좋아요, 내일 봐요
내일 봐요
Message + Composer mine #232824 + white, theirs the surface
2026년 9월
일
월
화
수
목
금
토
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Calendar selected = ink #DFE4DC fill, today ring, the band #000000
2위
박지훈
1,547
3.5
1위
이서연
1,623
4.0
3위
최유나
1,498
3.0
Podium medal rings unchanged
1세트
2세트
3세트
김민준, 이서연
6
7
박지훈, 최유나
4
6
2세트 타이브레이크, 진 팀의 점수
5
빠른 입력
6-06-16-26-36-47-57-6
ScoreEntry the focus border is the dark ink
LIVE코트 2
김민준, 이서연6340
박지훈, 최유나4230
팀 A 포인트
팀 B 포인트
LiveScore #232824 block, the ball numerals unchanged
이서연30,000원
9월 17일 입금 완료 신청
최유나30,000원
9월 회비
김민준30,000원
내 회비는 다른 운영진이 확인해요
Select mode checked = ink fill + a canvas check
미납09:41 남음
카카오뱅크 3333-01-1234567 김총무
시간이 지나 자리가 풀렸어요
다시 신청
CountdownNotice warning #E2B13C and danger #FA9B94 with ink text
BText scalingaddendum B6: follow the OS from 85% to 200%; five classes with caps; every fixed height is a floor; from 1.5 the layouts stack
Each row is one component at 100 / 130 / 150 / 200% (the board's four stops: 130 is a scale Android ships a conversion curve for and sits near iOS xxxLarge; 150 is the stacking pivot; 200 is the cap). Sizes and line heights are the base × min(scale, cap) of the component's class; boxes are floors plus padding, so a height is what the content makes it. The caption of each frame prints the height derived from the addendum's formulas, the addendum's table value and the height measured in the DOM (± 1 pt). The stacked layouts appear at 150 and 200 and not at 130. Linear stops are the conservative envelope: Android's non-linear curve sits at or below them from 12 sp up at 200% (B6.2).
ClassCapiOS dynamicTypeRampRoles100% → 130% → 200% (size/line)
reading2.0headline, subheadline, footnote, caption1row L1 / L2, cell, input text, button labels lg / md, notice, toast, dialog, day header, section title, block eyebrow and meta15/22 → 19.5/28.6 → 30/44
hero1.7title1detail title, identity name24/32 → 31.2/41.6 → 40.8/54.4
numeral1.5largeTitleblock numeral (display 40/48), stat-strip numeral (24/32)40/48 → 52/62.4 → 60/72
label1.5footnote, subheadlineTag, level numeral, Choice, small button, section action13/18 → 16.9/23.4 → 19.5/27
chrome1.3title3, headline, subheadline, caption2tab-root header title (20/28), pushed header title (17/24), scope tab label, FAB label, tab-bar label (11/14), count tag, chart labels20/28 → 26/36.4 → 26/36.4
The 200% frame measures each class at its cap (computed style)
일정 목록
reading 15/22 × 2 = 30/44
강남 라켓클럽
hero 24/32 × 1.7 = 40.8/54.4
1,482
numeral 40/48 × 1.5 = 60/72
마감 임박
label 13/18 × 1.5 = 19.5/27
경기
chrome 20/28 × 1.3 = 26/36.4
MediaRowmin 80 · pad 12 · thumb 56 stays · L1 17/24·600 + 4 + L2 15/22 (wraps, up to 3 lines) · the trailing Tag centred on L1; from 1.5 it moves under L2, 8 below, left-aligned to the text column
09:00–11:00
마감 임박
올림픽공원복식
100% derived 80 · table 80 · measured 80
09:00–11:00
마감 임박
올림픽공원복식
130% derived 87.8 · table 87.8 · measured 87.8
09:00–11:00
올림픽공원복식
마감 임박
150% derived 138 · table 138 · measured 138
09:00–11:00
올림픽공원복식
마감 임박
200% derived 161 · table 161 · measured 161
09:00–11:00
서울올림픽공원 야외 테니스장 3번 코트
마감 임박
200% · two-line venue derived 205 · table 205 · measured 205
PersonRow (68)min 68 · pad 12 · avatar 40 stays · L1 15/22·600 · L2 13/18·500 · trailing figure 15/22·700; from 1.5 the figure sits under L2, 4 below · names wrap (never cut), 2 lines, 3 when stacked
김민준
30,000원
총무
100% derived 68 · table 68 · measured 68
김민준
30,000원
총무
130% derived 80 · table 80 · measured 80
김민준
총무
30,000원
150% derived 125 · table 125 · measured 125
김민준
총무
30,000원
200% derived 156 · table 156 · measured 156
남궁민수정현우진서연
총무
30,000원
200% · a 10-syllable name Pretendard's Hangul advance is about 0.87 em: ten syllables (260) still fit the 298 text column at 200%; the frame measures what it is
남궁민수정현우진서연하윤
총무
30,000원
200% · a 12-syllable name derived 200 · table 156 · measured 200
Cellmin 48 · pad 13 20 (48 = 22 + 26) · label 15/22 secondary + value 15/22·500; from 1.5 the label sits over the value, 4 apart, the value left-aligned; long values wrap
종목복식
100% derived 48 · table 48 · measured 48
종목복식
130% derived 54.6 · table 54.6 · measured 54.6
종목복식
150% derived 96 · table 96 · measured 96
종목복식
200% derived 118 · table 118 · measured 118
Tag · Level tagTag sm 3 8 pad, 13/18·600 · md 3 10, 15/22·600 · one line, flexShrink 0, label class cap 1.5 · the level circle grows with its numeral: ceil(24 × min(s, 1.5) / 4) × 4 = 24 / 32 / 36 / 36
마감 임박모집 중3.5
100% derived 24 · table 24 · measured 24
마감 임박모집 중3.5
130% derived 29.4 · table 29.4 · measured 29.4
마감 임박모집 중3.5
150% derived 33 · table 33 · measured 33
마감 임박모집 중3.5
200% derived 33 · table 33 · measured 33
Buttonlg 17/24·600 pad 14 20 (52 = 24 + 28) · md 15/22 pad 11 20 (44 = 22 + 22) · sm pad 7 16 (36 = 22 + 14, label class cap 1.5) · min-heights; labels wrap rather than clip
참석하기
취소
공유
100% derived 52 · table 52 · measured 52
참석하기
취소
공유
130% derived 59.2 · table 59.2 · measured 59.2
참석하기
취소
공유
150% derived 64 · table 64 · measured 64
참석하기
취소
공유
200% derived 76 · table 76 · measured 76
Block (home)min 144 · pad 20 · eyebrow 13/18·600 + 8 + numeral 40/48·700 (cap 1.5) + 8 + meta 15/22·500 (wraps to 3 lines) + 20 · the whole block still opens the day
내일, 9월 19일 (토)
2개 일정
첫 시작 09:00
100% derived 144 · table 144 · measured 144
내일, 9월 19일 (토)
2개 일정
첫 시작 09:00
130% derived 170.4 · table 170.4 · measured 170.4
내일, 9월 19일 (토)
2개 일정
첫 시작 09:00
150% derived 188 · table 188 · measured 188
내일, 9월 19일 (토)
2개 일정
첫 시작 09:00
200% derived 208 · table 208 · measured 208
내일, 9월 19일 (토)
2개 일정
첫 시작 09:00올림픽공원
200% · a two-line meta derived 252 · table 252 · measured 252
Header56 at every scale: the title is the chrome class (cap 1.3): 36.4 + 2 × 8 pad = 52.4, under the floor · the action glyphs (24 in 44) do not scale
경기
100% derived 56 · table 56 · measured 56
경기
130% derived 56 · table 56 · measured 56
경기
150% derived 56 · table 56 · measured 56
경기
200% derived 56 · table 56 · measured 56
Tab bar56 + the 34 inset at every scale · icons 24 · label 11/14, Android: chrome class cap 1.3 (a custom tabBarLabel); iOS keeps the label at 11 (allowFontScaling false) and offers the Large Content Viewer
홈
클럽
경기
기록
프로필
100% derived 90 · table 90 · measured 90
홈
클럽
경기
기록
프로필
130% derived 90 · table 90 · measured 90
홈
클럽
경기
기록
프로필
150% derived 90 · table 90 · measured 90
홈
클럽
경기
기록
프로필
200% derived 90 · table 90 · measured 90
Dialog350 · pad 20 · r16 · title 17/24·600 · body 15/22 secondary · the pair side by side; from 1.5 stacked, the primary on top (HIG Alerts), both lg
참석을 취소할까요?시작 24시간 전까지는 참가비가 전액 환불돼요.
닫기
참석 취소
100% derived 166 · measured 166
참석을 취소할까요?시작 24시간 전까지는 참가비가 전액 환불돼요.
닫기
참석 취소
130% derived 215.6 · measured 215.6
참석을 취소할까요?시작 24시간 전까지는 참가비가 전액 환불돼요.
닫기
참석 취소
150% derived 306 · measured 306
참석을 취소할까요?시작 24시간 전까지는 참가비가 전액 환불돼요.
닫기
참석 취소
200% derived 364 · measured 364
Strip · Roster · NoticeStrip: 3 columns kept (numeral cap 1.5), labels wrap · Roster 4-up → 2-up from 1.5 · Notice (one line, no action): min 48 — with an action it drops under the text from 1.5
47경기
68%승률
92%출석
김민준
3.5
이서연
4.0
박지훈
3.5
최유나
3.0
오프라인이에요
100% derived 80 · table 80 · measured 80
47경기
68%승률
92%출석
김민준
3.5
이서연
4.0
박지훈
3.5
최유나
3.0
오프라인이에요
130% derived 93 · table 93 · measured 93
47경기
68%승률
92%출석
김민준
3.5
이서연
4.0
박지훈
3.5
최유나
3.0
오프라인이에요
150% derived 103 · table 103 · measured 103
47경기
68%승률
92%출석
김민준
3.5
이서연
4.0
박지훈
3.5
최유나
3.0
오프라인이에요
200% derived 112 · table 112 · measured 112
English at 200% and the 320 reflow reference
The English strings are the longer ones ("Waitlisted", "Doubles NTRP 3.0–3.5"); a 320-wide frame at 200% is the WCAG 1.4.10 reference: no horizontal scroll, nothing clipped.
9:00–11:00 AM
Olympic Park Tennis CourtsDoublesNTRP 3.0–4.0
Waitlisted
LevelDoubles NTRP 3.0–3.5
Request to join
390 · 200% · English Waitlisted wraps under the row; the value wraps under the label
9:00–11:00 AM
Olympic Park Tennis CourtsDoubles
Waitlisted
남궁민수정현우진서연
총무
30,000원
LevelDoubles NTRP 3.0–3.5
Request to join
320 · 200% · reflow no horizontal scroll: scrollWidth = clientWidth; the ten-syllable name wraps here, no ellipsis
CMotionaddendum B7: timing on effects, springs only for the press and the tab indicator; no layout animation in lists; reduced motion has one table
One row per motion. The three frames are the start, the middle and the end of the duration; the middle values are computed from the curve at half the time (cubic-bezier for timing, the damped oscillator for the springs), and printed under each frame. The magenta dot on a plot is where the middle frame was taken. Nothing on this board animates: every frame is frozen. The Reduced column follows B7.7: translate, scale and layout are removed; opacity cross-fades become instant; the press keeps its dim; loops freeze on a static frame; gesture-tracking motion is unchanged; platform transitions belong to the system. No motion adds a tap or a wait to the RSVP, pay and dues flows: each frame sequence accepts input while it plays.
Duration tokenmsUsed for
pressIn80press dim in
pressOut150press dim out
exitFast100edge hairline, hover, menu out, toast out, Switch track, Choice, FAB label out
fast150skeleton → content, header title, FAB label in, menu in, Notice dismiss, image fade, Switch thumb
toggle · modalExit200chevron, FAB width, Notice height · sheet and dialog exit
entrance · reveal250RevealStack, ExpandReveal, wizard fill, toast in
modal · slow300sheet rise · ProgressBar fill
shimmer · pulseDot800 · 1600loops, 5 s per mount (WCAG 2.2.2)
Easings: standard (0.2, 0, 0, 1) anything that stays on screen · enter (0, 0, 0, 1) things entering · exit (0.3, 0, 1, 1) things leaving. Retired: standard 220, exitStandard 180, liveDot, badgePulse, the list stagger.
M01
Press
every Pressable: the settle on the UI thread, the dim beside it; row .98 / .80, button .97 / .92, pill .96 / .85, icon .92 / .78, fab .96 / .90
0 ms · rest
80 ms · scale 0.985, dim in
247 ms · settled .98 / .80
dim 80 in / 150 out · `pressIn` / `pressOut` springs
spring {20, 400, .7}: settles 247 ms, overshoot 9.5%; release {15, 300, .7}: 377 ms, 14.8%
at 80 ms the spring is 75% of the way
Reduced: press feedback
the dim only, no settle
`useReducedMotion()` drops the scale and keeps the dim (existing physics, unchanged).
M02
Hover (web)
a real pointer over a pressable
0 ms
50 ms · opacity 0.868
100 ms · opacity .85
100 ms · `fadeFast`
cubic-bezier(0.2, 0, 0, 1): standard; no scale
Reduced: opacity cross-fade
instant
the dim to .85 is immediate.
M03
Focus ring (web)
keyboard focus lands on a control
before focus
no in-between frame: the ring is visible the moment focus lands
0 ms · 2px ink ring, offset 2
0 ms · none
never animates (WCAG 2.4.7: it must be visible at once)
Reduced: gesture-tracking
unchanged: it mirrors the finger
identical in both modes.
M04
Scope-tab switch
a tap on another scope tab: the pane swaps at once (useDeferredValue), the underline slides
예정매치 찾기테니스장
0 ms · under 예정
예정매치 찾기테니스장
155 ms · 93% of the way
예정매치 찾기테니스장
311 ms · under 매치 찾기
≈ 311 ms · `navSlide` spring
spring {22, 280, .8}: settles 311 ms, overshoot 3.2%
Reduced: translate / scale / layout
removed: the end state appears at once
the underline jumps to the new tab.
M05
Sheet · in and out
a ModalPanel opens from the bottom (the ActionSheet, every edit sheet)
0 ms · y +48, scrim 0
150 ms · 89% risen
300 ms · at rest, scrim .4
300 in · 200 out · `sheetIn` · `modal` / `modalExit`
in: cubic-bezier(0, 0, 0, 1) (enter) · out: (0.3, 0, 1, 1) (exit) · the scrim fade is the platform Modal's
Reduced: translate / scale / layout
removed: the end state appears at once
no rise: the sheet appears with the platform fade only (existing).
M06
Dialog
a confirm opens in the middle
start
mid (illustrative)
end · no scale at any point
platform · RN Modal `animationType: fade`
not tunable: Android `config_shortAnimTime` accelerate, iOS system; the library's old 150 ms is dropped (F8)
Reduced: platform transition
the system decides
iOS cross-fade, Android scale 0: the system decides.
M07
Menu
the ⋯ of a header opens a Menu under it (≤ 5 rows)
0 ms · opacity 0
75 ms · opacity 0.88
150 ms · opacity 1
150 in · 100 out · `fade` in · `fadeFast` out
standard (0.2, 0, 0, 1); no translate, no scale; anchored under the trigger
Reduced: opacity cross-fade
instant
the Menu appears and goes at once.
M08
Toast
a mutation outcome: rise 12 and fade in, fade out only; drag follows the finger
0 ms · y +12, opacity 0
125 ms · y +1.3, opacity 0.89
250 ms · at rest
250 in · 100 out · `reveal` in · `exitFast` out
enter (0, 0, 0, 1) for the middle values; exit is a fade (the exit style drops y: 12); release on the pressOut spring
stays 4000 ms (success, info), 6000 (warning, error), 8000 (with an action); a × on error and action toasts, a tap dismisses
Reduced: translate / scale / layout
removed: the end state appears at once
no rise: it fades in at once and out at once; the drag still tracks the finger.
M09
Scroll-linked header title
the hero title's bottom edge passes the header's bottom edge (hysteresis 24)
0 ms · transparent, no title
75 ms · white 0.88
150 ms · white, title in, hairline
150 ms · `fade`
standard (0.2, 0, 0, 1); title and the solid layer fade together (replaces the `quick` spring on three layers)
Reduced: opacity cross-fade
instant
the header flips at once.
M10
Edge hairline
content passes under a sticky bar: on above offset 8, off at 0
0 ms · offset 8, no line
50 ms · α 0.88
100 ms · #D4D4D4 line
100 ms · `fadeFast`
standard (0.2, 0, 0, 1); an 8-pt hysteresis band
Reduced: opacity cross-fade
instant
the line appears and goes at once.
M11
FAB collapse and expand
120 pt of cumulative downward scroll collapses; 24 pt up (or offset ≤ 24) expands; hides behind the keyboard
매치
0 ms · extended 100 × 48
100 ms · label out, width 54
200 ms · circle 48
label 100 · width 200 · `exitFast` · `toggle`
width: standard (0.2, 0, 0, 1) · the only layout property animated; expand reverses with the label in at 150
Reduced: translate / scale / layout
removed: the end state appears at once
an instant swap between the pill and the circle.
M12
Switch
a tap toggles; the toggle itself is the feedback
0 ms · off
75 ms · thumb 88%, track 98%
150 ms · on
thumb 150 · track 100 · `fade` · `exitFast`
standard (0.2, 0, 0, 1) for both
Reduced: opacity cross-fade
instant
the thumb and the track jump.
M13
Choice · checkbox · radio
a selection changes: the fill changes, the glyph appears at once
복식
0 ms · unselected
복식
50 ms · fill 88%
복식
100 ms · selected
100 ms · `exitFast`
standard (0.2, 0, 0, 1); NN/g: about 100 ms for a checkbox
Reduced: opacity cross-fade
instant
the fill changes at once.
M14
ExpandToggle
a disclosure: the chevron turns 180°, the content reveals sliding 8
더보기
0 ms · closed
더보기
chevron 158° · reveal 89%
더보기
250 ms · open
chevron 200 · reveal 250 · `toggle` · `reveal`
chevron: standard timing (was the `quick` spring); reveal: enter, sliding 8
Reduced: translate / scale / layout
removed: the end state appears at once
the chevron and the content are at their end state at once, no slide (existing).
M15
Notice dismiss
the × of a Notice: opacity first, then the height collapses
0 ms
100 ms · faded, height still held
250 ms · the row moved up
100 + 150 ms · `exitFast` + `fast`
opacity 100, then the height 150, standard (0.2, 0, 0, 1)
Reduced: opacity cross-fade
instant
the Notice is gone at once.
M16
Skeleton to content
a load ends; the skeleton shows only after 400 ms of loading and stays ≥ 400 ms once shown
0 ms · skeleton
75 ms · 12% skeleton
150 ms · content
150 ms · shimmer 800 half-cycle · `fade` · `shimmer`
standard (0.2, 0, 0, 1); the shimmer pulse .6 ↔ .9 runs 5 s per mount, then rests on the static frame
Reduced: loop
a static frame
static at .75, an instant swap to the content; after 10 s the slow-load Notice (proposal).
M17
Wizard step
the next field reveals in place; the progress line fills
0 ms · 2 of 6
125 ms · line 48%
250 ms · 3 of 6
250 ms · `reveal` · `entrance`
reveal for the field; the line fills on standard; no stagger
Reduced: translate / scale / layout
removed: the end state appears at once
the field and the line are at their end state at once.
M18
Image load
a photo finishes loading over its placeholder
0 ms · placeholder pulse
75 ms · 88% photo
150 ms · photo
150 ms · `fade`
standard (0.2, 0, 0, 1); the placeholder pulses .6 ↔ .9 until then
Reduced: loop
a static frame
a static placeholder, the photo swaps in at once.
M19
Live dot
LIVE tag: a 6px dot pulses for 5 s per mount, then rests solid (WCAG 2.2.2)
LIVE
0 ms · ring at the dot, α .45
LIVE
800 ms · ring ×1.7, α .22
LIVE
1600 ms · ring ×2.4, α 0
1600 ms loop · 5 s cap · `pulseDot`
linear loop; after 5 s the static frame (dot only) stays; the text LIVE carries the state
Reduced: loop
a static frame
the dot is solid from the start; no ring.
M20
RSVP move · in a stack
your own RSVP moves you from 예비 to 참석 inside a short fixed stack (≤ 24 children)
참석 2
A
B
예비 1
C
0 ms · C waits
참석 2
A
B
C
예비 0
C
100 ms out · 50 ms · 150 ms in
참석 3
A
B
C
예비 0
300 ms · C is in 참석
out 100 · in 150 · glide 200 · `fadeFast` · `fade` · LinearTransition
the mover fades out 100, in 150 after 50; siblings glide 200 on standard; counts update instantly; the success haptic and the toast announce it
Reduced: translate / scale / layout
removed: the end state appears at once
no glide: the mover and the siblings are at their end place at once.
M21
RSVP move · in a virtualized list
the same move inside FeedList / FlashList / FlatList (members, sessions, ranking)
A
B
C
0 ms · before
no frame between: data changes apply in one frame; no entering, exiting, layout or stagger
A
C
B
next frame · after
0 ms (snap) · none
views recycle, so per-item entrances re-fire on scroll (canon, FlashList); someone else's change (poll, push) never animates either; a lint rule rejects entering / exiting / layout inside item renderers
Reduced: gesture-tracking
unchanged: it mirrors the finger
identical: the list already snaps.
M22
New message bubble
a bubble arrives in the thread (A6)
0 ms · y +8, opacity 0
75 ms · y +0.9, 0.89
150 ms · at rest
150 ms · `fade`
enter (0, 0, 0, 1); the thread stays bottom-anchored (inverted list: only the new bubble animates)
Reduced: translate / scale / layout
removed: the end state appears at once
the bubble is simply there.
M23
Score and counter change
a live point, 4/8 → 5/8, a Block numeral on refresh
30
0 ms · 30
40
B7.6: the next frame · 40 (instant)
3040
library §14: 75 ms · 50 / 50 cross-fade
0 ms (B7.6) · 150 ms (§14) · none · `fade`
an open conflict: the addendum says scores update instantly (the value IS the message, AnimatedCount precedent), the LiveScore row says a 150 ms numeral cross-fade
Reduced: opacity cross-fade
instant
instant in both.
M24
Platform and gesture-tracking
screen push and pop · bottom tab switch · pull to refresh · chart scrub
push / pop: the native stack default (iOS slide + the interactive swipe; Android 13+ translate 10% over 450 ms with an 83 ms fade); not tunable
tab switch: none (the fork's animation is 'none'); pull to refresh: the native control; chart scrub: the marker follows the finger
nothing here is drawn as a frame: these are the platform's or the finger's, never timed by the app
platform / 0 ms · none
450 ms is the platform's and is accepted; gesture tracking has no duration
Reduced: platform transition
the system decides
the system decides (iOS cross-fade, Android scale 0); gesture tracking is unchanged.
DAccessibilityaddendum B8: one focus stop per row with a spoken name built from the row's own model; exactly one visible h1; state in aria-*, never in the name
Legend. A dashed magenta box is one accessibility element (one focus stop); the number is its place in the focus order (header, scope tabs, content, FAB, CTA band, tab bar); a green H1 or H2 marks a heading and its level; a grey hidden or aria-hidden tag marks what the screen reader skips. The name is composed beside the visible formatter from the same view-model: urgent status word first, then when, where, kind, rating, count and last the viewer's own state, joined with a comma and a space; symbols and 24-hour digits are replaced by their spoken forms. The Korean strings are the addendum's patterns; how a Korean voice pronounces them is unverified until a device run.
09:00–11:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
1
MediaRow · session one stop; thumb, count and the MetaBreak are hidden
Role
button (a link on web)
Name
"9월 19일 토요일 오전 9시부터 11시까지, 올림픽공원 테니스장, 복식, NTRP 3.0부터 4.0, 8명 중 4명 참석"
State
aria-selected in pickers; never in the name
Stops
1
20:00–22:00
마감 임박
반포 한강 테니스장혼복NTRP 3.5–4.5
1
MediaRow · urgent the urgent word leads
Role
button
Name
"마감 임박, 9월 19일 토요일 오후 8시부터 10시까지, 반포 한강 테니스장, 혼복, NTRP 3.5부터 4.5"
State
—
Stops
1
김
김민준
3.5
총무
1
김
김민준
30,000원
9월 회비
2
3
김
김민준
1,4823.5
52경기
3
PersonRow · member · dues · ranking name, role, rating · name, amount, state · rank first
Name
"김민준, 총무, NTRP 3.5" · "김민준, 회비 30,000원, 미납" · "3위, 김민준, ELO 1,482, NTRP 3.5"
2nd stop
a real control gets its own stop named for its owner: "김민준 메뉴"
Stops
1 per row (2 with a ⋯)
종목복식
1
프로필 공개
클럽 멤버에게만
2
경기 알림
3switch · on
Cell · static · navigation · toggle label then value; the value joins a navigation name; a switch's name never changes
Name
"종목, 복식" (text group) · "프로필 공개, 클럽 멤버에게만" (button) · "경기 알림" (switch)
State
the switch: aria-checked (on); the name stays "경기 알림"
Stops
1 per cell
내일, 9월 19일 (토)
2개 일정
첫 시작 09:00올림픽공원
1
ELONTRP 3.5–4.0
1,482
마스터까지 518점
2progress: hidden
김
김민준3.5
강남 테니스 클럽3년 차
3
4
Block · home · ELO · identity a block is one stop; the progress bar is hidden; the edit pencil is its own stop
Name
"내일 9월 19일 토요일, 일정 2개, 첫 시작 오전 9시, 올림픽공원"
Name
"ELO 1,482, NTRP 3.5부터 4.0, 마스터까지 518점 남음" · "김민준, NTRP 3.5, 강남 테니스 클럽 3년 차" + "프로필 수정"
Stops
1 each (identity: 2, the pencil 프로필 수정 is its own)
47총 경기
1
68%승률
2
92%출석률
3
Strip a column is a text group: label, then value; a button when it links
Name
"총 경기 47" · "승률 68퍼센트" · "출석률 92퍼센트"
State
—
Stops
3, one per column
Session detail: the focus order and the outline
Back · (hidden header title) · eyebrow · h1 hero title · three facts · h2 참석 · roster · h2 정보 · Cells · the CTA band (B8.3 detail template). Seventeen stops, three headings; the FAB and the tab bar do not exist on a detail page.
9:41
1
토요일 올림픽공원 복식aria-hidden until it shows
강남 라켓클럽
2
토요일 올림픽공원 복식
3H1
9월 19일 (토) 09:00–11:00
4
올림픽공원 테니스장
5
참가비 10,000원
6
참석
7H2
김민준
3.5
8
이서연
4.0
9
박지훈
3.5
10
최유나
3.0
11
정보
12H2
종목복식
13
대상NTRP 3.0–4.0
14
호스트김민준
15
참가비 10,000원시작 24시간 전까지 무료 취소
참석하기
16
17
the stack at 1:1 the CTA band carries the helper (A3)
#ElementRoleSpoken nameNote
1Header backbutton뒤로—
·Header titleheading, hiddenaria-hidden until the hero title scrolls under the header (then it shows)the opacity-zero layer is not enough on iOS: the flag is explicit
2Eyebrowtext강남 라켓클럽—
3Hero titleheading, level 1토요일 올림픽공원 복식the one visible h1 of the screen
4Fact: whentext9월 19일 토요일 오전 9시부터 11시까지spoken form of 9월 19일 (토) 09:00–11:00
5Fact: wheretext올림픽공원 테니스장opens the map as a link when it is one
6Fact: feetext참가비 10,000원—
7Section 참석heading, level 2참석, 8명 중 6명, 2자리 남음, 예비 2명the group head carries the count (spotsLeft); empty slots are hidden
8 – 11Roster cellsbutton김민준, NTRP 3.5 · 이서연, NTRP 4.0 · 박지훈, NTRP 3.5 · 최유나, NTRP 3.0one stop per cell; the level circle and the avatar are hidden
12Section 정보heading, level 2정보—
13Cell: 종목text group종목, 복식label then value
14Cell: 대상text group대상, NTRP 3.0부터 4.0the range is spoken, not read as symbols
15Cell: 호스트text group호스트, 김민준—
16CTA helpertext참가비 10,000원, 시작 24시간 전까지 무료 취소a text stop before the button; the MetaBreak is never spoken
17CTA buttonbutton참석하기the visible label (WCAG 2.5.3); aria-busy while loading, the name does not change
Heading outline per template (B8.3)
Hub, 홈
h1 홈 · block (button) · h2 내일 일정 and its 전체 보기 · rows · h2 이번 주 · rows · FAB 일정 만들기 · tab bar
List, 경기
h1 경기 · tablist 예정, 매치 찾기, 테니스장 · h2 per day header · rows · FAB · tab bar
Records, 기록
h1 기록 · ELO block · tablist · Strip columns · h2 최근 ELO 추이 · chart · h2 클럽 순위 · rows
Profile, 프로필
h1 프로필 · identity block and 프로필 수정 · Strip · h2 친구 24명 · avatar group · h2 최근 경기 · rows · h2 업적
Detail, session
back · (hidden header title) · eyebrow · h1 hero title · three facts · h2 참석 · roster · h2 정보 · Cells · CTA band
Form, wizard
back and skip · progress "4단계 중 2단계" · h1 step title · fields (label, control, helper) · CTA band
Sheet, dialog
title heading (h2 on web inside dialog) · content · actions, least destructive first
EPlatformaddendum B9: edge-to-edge canvas under the system bars; the bottom chrome adds the inset; the offsets are 16 steps
Android targeting SDK 35+ is edge-to-edge on Android 15 and later, and for apps targeting 36 the opt-out is disabled: the app draws under both system bars and pads its own bottom chrome by the runtime inset. The magenta brackets at the left edge are the measured offsets (the DOM script reads the frames). Gesture navigation is transparent; 3-button navigation gets a scrim coloured by the system theme, so an app theme that differs from the system theme can show a contrast seam under the tab bar (Q5, drawn below; fix is native).
ElementPosition (B9.6)Notes
Tab barheight 56 + insets.bottom; the canvas fill continues under the system barpublishes 56 + inset to the chrome store; today's extra +8 goes ((tabs)/_layout.tsx:47,75-76)
CTA band16 + 52 + 16 + insets.bottom; the hairline only while content lies belowscreens without a tab bar; the code today pads max(16, inset) below, v2 adds the inset
FABright 20; bottom = tab-bar top + 16 on tab screens, insets.bottom + 16 elsewhereone per screen
Toastcentred; bottom = max(top of the bottom chrome, top of the keyboard) + 16; above the FAB when present: FAB top + 16TOAST_CHROME_GAP 8 becomes 16 (library K1)
Sheetpadding bottom 20 + insets.bottomexisting (ModalPanel); add navigationBarTranslucent
Headerthe status inset on top of the 56topInset
iPhone · home indicatorstatus 44 (47 on a 390 pt iPhone) · home indicator inset 34 · tab bar 56 + 34 = 90
9:41
경기
9월 19일 (토)내일
09:00–11:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
20:00–22:00
마감 임박
반포 한강 테니스장혼복NTRP 3.5–4.5
19:00–21:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
9월 20일 (일)모레
21:00–23:00
올림픽공원 테니스장복식NTRP 3.0–4.0
07:00–09:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
홈
클럽
경기
기록
프로필
매치
참석했어요
되돌리기
34
56
16
16
tab screen · light header, rows, FAB, toast, tab bar
9:41
경기
9월 19일 (토)내일
09:00–11:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
20:00–22:00
마감 임박
반포 한강 테니스장혼복NTRP 3.5–4.5
19:00–21:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
9월 20일 (일)모레
21:00–23:00
올림픽공원 테니스장복식NTRP 3.0–4.0
07:00–09:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
홈
클럽
경기
기록
프로필
매치
참석했어요
되돌리기
tab screen · dark the same markup, the dark values
9:41
강남 라켓클럽
토요일 올림픽공원 복식
9월 19일 (토) 09:00–11:00
올림픽공원 테니스장
참가비 10,000원
종목복식
대상NTRP 3.0–4.0
호스트김민준
참석하기
참석했어요
되돌리기
34
84
16
CTA screen · light header, content, toast, CTA band (no tab bar)
9:41
강남 라켓클럽
토요일 올림픽공원 복식
9월 19일 (토) 09:00–11:00
올림픽공원 테니스장
참가비 10,000원
종목복식
대상NTRP 3.0–4.0
호스트김민준
참석하기
참석했어요
되돌리기
CTA screen · dark ball and ink text stay; the band canvas goes dark
Android · gesture navigationstatus 24 · gesture bar inset 24 (nominal) · tab bar 56 + 24 = 80 · transparent navigation bar, the canvas shows through
9:41
경기
9월 19일 (토)내일
09:00–11:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
20:00–22:00
마감 임박
반포 한강 테니스장혼복NTRP 3.5–4.5
19:00–21:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
9월 20일 (일)모레
21:00–23:00
올림픽공원 테니스장복식NTRP 3.0–4.0
07:00–09:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
홈
클럽
경기
기록
프로필
매치
참석했어요
되돌리기
24
56
16
16
tab screen · light header, rows, FAB, toast, tab bar
9:41
경기
9월 19일 (토)내일
09:00–11:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
20:00–22:00
마감 임박
반포 한강 테니스장혼복NTRP 3.5–4.5
19:00–21:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
9월 20일 (일)모레
21:00–23:00
올림픽공원 테니스장복식NTRP 3.0–4.0
07:00–09:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
홈
클럽
경기
기록
프로필
매치
참석했어요
되돌리기
tab screen · dark the same markup, the dark values
9:41
강남 라켓클럽
토요일 올림픽공원 복식
9월 19일 (토) 09:00–11:00
올림픽공원 테니스장
참가비 10,000원
종목복식
대상NTRP 3.0–4.0
호스트김민준
참석하기
참석했어요
되돌리기
24
84
16
CTA screen · light header, content, toast, CTA band (no tab bar)
9:41
강남 라켓클럽
토요일 올림픽공원 복식
9월 19일 (토) 09:00–11:00
올림픽공원 테니스장
참가비 10,000원
종목복식
대상NTRP 3.0–4.0
호스트김민준
참석하기
참석했어요
되돌리기
CTA screen · dark ball and ink text stay; the band canvas goes dark
Android · 3-button navigationstatus 24 · 3-button bar inset 48 (nominal, typically taller) · tab bar 56 + 48 = 104 · a translucent scrim in the system theme with back / home / recents
9:41
경기
9월 19일 (토)내일
09:00–11:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
20:00–22:00
마감 임박
반포 한강 테니스장혼복NTRP 3.5–4.5
19:00–21:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
9월 20일 (일)모레
21:00–23:00
올림픽공원 테니스장복식NTRP 3.0–4.0
07:00–09:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
홈
클럽
경기
기록
프로필
매치
참석했어요
되돌리기
48
56
16
16
tab screen · light header, rows, FAB, toast, tab bar
9:41
경기
9월 19일 (토)내일
09:00–11:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
20:00–22:00
마감 임박
반포 한강 테니스장혼복NTRP 3.5–4.5
19:00–21:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
9월 20일 (일)모레
21:00–23:00
올림픽공원 테니스장복식NTRP 3.0–4.0
07:00–09:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
홈
클럽
경기
기록
프로필
매치
참석했어요
되돌리기
tab screen · dark the same markup, the dark values
9:41
강남 라켓클럽
토요일 올림픽공원 복식
9월 19일 (토) 09:00–11:00
올림픽공원 테니스장
참가비 10,000원
종목복식
대상NTRP 3.0–4.0
호스트김민준
참석하기
참석했어요
되돌리기
48
84
16
CTA screen · light header, content, toast, CTA band (no tab bar)
9:41
강남 라켓클럽
토요일 올림픽공원 복식
9월 19일 (토) 09:00–11:00
올림픽공원 테니스장
참가비 10,000원
종목복식
대상NTRP 3.0–4.0
호스트김민준
참석하기
참석했어요
되돌리기
CTA screen · dark ball and ink text stay; the band canvas goes dark
All layers at oncestatus bar, header, content, FAB, toast, CTA band and tab bar in one 390 × 844 frame: a measuring diagram, since a tab screen has no CTA band and a detail screen no tab bar; it is the worst case the toast and the FAB must clear
9:41
경기
9월 19일 (토)내일
09:00–11:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
20:00–22:00
마감 임박
반포 한강 테니스장혼복NTRP 3.5–4.5
19:00–21:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
9월 20일 (일)모레
21:00–23:00
올림픽공원 테니스장복식NTRP 3.0–4.0
07:00–09:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
홈
클럽
경기
기록
프로필
참석하기
매치
참석했어요
되돌리기
34
56
84
16
16
iPhone · home indicator · all layers tab bar 56 + 34, band 84 above it, FAB and toast 16 apart
9:41
경기
9월 19일 (토)내일
09:00–11:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
20:00–22:00
마감 임박
반포 한강 테니스장혼복NTRP 3.5–4.5
19:00–21:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
9월 20일 (일)모레
21:00–23:00
올림픽공원 테니스장복식NTRP 3.0–4.0
07:00–09:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
홈
클럽
경기
기록
프로필
참석하기
매치
참석했어요
되돌리기
24
56
84
16
16
Android · gesture navigation · all layers tab bar 56 + 24, band 84 above it, FAB and toast 16 apart
9:41
경기
9월 19일 (토)내일
09:00–11:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
20:00–22:00
마감 임박
반포 한강 테니스장혼복NTRP 3.5–4.5
19:00–21:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
9월 20일 (일)모레
21:00–23:00
올림픽공원 테니스장복식NTRP 3.0–4.0
07:00–09:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
홈
클럽
경기
기록
프로필
참석하기
매치
참석했어요
되돌리기
48
56
84
16
16
Android · 3-button navigation · all layers tab bar 56 + 48, band 84 above it, FAB and toast 16 apart
Keyboard open · and the 3-button seamthe keyboard owns the bottom: no inset, no tab bar (tabBarHideOnKeyboard), no FAB; the CTA band rides the keyboard; the focused input keeps 16 clear above the band
9:41
클럽 만들기
클럽 이름을 알려주세요
이름
강남 라켓클럽
2–20자로 정해 주세요
다음
84
16
form screen · keyboard open no tab bar, no FAB, no inset; the band rides the keyboard
9:41
강남 라켓클럽
관리
클럽 소개 수정
소개
매주 토요일 오전, 올림픽공원에서 쳐요
저장
sheet with an input the sheet lifts with the keyboard; the CTA stays visible above it
9:41
경기
9월 19일 (토)내일
09:00–11:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
20:00–22:00
마감 임박
반포 한강 테니스장혼복NTRP 3.5–4.5
19:00–21:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
9월 20일 (일)모레
21:00–23:00
올림픽공원 테니스장복식NTRP 3.0–4.0
07:00–09:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
홈
클럽
경기
기록
프로필
매치
3-button seam · app dark on system light a pale scrim over the dark tab bar: possible contrast seam (Q5, device check)
9:41
경기
9월 19일 (토)내일
09:00–11:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
20:00–22:00
마감 임박
반포 한강 테니스장혼복NTRP 3.5–4.5
19:00–21:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
9월 20일 (일)모레
21:00–23:00
올림픽공원 테니스장복식NTRP 3.0–4.0
07:00–09:00
4/8
올림픽공원 테니스장복식NTRP 3.0–4.0
홈
클럽
경기
기록
프로필
매치
3-button seam · app light on system dark a dark scrim under the white tab bar; the fix is native: enforceNavigationBarContrast false + SystemBars (EAS)
FVerification and judgement callswhat was measured in a real browser, and every place the text could not be drawn exactly as written
1 · Heights of the section 14 components and the shared rows (offsetHeight, Chromium, window 1800)
componentmarkspec (pt)measuredinstanceslibrary row · result
HeaderTextAction targethta444419§14 · pass
Header (tab root, pushed, select mode)header565646§3 · B6: 56 at every scale · pass
ActionSheet at its 80% capas-sheet675.2675.21§14 (0.8 × 844) · pass
ActionSheet Cellas-cell484841§14 · pass
ActionSheet group title lineas-gtitle181814§14 (13/18) · pass
CTA band, no helpercta-h01181181§3 (16 + 52 + 16 + 34) · pass
CTA band, one-line helpercta-h11441449§14 · pass
CTA band, two-line helpercta-h21621621§14 · pass
MediaBand photo (and its skeleton, default cover)mb-photo24024013§14 · pass
MediaBand top scrimmb-scrim10010018§14 · pass
MediaBand overlaid header + statusmb-header10010017§14 (44 + 56) · pass
MediaBand glyph circlephoto-circle363644§14 · pass
MediaBand mapmb-map2402407§19.2 · pass
PostRow author linepost-author444413§14 · pass
PostRow one photo (350 wide)post-photo-12622623§14 (4:3) · pass
PostRow two photos (171 wide)post-photo-21711716§14 · pass
PostRow action rowpost-actions444411§14 · pass
PostRow deletedpost-deleted48481§14 (a 48 row) · pass
DM avataravatar-32323211§13 C13 · pass
date / system linethread-line50506§14 (16 + 18 + 16) · pass
send targetsend44448§14 · pass
send circlesend-circle36368§14 · pass
Composer (min 56 + inset)composermin 56 + inset56 / 90 / 124 / 1688§14: 56 / 90 / 124 / 168 · pass
Calendar month headercal-head44445§14 · pass
Calendar weekday rowcal-wd323214§14 · pass
Calendar day cellcal-cell4444205§14 (50 × 44) · pass
Calendar numeral circlecal-num3636226§14 · pass
Calendar activity dotcal-dot4454§14 · pass
Calendar week stripcal-week64643§14 · pass
Podium (a floor)podium1681689§14 · pass
Podium, a name wrapping in a lower columnpodium-2l1901901§14 + 22 · pass
Podium avatar, 1위pod-av156569§14 · pass
Podium avatar, 2위 / 3위pod-av2484818§14 · pass
ScoreEntry inputscore-in484852§14 · pass
ScoreEntry tiebreak fieldscore-tb36366§14 · pass
LiveScore block (a floor)live-block144144 / 15612§14 · pass
LiveScore team row (a floor)live-row3232 / 4422§14 · pass
LiveScore serving dotlive-dot6611§14 · pass
Select-mode rowsel-row686840§14 + §15 · pass
Select-mode checkboxcheckbox242434§14 · pass
CountdownNotice, runningcd-notice-268686§14 · pass
CountdownNotice, expiredcd-notice-148488§14 · pass
PersonRow (shared)row-person68685§5 + §15 · pass
MediaRow (shared)row-media808057§5 · pass
Cell (shared)cell484824§5 · pass
Tab bar itemstabbar565611§3 · pass
FABfab484811§3 · pass
Level taglevel242435§6 · pass
Tag smtag242431§6 · pass
Choicechoice363638§7 · pass
PersonRow and ResultRow (68). Re-measured on the components board after the pitch patch: row-person 68 on all 23 instances (the anatomy, the states, the rank variant, the ResultRow); this board's PersonRow instances (Podium list, a11y overlay, scaling strip) measure 68 too.
2 · Text scaling: the addendum's per-component table → the measured height (± 1 pt)
component100%130%150%200%note
MediaRow80 → 8087.8 → 87.8138 → 138161 → 161
PersonRow68 → 6880 → 80125 → 125156 → 156
Cell48 → 4854.6 → 54.696 → 96118 → 118
Tag sm24 → 2429.4 → 29.433 → 3333 → 33
Tag md28 → 2834.6 → 34.639 → 3939 → 39
Level tag24 → 2432 → 3236 → 3636 → 36
Button lg52 → 5259.2 → 59.264 → 6476 → 76
Button md44 → 4450.6 → 50.655 → 5566 → 66
Button sm36 → 3642.6 → 42.647 → 4747 → 47
Block home144 → 144170.4 → 170.4188 → 188208 → 208
Header56 → 5656 → 5656 → 5656 → 56
Tab bar90 → 9090 → 9090 → 9090 → 90
Dialogderived → 166derived → 215.6derived → 306derived → 364not in the table: derived from the formulas
Strip80 → 8093 → 93103 → 103112 → 112
Roster cell98 → 98111.4 → 111.4119 → 119128 → 128the cell; 2-up from 150%
Notice, one line48 → 4852.6 → 52.657 → 5768 → 68
MediaRow, two-line venue–––205 → 205L2 wraps to two lines
Block home, two-line meta–––252 → 252meta wraps to two lines
PersonRow, long names–––10 syllables → 156 (one line, 156) · 12 syllables → 200 (two lines)wraps, no ellipsis
Frame checks. Clipped text in any scaled frame: 0. Overlapping sibling slots: 0. Frames off the table (or its derived value) by more than 1 pt: 0. Tags on one line at every scale. Header and tab bar unchanged at all four scales. The 320-wide frame at 200%: scrollWidth 320 = clientWidth 320, the widest child ends at 320: no horizontal scroll. The 200% classes, read from the computed style: reading 30/44 · hero 40.8/54.4 · numeral 60/72 · label 19.5/27 · chrome 26/36.4.
3 · The chrome stack: measured offsets in the 390 × 844 frames (B9.6)
framespecbottom chromeFAB above tab barFAB above bandtoast above FABtoast above bandFAB rightstatusheader
iPhone · tab screentab bar 56 + 349016–16–204456
iPhone · CTA screenband 16 + 52 + 16 + 34118–––16–4456
Android gesture · tab screentab bar 56 + 248016–16–202456
Android gesture · CTA screenband 16 + 52 + 16 + 24108–––16–2456
Android 3-button · tab screentab bar 56 + 4810416–16–202456
Android 3-button · CTA screenband 16 + 52 + 16 + 48132–––16–2456
iPhone · all layerstab bar 56 + 34 under a 84 band90 + 84–1616–204456
Android gesture · all layerstab bar 56 + 24 under a 84 band80 + 84–1616–202456
Android 3-button · all layerstab bar 56 + 48 under a 84 band104 + 84–1616–202456
Every offset is the 16 step of B9.6 (FAB 16 above the tab bar or the band, toast 16 above the FAB or the band, FAB 20 from the right rail); the tab bar is 56 + the inset and the band 84 + the inset (84 when it sits over a tab bar). Rulers: overflowing or overlapping notes on this board: 0.
Where the drawing departs from, or has to interpret, the written spec
WhereWhat the spec saysWhat the board draws, and why
§14 countThe section says "eleven things" and lists twelve rows (ScoreEntry and LiveScore are one gap of the IA audit, two rows of the table).All twelve rows are drawn, each as a block.
Fv* and FV_PAL_S2The brief names Fv* primitives and FV_PAL_S2 in the two sibling boards.Those names live in flat-v2.jsx; the sibling boards carry copies (Fq* in components, Fk* in controls). This board copies from the siblings (Fp*) and reads the palette of FQ_LIGHT / FK_LIGHT, which equal FV_PAL_S2.
HeaderTextAction count"At most one per header" (§14) against the Select-mode header, which has 취소 on the left and 전체 선택 on the right.Drawn as the Select-mode row says; the rule should read "at most one per side".
Text beside an iconIA-4 listed [관리] [⋯] (text first) while §14 put the icon first and the text last, on the rail.Resolved by §17 H1 and redrawn: a text action sits left of any icon and the overflow ⋯ is rightmost ([관리] [⋯], [초대 코드] [+]); the rightmost icon takes the rail.
Text over a photo§14 specifies glyph circles over the MediaBand and nothing for a text action.A proposal: white 600 on an ink α0.4 pill 36 tall (the circles' sibling), marked as proposed.
ActionSheet capActionSheet: max 80% of the screen; ModalPanel in the code caps at 88%. Nine actions in three groups plus a destructive group are 866 tall, the 80% sheet 675.2.Drawn at 80%; the body scrolls and 위험 구역 sits below the first screen (shown scrolled to the end). The sheet's own padding (20 + inset = 54) stays under the scroll body.
CTA band bottomThe code pads max(16, inset) below and keeps a permanent top border; §3 and B9.6 say 16 + 52 + 16 + inset with the hairline on scroll only.Drawn as §3 / B9.6: 118, 144 and 162 at inset 34; the change from the code is stated in the helper block.
MediaBand map§14 drew a 160 band under a white header with a 지도 Tag that opened the map app; §19.2 (MAP-BAR-INK, MAP-NO-CHIP) gives it the photo geometry, no Tag, a tap to 장소 상세 and the pin at y 170.Redrawn as §19.2: 240 full-bleed with the scrim and glyph circles, no Tag. Press = dim .85 without a settle and the focus ring inset 2 stay proposals (§16).
PostRow clamp and photos§14: 4-line clamp + 더보기, three or more photos = two squares with +N over the second. The code clamps at 5 and shows three squares.더보기 is drawn inline at the end of the 4th line (the code's trailing slot, a passive hint); the two-square layout as §14.
MessageBubble extras§14 gives rest only: the time "beside the last bubble", no sending or failed face, no keyboard height.The time sits bottom-aligned on the inner side; sending (.6, 보내는 중) and failed (danger glyph, 다시 보내기) are drawn as proposals; the keyboard is a 336 stand-in block.
Calendar colours§14 colours only the weekday row (일 danger, 토 $series1).Numerals stay ink; the activity dots are ink; the week strip carries no dots (the 64 has no room); the range band is flat at the week break.
Podium height168 holds only if the lower columns have one-line names: 16 + 48 + 4 + 18 + 4 + 22 + 4 + 24 + 4 + 24.Drawn as a floor: 168 with one-line names, 190 with a wrapped name in a lower column (182 in the raised 1위 column).
ScoreEntry unknownsThe text does not say which team a quick pick credits, where the tiebreak field sits, nor the label of the set columns.The top team gets the first number (an open call); the tiebreak is a 44 row under the table with the 36 × 36 field at the right; the columns are labelled 1세트 … 3세트.
LiveScore composition§14 gives the 350 × 144 block but not its rows; 144 = 20 + 104 + 20.A LIVE row (24) + two 32 rows + 8s fill the 104, so the block reads as live; AD is wider than its 32 column and overflows centred; a two-line name makes the block a floor (156).
LiveScore vs B7.6§14: a 150 ms numeral cross-fade on a point; B7.6: counters and scores update instantly (the AnimatedCount precedent).An open conflict. Row M23 draws both; the LiveScore block says what §14 says.
Select mode titleHeader: 취소 · "n명 선택됨" · 전체 선택; nothing for when every row is ticked; the viewer's own row and the total come from the IA audit.The title is centred between the two actions; 전체 선택 keeps its label; the own row (disabled, caption) and the 합계 helper are drawn from the IA text.
CountdownNotice last minuteOnly the screen-reader milestones (5:00, 1:00) are specified.No visual change in the last minute; the clock keeps ticking in place.
B6 · ELO BlockThe addendum's table gives ELO 158 / 181.2 / 198 / 216 (the old 6 pt progress); the library ruled 160 with an 8 pt progress.ELO is not in the strip (home Block only); by the library's geometry it would be 160 / 186.4 / 204 / 224.
B6 · long namesMediaRow and PersonRow "with a 10-syllable name (wraps)".Pretendard's Hangul advance is about 0.87 em, so ten syllables (260) still fit the 298 text column at 200%; the strip shows that frame and a 12-syllable name that wraps. React Native on a device may wrap the ten-syllable one: unverified.
B6 · MetaBreakMetaSegments is a wrapping flex row with a break before each fact.At 130% and beyond the break can dangle at the end of the first line (the real component does the same); a fix needs a layout pass: noted, not drawn.
B7 · spring numbersB7.2 prints settle times of 247 / 377 / 311 ms and overshoots of 9.5 / 14.8 / 3.2% for pressIn, pressOut and navSlide.Those are printed as written; the middle frames use the closed-form damped oscillator, which settles 248 / 379 / 313 ms with overshoots of 9.6 / 15.0 / 3.3% (a 1 to 2 ms difference of integration, no visible effect).
B7 · board checksB7 asks for looping demos, a reduce-motion switch and the token tables printed from motion.ts by importing it.Not drawn: this is a static render. Every motion is a frozen frame sequence with computed middle values; the tokens are typed from the addendum.
B8 · speechKorean pronunciation of the spoken forms and how a Korean voice reads 오전 9시 or 8명 중 7명 참석 are unverified until a device run; axe-core is not run on a static board.The Korean names are the addendum's patterns; stops, order and headings are drawn, not tested.
B9 · insetsInsets are read at runtime; the 3-button bar is "typically taller" (unverified).Nominal values drawn: 34 (iPhone), 24 (Android gesture), 48 (3-button); status 44 (47 on a 390 pt iPhone). The tab bar and the CTA band never share a screen, so each platform has a tab frame and a CTA frame.
Resolved 2026-10-09 (owner: go with best practices and your recommendation): library sections 14 and 15 and addendum Q1 to Q10 stand as recommended. Owner's word still needed → adopt the twelve page components and the scaling, motion, accessibility and platform rules of B6 to B9 as the Phase B spec, with the open calls of part F decided?
Spec (grounding, anatomy)

Proposal (2026-10-09), Phase B of the flat grammar v2 migration, the third component board: the twelve page components of docs/design/flat-v2-component-library-2026-10-09.md section 14 (HeaderTextAction, ActionSheet, CTA band helper line, MediaBand, PostRow, MessageBubble + Composer, Calendar, Podium, ScoreEntry, LiveScore, Select mode, CountdownNotice) each drawn as a specimen at 1:1 with anatomy rulers, states, variants, behaviour and used by / replaces, then the four behaviours of docs/design/flat-v2-behaviour-addendum-2026-10-09.md: text scaling at 100 / 130 / 150 / 200% (B6), a frame sequence per motion with its curve and the reduced-motion column (B7), the focus order, spoken names and headings (B8) and the chrome stack on iPhone, Android gesture and 3-button navigation in light and dark (B9), then a DOM verification and every judgement call. Built from local primitives only (Fp*): no WFPhone, WFCard, WFChip, WFBadge, WFSelectChip, WFInput or WFSwitch.

route · packages/ui/src/modal-panel.tsx · header-actions-menu.tsx · bottom-cta-band.tsx · text.tsx · motion.ts · apps/mobile/app/(tabs)/_layout.tsx · packages/features/clubs/src/shared/post-row.tsx · packages/features/messaging/src/dm-thread/compose-bar.tsx · packages/app/src/presentation/components/leaderboard-row.tsx · leaderboard-podium.tsx · activity-metric-calendar.tsx · packages/features/sessions/src/scorecard/score-edit-sheet.tsx · match-card.tsx grounded in 13 source file(s)
packages/ui/src/modal-panel.tsxpackages/ui/src/header-menu.tsxpackages/ui/src/bottom-cta-band.tsxpackages/ui/src/text.tsxpackages/ui/src/motion.tsapps/mobile/app/(tabs)/_layout.tsxpackages/features/clubs/src/shared/post-row.tsxpackages/features/messaging/src/dm-thread/compose-bar.tsxpackages/app/src/presentation/components/leaderboard-row.tsxpackages/app/src/presentation/components/leaderboard-podium.tsxpackages/features/records/src/record-detail/period-calendar.tsxpackages/features/sessions/src/scorecard-page/overlays/score-entry-sheet.tsxpackages/features/sessions/src/scorecard-page/schedule-match-row.tsx