← Cockpit Wireframes Pages by level Navigation map
Library — controls, overlays, states, forms▾
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 — controls and fields
Interactive kit · grounded in code

Library — controls, overlays, states, forms

Owner brief · Phase B of the migration plan
“Design all of the components first, including all their interactive behaviours, so that we have shared components used throughout the app.”
This board is the controls, overlays, states and forms half (library sections 7 to 10) of docs/design/flat-v2-component-library-2026-10-09.md, in scheme S2 Ball and Ink. The chrome, surfaces, rows and labels are the other board, flat-v2-components. Every number is drawn, then measured from the rendered DOM (the last panel).
How to read a specimen
Each component has the same five parts. Anatomy: the component 1:1 on a 390 stage (the phone width; content is 350 on the 20 rail) with pink rulers in pt, dashed boxes where a hit area is bigger than the visual. States: every state that applies, labelled with its applied value; a state that does not apply says why. Variants: sizes, tones, modes. Then the behaviour from library section 2 and what the component replaces. Korean appears only as in-app sample text.
The state vocabulary (library section 2)
restas drawn
pressedsettle + dim: button .97 / .92 · pill .96 / .85 · row .98 / .80 · icon .92 / .78 · spring in {20, 400, .7}, out {15, 300, .7} · dim 80 ms in, 150 ms out
hoveropacity .85, pointer cursor (web only)
focus2px ink ring, offset 2 · inputs: a 2px ink border instead
disabledopacity .5 · not-allowed · inert · aria-disabled
loadingan ink spinner replaces the label at full opacity · width held · aria-busy
selected · on · checkedink fill + a check (Choice) · ink track (Switch) · ink square (checkbox) · aria-checked
error (inputs)a danger border + a danger helper under the field, never a toast
Colour read by the components
canvas#FFFFFF#0F1211
ink#0D100E#DFE4DC
secondary#4F5851#A9B3AB
gray100 / fill#F2F2F2#363E38*
cell hairline#E5E5E5white α0.09
edge hairline#D4D4D4white α0.14
ball#D6F04A#D6F04A
danger#B8332E#FA9B94
light · dark. * derived: accepted, gated at S1 (the dark strip lists every derived value).
Resolved 2026-10-09 · library section 13
Owner: “go with best practices and your recommendation.”
K1 the toast floats 16 above the tab bar and 16 above a FAB (the code's 8 changes). K2 the unchecked checkbox is a 2px #4F5851 edge. K3 one Button size scale for every tone: sm 36 · md 44 · lg 52. K4 the derived dark values are accepted, gated at S1. K5 the toast status glyph stays: 20, before the text, in the dark tones on the ink surface. K6 a ghost Button pads 0 12; the wizard progress line is ink for done + current, #E5E5E5 for the rest.
AControls
Library section 7: the things a person presses, types into or drags. Twelve components; every state that applies is drawn and the ones that do not say why.
7.1ButtonThe action: one primary per region; a tonal sits beside it only in a wizard or a confirm.Controls
Library spec one size scale for every tone: sm 36 · r10 · 15/22·600 · pad 0 16 | md 44 · r12 · 15/22·600 · pad 0 20 | lg 52 · r12 · 17/24·600 · pad 0 20 (the default) | tones: primary = ball fill, ink · tonal = gray100, ink · ghost = no fill, ink text, pad 0 12 at every size so the text edge can land on the rail · danger = #B8332E, white | lg: the CTA band and full-width form submits · md: inline actions (empty states, notices, sheet content) · sm: inside rows and notices | full width on the rail or content width | leading glyph 20 + 8.
Anatomydrawn 1:1 · pink = pt
Primary · full width
lg 52 · r12 · ball · label 17/24·600 ink
참석하기
52r12350 = rail to rail
Content width · leading glyph
lg · pad 0 20 · glyph 20 + 8 · label 17/24·600
일정 만들기
5220208label 77.520
One size scale
sm 36 · md 44 · lg 52, every tone (tonal shown) · r10 / r12 / r12
수정
수정
수정
sm 36md 44lg 52
Ghost · pad 0 12
no fill: the padding is hit area only, so the text can end on the rail
다시 시도
md 4412
Statesevery tone × every state; loading holds the width of the rest button; a state does not depend on the size
Four tones · six states
rest · pressed · hover · focus · disabled · loading
rest
default
pressed
.97 scale · .92
hover
opacity .85
focus
ring 2 · offset 2
disabled
.5
loading
spinner · width held
Primary
lg 52
확인
확인
확인
확인
확인
확인
Tonal
lg 52
확인
확인
확인
확인
확인
확인
Danger
lg 52
삭제
삭제
삭제
삭제
삭제
삭제
Ghost
md 44
다시 시도
다시 시도
다시 시도
다시 시도
다시 시도
다시 시도
Tonal
sm 36
수정
수정
수정
수정
수정
수정
Variantssize × tone · width · glyph · pair
Every tone at every size
primary · tonal · ghost · danger × sm 36 · md 44 · lg 52
sm
36 · r10 · 15/22·600
md
44 · r12 · 15/22·600
lg
52 · r12 · 17/24·600
Primary
확인
확인
확인
Tonal
확인
확인
확인
Ghost
다시 시도
다시 시도
다시 시도
Danger
삭제
삭제
삭제
Where each size is used
lg 52 — the CTA band and full-width form submits.
md 44 — inline actions: empty states, notices, sheet content.
sm 36 — inside rows and notices.
Full width on the rail
three tones, 12 between when stacked
저장
나중에
클럽 삭제
Content width with a glyph
glyph 20 + 8, ink on ball / gray100
일정 만들기
공유하기
수정
Loading holds the width
same box as rest, measured equal
참석하기
참석하기
Pair in a wizard or a confirm
tonal flex 1 + primary flex 2, gap 12
이전
다음
Behaviour (library section 2)
Pressed .97 scale / .92 dim, spring in {20, 400, .7}, out {15, 300, .7}, dim 80 ms in / 150 ms out, on the UI thread. Hover .85 + a pointer cursor (web). Focus ring 2px ink, offset 2; the ball button gets the same ink ring. Disabled .5, not-allowed, inert, aria-disabled. Loading: an ink spinner replaces the label at full opacity, the width is held (the label stays in flow, hidden), aria-busy, presses ignored. Reduced motion drops the settle and keeps the dim. Haptics belong to the mutation outcome, never the button. Size is independent of tone and of state: any tone at any size takes the same six states.
Replaces
ActionButton / Button (green, 56), ActionPill, the hand-drawn pressable pills.
7.2IconButtonA 24 glyph in a 44 target; ghost, no fill, tone carries the meaning.Controls
Library spec 44 target · 24 glyph · ghost | tones ink / secondary / danger / inverse (on a block) | aria-label required.
Anatomydrawn 1:1
IconButton
44 target · 24 glyph, centred (10 each side)
4444 target24 glyph
In a header: two actions
gap 8 · last glyph on the rail
44844
Statesno loading state: an icon button that works shows the result, not a spinner
Four tones · five states
rest · pressed · hover · focus · disabled
rest
default
pressed
.92 scale · .78
hover
opacity .85
focus
ring 2 · offset 2
disabled
.5
Ink
Secondary
Danger
Inverse
on the block #15181A
Behaviour (library section 2)
Pressed .92 scale / .78 dim · hover .85 · focus ring 2px ink, offset 2, following the circle (ball on an ink block) · disabled .5 · aria-label required because the glyph is the only content · the target is the visual: nothing needs hit slop, the glyph centres in 44 · in a header the last glyph's right edge sits on the rail, the 44 target bleeding 10 into the margin.
Replaces
HeaderIconButton, ActionIconButton, RemoveIconButton, SaveButton.
7.3ChoiceOne of a small set, or a filter that opens one. Never a status, never a tag.Controls
Library spec 36 tall (44 inside wizard forms) · r full · 15/22·500 · pad 0 16 (0 20 at 44) | resting 1px #D4D4D4 on white | selected = ink fill + white 700 label + leading check 16 | modes: menu (chevron 16), input (× 16, own 44 target), range | hit slop to 44.
Anatomydrawn 1:1; dashed = the 44 hit area
Choice 36 · selected
17 = 1px border + 16 pad · check 16 + 8 · label 15/22·700
단식
36r full17168label17
Choice 44 · in a form
21 = 1px border + 20 pad · same type, taller target
단식
44r full21label21
Input mode · the × is its own target
× 16 trailing · a 44 × 44 target around it removes the chip
강남구
36r full× target 44
Statessize × resting / selected × five states
36 and 44 · resting and selected
rest · pressed · hover · focus · disabled
rest
default
pressed
.96 scale · .85
hover
opacity .85
focus
ring 2 · offset 2
disabled
.5
36 resting
복식
복식
복식
복식
복식
36 selected
복식
복식
복식
복식
복식
44 resting
복식
복식
복식
복식
복식
44 selected
복식
복식
복식
복식
복식
Variantsmodes · the wrapped set
Modes
menu · input · range
menu
chevron 16
지역서울 전체
input
× 16 · 44 target
강남구서초구
range
opens a band
NTRP3.0–4.0
A wrapped set · 36
left aligned · gap 8 · never sideways
전체복식단식혼복남자여자초급중급상급
A wrapped set · 44 in a form
gap 8 both ways
복식단식혼합 복식남자 복식여자 복식
Behaviour (library section 2)
Pressed .96 scale / .85 dim · hover .85 · focus ring 2px ink, offset 2 · disabled .5. A set is role radio with aria-checked, arrow keys move the selection on web; menu and range modes are buttons with aria-expanded; input mode's × is its own 44 target and removes only itself. Selection is ink fill + the check glyph, never colour alone. A set wraps left-aligned, gap 8, and never scrolls sideways. Hit slop = (44 − 36) / 2 = 4 above and below at 36; the 44 form size needs none.
Replaces
SelectionChip (56 files), DropdownChip, RangeChip, RemovableChip.
7.4Segmented (form)Two to four exclusive options in a settings row or a wizard step; the one place a pill group is allowed.Controls
Library spec 40 tall · track gray100, r full, pad 4 | selected = white pill 32 + 1px #D4D4D4 + 600 ink; others 500 secondary | equal columns (the group-spread law) | settings and wizards only.
Anatomydrawn 1:1
Two segments
40 = 4 + 32 + 4 · equal columns
공개비공개
432 pill44171171
Statesselected moves, rest · pressed · focus · disabled · mid-slide
Rest · first selected
2 segments
공개비공개
Selected · second
2 segments
공개비공개
Four segments
equal columns, 85.5 each
전체복식단식혼복
Mid-slide · navSlide
dashed = start and end
전체복식단식혼복
Pressed segment
.96 scale · .85 dim on the tapped segment
공개비공개
Focus
ring 2 · offset 2 around the track
공개비공개
Disabled
.5, inert
공개비공개
Behaviour (library section 2)
The indicator slides with the navSlide spring (the drawn mid-slide frame shows start and end as dashed outlines) · role radiogroup with aria-checked, arrow keys move the choice on web · press on a segment .96 / .85 · focus ring 2px ink, offset 2, around the track · disabled .5 · never used as scope tabs (those are the underline tabs) and never beyond four segments.
Replaces
PillNav (pill variant) inside forms.
7.5SwitchAn immediate on / off. The toggle is the feedback; there is no pressed look.Controls
Library spec 48 × 28 track · r full · off #D4D4D4, on ink · thumb 24 white with a 1px #D4D4D4 edge, 2 in from the track · 150 ms travel.
Anatomydrawn 1:1
Switch · on
track 48 × 28 · thumb 24 · 2 inset
2848thumb 24
Statespressed is intentionally absent
Off and on
rest · mid-travel · hover · focus · disabled
rest
default
mid-travel
150 ms
hover
opacity .85
focus
ring 2 · offset 2
disabled
.5
Off
On
Variantsin a toggle Cell
Toggle Cell
48 · label left, switch on the rail
알림 받기
일정 시작 1시간 전 알림
Behaviour (library section 2)
role switch + aria-checked (this fixes the a11y hole of TogglePill); disabled .5 and inert; pressed: none, the thumb's 150 ms travel is the feedback (a mid-travel frame is drawn); hover .85; focus ring 2px ink, offset 2, around the track; haptics only through the mutation outcome; a toggle Cell presses the switch, so the whole 48 row is the target.
Replaces
TogglePill, Switch.
7.6InputOne line of text. The border is the only chrome; focus thickens it.Controls
Library spec 48 tall · r12 · 1px #D4D4D4 · pad 0 16 · text 15/22 ink · placeholder secondary | label 13/18·600 above (4) · helper 13/18 secondary below (4) | error: border + helper #B8332E | leading / trailing glyphs 20.
Anatomydrawn 1:1
Control · leading and trailing glyphs
48 · r12 · 1px · pad 16 · glyph 20 + 8
서울 강남구
48r12172082017text 15/22 · ink
Field · label, control, helper
18 + 4 + 48 + 4 + 18
클럽 이름
클럽 이름을 입력하세요
2–20자
label 13/18·600448r124helper 13/18·500
Statesrest · focus · error · disabled · filled · glyphs (pressed and hover do not apply)
Rest · empty
placeholder in secondary ink
클럽 이름
클럽 이름을 입력하세요
2–20자
Required · unset
one $error asterisk after the label · placeholder
클럽 이름
클럽 이름을 입력하세요
2–20자
Filled
unchanged from rest
클럽 이름
강남 라켓클럽
2–20자
Focus
2px ink border · caret ink · no ring
클럽 이름
강남 라켓
2–20자
Error
danger border + danger helper, never a toast
클럽 이름
강
2자 이상 입력해주세요
Disabled
.5, not-allowed
클럽 이름
강남 라켓클럽
가입 후에는 바꿀 수 없어요
With leading and trailing glyphs
glyph 20 secondary · trailing clear is a 44 target
지역
서울 강남구
클럽이 주로 모이는 곳
Behaviour (library section 2)
Focus = a 2px ink border, no ring: the padding drops by 1 so the text does not move. Error = danger border + danger helper, set when validation fails, never a toast. Disabled .5, not-allowed. The filled state is unchanged from rest. Inputs scroll above the keyboard (react-native-keyboard-controller, per screen, never a global pan → resize). The placeholder is secondary ink, never a substitute for the label. A trailing clear / reveal glyph is its own 44 target. Pressed and hover do not apply: an input is not a Pressable (the web cursor is text).
Replaces
StyledInput, LabeledField.
7.7TextAreaFree text: the Input's border and states, with room to grow and a counter.Controls
Library spec min 120 · auto-grows 22 per line to 200, then scrolls inside · counter 13/18 secondary bottom-right inside the pad | otherwise as Input.
Anatomydrawn 1:1
TextArea · minimum
120 · r12 · pad 12 16 · counter bottom-right
매주 토요일 오전, 올림픽공원에서복식 위주로 가볍게 치는 모임이에요.초보도 환영하고, 레슨 코치님이58 / 200
min 120r12counter 13/1817
Statesgrows with its lines, caps at 200 and scrolls
Rest · empty
min 120
클럽을 소개해 주세요0 / 200
Required · unset
asterisk on the label, placeholder, counter 0 / 200
클럽 소개
클럽을 소개해 주세요0 / 200
Filled · three lines
still 120, the counter has its own row
매주 토요일 오전, 올림픽공원에서복식 위주로 가볍게 치는 모임이에요.초보도 환영하고, 레슨 코치님이58 / 200
Grown to the cap
7 lines = 200 (12 + 154 + 34)
매주 토요일 오전, 올림픽공원에서복식 위주로 가볍게 치는 모임이에요.초보도 환영하고, 레슨 코치님이한 달에 한 번 와서 봐 주세요.회비는 월 3만 원이고 코트비,공 값이 포함돼요. 가입 전에는한 번 놀러 와서 같이 쳐 보세요.142 / 200
Past the cap
stays 200, scrolls inside
매주 토요일 오전, 올림픽공원에서복식 위주로 가볍게 치는 모임이에요.초보도 환영하고, 레슨 코치님이한 달에 한 번 와서 봐 주세요.회비는 월 3만 원이고 코트비,공 값이 포함돼요. 가입 전에는한 번 놀러 와서 같이 쳐 보세요.주차는 올림픽공원 3주차장이가장 가까워요.199 / 200
Focus
2px ink border
매주 토요일 오전, 올림픽공원에서복식 위주로 가볍게 치는 모임이에요.31 / 200
Error
danger border + helper
아1 / 200
10자 이상 입력해주세요
Behaviour (library section 2)
As Input. The box grows with its lines from 120 up to 200, then the text scrolls inside it (a thin indicator shows at the right) and the box stops growing; the counter sits 12 from the floor and 16 from the right edge on its own canvas patch so text never runs under it. Focus = 2px ink border; error = danger border + danger helper; the keyboard rules of Input apply (scroll the field above the keyboard, per screen).
Replaces
StyledInput multiline, the composer.
7.8SearchA find field. On a list screen it lives behind a header glyph and takes over the title.Controls
Library spec 44 · r full · gray100 · glyph 20 + 8 · 15/22 placeholder secondary · clear × 20 in a 44 target when filled · a ghost cancel at the right when focused · header takeover on list screens (glyph in the header, the field replaces the title).
Anatomydrawn 1:1 · filled state shows the clear target
Search · filled
44 · glyph 20 + 8 · clear × 20 in a 44 target
올림픽
44r full12208clear 4420
Statesrest · focused with cancel · filled with clear
Rest
placeholder secondary
코트, 클럽, 사람 검색
Focused · with cancel
caret ink · field stays gray100
코트, 클럽, 사람 검색
취소
Filled · with clear
× 20 in its own 44 target
올림픽
취소
Header takeoverinside a 390 header (56): the glyph is pressed, the field replaces the title
Before
the search glyph in the list header
경기
After
field 44 centred in 56 · 취소 in place of the actions
코트, 클럽, 사람 검색
취소
Behaviour (library section 2)
Focus keeps the field gray100 and turns the caret ink (no border change, no ring). Focus reveals 취소: the field shrinks, the cancel is a ghost button (44 target) and clears + blurs on press. The clear × appears once there is a character and is its own 44 target. The header takeover swaps the title for the field in place, the actions yield to 취소, and the keyboard opens with it; the back gesture closes it. Disabled .5.
Replaces
SearchBar.
7.9StepperA bounded integer dialled up or down: counts, scores, limits.Controls
Library spec 36 pill group: − / value 15/22·600 / +, each end a 44 target.
Anatomydrawn 1:1 · dashed = the 44 × 44 hit areas
Stepper
136 × 36 · ends 44 · value 48
4
36r full4448 value44hit 44 × 44
Statesrest · pressed · focus · bounds (hover is the pointer .85 on the pill)
Value 4 of 1–8, and the two bounds
the pill never scales; the glyph does
rest
default
− pressed
.92 · .78
+ pressed
.92 · .78
focus
ring on the end
at min
− disabled .5
at max
+ disabled .5
Stepper
4
4
4
4
1
8
Variantsin a Cell row
Label left, stepper on the rail
Cell 48 · the pill's right edge is the rail
참가 인원
8
Behaviour (library section 2)
Each end's glyph (20) presses .92 / .78; the pill itself does not move. At a bound the end that cannot go further dims to .5 and goes inert; the value cell is a fixed 48 so the digits do not jitter. The 44 × 44 hit area is the visual 36 plus 4 above and below (hit slop (44 − 36) / 2). Role group with a spin value (min, max, now, text) for screen readers; focus ring 2px ink, offset 2, around the focused end.
Replaces
Stepper (32 circle buttons).
7.10SliderA value or a band dialled by drag, with the live value always on screen.Controls
Library spec track 4 #E5E5E5 · fill ink · thumb 24 white with a 1px edge · live value tag (ink, 24) above the thumb · band sliders keep the three grab surfaces.
Anatomydrawn 1:1
Single
tag 24 + 8 + thumb 24 · track 4 · scale labels 13/18
4명1명8명
tag 248thumb 24scale 18350 · track 4 · fill ink
Band · the three grab surfaces
two thumbs + the body, each a 44 target
2.5–5.51.07.0
left thumbright thumbband body
Statesrest · focus · disabled · at the bounds (pressed is the drag itself; the tag is its feedback)
Single · rest
value 4 of 1–8
4명1명8명
Single · focus
ring on the thumb
4명1명8명
Single · disabled
.5, inert
4명1명8명
At the minimum
tag clamps inside the rail
1명1명8명
Band · rest
NTRP 3.0–4.5 of 1.0–7.0
3.0–4.51.07.0
Band · focus
ring on the focused thumb
3.0–4.51.07.0
Behaviour (library section 2)
The tag rides above the thumb and always reads the live value (a band's tag centres over the band). A band has three grab surfaces: left thumb, right thumb and the band body (drag moves both); each is a 44 target, the thumbs centred on the 24 visual. The drag does not share an axis with a scroll: fit the domain, snap, and show the live label for precision. The thumb's 150 ms settle is dropped under reduced motion. Focus ring 2px ink, offset 2, around the thumb; arrow keys step on web.
Replaces
TimeBandSlider, RangeBandSlider, ValueSlider (retire).
7.11Checkbox / radio rowsChoosing from a list: a trailing check for one, a 24 square for several.Controls
Library spec Cell 48 with a trailing check (single choice) or a 24 checkbox square r6, ink when on (multi) | the cell hairline #E5E5E5 is inset to the rail between rows, none after the last.
Anatomydrawn 1:1 · rows are full-bleed 390, content on the rail
Single · trailing check
48 rows · check 20 + the 20 rail · label 15/22
초급 (NTRP 1.0–2.5)
중급 (NTRP 3.0–4.0)
상급 (NTRP 4.5 이상)
482020
Multi · 24 square
square 24 r6 + the 20 rail · on = ink + white check 16
토요일
일요일
평일 저녁
482420
Statesone multi row in each state (single rows differ only in the glyph)
Rest · unchecked
2px #4F5851 edge · 7.4 : 1 · resolved (K2)
토요일
Checked
ink square · white check 16
토요일
Pressed
.98 scale · .80 dim
토요일
Focus
ring inside the row
토요일
Disabled
.5, inert
토요일
Single · chosen
600 label + check 20
복식
Behaviour (library section 2)
aria-checked on the row (role radio for single, checkbox for multi). The whole row is the target: pressed .98 scale / .80 dim, hover .85, focus ring 2px ink inside the row, disabled .5. The chosen single row turns its label 600 and shows the 20 check in ink (no fill, no tint); multi rows keep the label and fill the square ink with a white check 16. An unchecked square is a 2px #4F5851 edge — resolved 2026-10-09 (library section 13, K2): #4F5851 on white is 7.4:1, WCAG 1.4.11 asks 3:1 for a control boundary, the 1px #D4D4D4 hairline is 1.48:1 and an empty square has no text to carry it; Material 3 draws a 2dp outline.
Replaces
SelectionCard lists.
7.12Date / time pickerA date or a time chosen on the platform wheel inside the standard bottom sheet.Controls
Library spec bottom ModalPanel with the native wheel · title 17/24·600 · CTA 완료 primary.
States the wheel is the platform's; the close × and 완료 carry IconButton and Button states.
Anatomydrawn 1:1 · sheet 390 · the scrim is the backdrop
Time
오전 / 시 / 분
경기
시작 시간
오전
오후
7
8
9
10
11
20
25
30
35
40
완료
20 padtitle 17/24·60016native wheel5 × 4024CTA 52완료20 + 34home inset
Date
년 / 월 / 일
경기
날짜 선택
2024년
2025년
2026년
2027년
2028년
8월
9월
10월
11월
12월
7일
8일
9일
10일
11일
완료
20 padtitle 17/24·60016native wheel5 × 4024CTA 52완료20 + 34home inset
Behaviour (library section 2)
The sheet slides 200 ms (`quick`), backdrop ink α0.4, tap-outside dismisses without applying; 완료 commits the wheel's value and closes. The wheel itself is the platform control (UIDatePicker wheel on iOS, the Material spinner on Android), so its row height and haptics are the platform's; the 5 × 40 drawn here is the proportion on a 390 phone. One CTA only: no 취소 beside it, the close × and the scrim cancel. The wheel is not an input: no typing.
Replaces
Raw react-native-modal-datetime-picker (6 files).
BOverlays and feedback
Library section 8: what appears over the page. One sheet, one dialog, one menu, one toast, one coachmark; no shadows anywhere (the scrim, the edge hairline or the ink surface does the separating).
8.1ModalPanel · sheetThe one overlay for a quick edit or a pick: rises from the bottom, keeps the page visible above it.Overlays
Library spec bottom · top radius 16 · pad 20 · title 17/24·600 + close 24 at the right · content · the CTA band inside (button 52, 24 above, 20 + the 34 home inset below).
States open (entering 200 ms) and closed; the close × is an IconButton (7.2) and the CTA a Button (7.1), each with its own states.
Anatomydrawn 1:1 · the scrim is ink α0.4 over the screen behind
Title, content, CTA band
334 = 20 + 24 + 16 + 144 + 24 + 52 + 54
경기
공개 범위
공개
클럽 멤버에게만
비공개
적용
20 padtitle 17/24·60016content3 × 4824CTA 52primary20 + 34home inset
Variantsno CTA · entering
No CTA band
238 · tapping an option commits and closes
경기
공개 범위
공개
클럽 멤버에게만
비공개
Entering · 200 ms `quick`
from y +48 and fading in; dashed = the resting place
경기
공개 범위
공개
클럽 멤버에게만
비공개
적용
Behaviour (library section 2)
RN Modal: the root fades 150 ms and the sheet slides up 200 ms (`quick`, from y +48), backdrop ink α0.4. A tap on the scrim dismisses; the close × (24 in a 44 target) does the same; there is no grabber and no drag (bottom-sheet libraries are retired). The keyboard lifts the sheet through the KeyboardAvoidingView. The sheet is auto-height, capped at 88%, its body a ScrollView that shrinks and scrolls past the cap; a footer CTA is a normal sibling, never absolute. Reduced motion drops the slide.
Replaces
ModalPanel (bottom placement).
8.2DialogA decision that cannot be postponed: confirm or cancel one action.Overlays
Library spec centred · 350 wide · r16 · pad 20 · title 17/24·600 · body 15/22 secondary (two lines at most) · CTA pair (tonal + primary; danger when the action destroys).
States none of its own: the dialog is open or closed. Its two Buttons carry theirs (7.1); the destructive variant swaps the primary for danger.
Anatomydrawn 1:1 · 350 wide on the rail, centred in the slice
Confirm
188 = 20 + 24 + 8 + 44 + 20 + 52 + 20
경기
참석을 확정할까요?
확정하면 자리가 예약돼요.취소는 일정 1시간 전까지예요.
취소
참석 확정
20 padtitle 17/24·6008body2 × 2220buttons52 · pair20 pad
Variantsdestructive: the committing button is danger
Destructive
danger fill #B8332E, white 17/24·600
경기
일정을 취소할까요?
취소하면 참석자 6명에게알림이 가요.
취소
일정 취소
Behaviour (library section 2)
Fades 150 ms over ink α0.4. The backdrop does not dismiss and the system back gesture is the same as 취소: a confirm needs a choice. The pair is tonal flex 1 + primary or danger flex 2, gap 12, so the committing button is the larger one and is on the right (thumb side). Destructive wording names the object (일정 취소, 클럽 삭제), never 확인. Role alertdialog; focus lands on the tonal button when the action is destructive, so Enter does not delete.
Replaces
ModalPanel (centre placement) as a confirm.
8.3MenuThe overflow of a header: up to five actions on one object.Overlays
Library spec anchored under the header action · 240 wide · r12 · 1px #D4D4D4 · rows 48 (glyph 20 + 15/22) · destructive last, in #B8332E, after one cell hairline · at most 5 rows.
Anatomydrawn 1:1 · the menu is 242 = 5 × 48 + the two 1px edges
Menu under the header
x 130–370 · 4 below the 56 header
경기
공유
링크 복사
수정
신고
삭제
row 48glyph 20 + 12row 48hairline belowdanger rowlast · #B8332E
Statesa row pressed
Row pressed
.98 scale · .80 dim on the tapped row
공유
링크 복사
수정
Two rows
a short menu is 96 + 2
수정
삭제
Behaviour (library section 2)
Fades 100 ms, no shadow (the edge hairline separates it from the page). Right-aligned to the rail under the action, 4 below the header. A tap outside or on a row closes it; each row is a Pressable (pressed .98 scale / .80 dim). Role menu / menuitem; arrow keys move on web. More than five actions is a sheet, not a menu. The destructive row never sits first and always asks a Dialog.
Replaces
HeaderActionsMenu (shadowed).
8.4ToastA confirmation that needs no answer: one at a time, gone in 2.8 s.Overlays
Library spec ink surface · r12 · min 48 · pad 12 16 · status glyph 20 before the text (success #75D1C6, warning #E1B75C, danger #FA9B94, info #C9B2F2) · text 15/22 white · optional action 15/22·600 in ball · 16 above the tab bar, and 16 above the FAB when one is present · no shadow.
States the entering and leaving fade, the held timer and the swipe-down drag are drawn below; the action is part of the pill (the whole pill is the tap target).
Anatomydrawn 1:1 · the stage is a 390 slice with the tab bar (56 + 34) and the FAB
Above the tab bar
bottom edge 16 above the bar
경기
홈
클럽
경기
기록
프로필
일정을 저장했어요
toastmin 4816tab bar56 + 34
Clearing a FAB
16 above the FAB, which sits 16 above the bar
경기
매치
홈
클럽
경기
기록
프로필
참석을 취소했어요
실행 취소
toast4816FAB4816
Variantsaction · two lines · status glyph · swipe
With an action
15/22·600 ball text, 16 from the text
참석을 취소했어요
실행 취소
Two lines
68 = 12 + 44 + 12
저장하지 못했어요.연결을 확인하고 다시 시도해 주세요.
Status glyph (kept from the code)
20 before the text · dark-theme tones, 7.3 to 10.7 : 1 on ink
일정을 저장했어요
마감이 가까워요
저장하지 못했어요
다시 시도
일정이 변경됐어요
Swipe down to dismiss
dashed = rest · drag 1:1 · past 40 px or 500 px/s
일정을 저장했어요
dragged 44 down · past 40 it leaves
Behaviour (library section 2)
One at a time (a new toast replaces the old). 2800 ms, the timer pauses while a finger is down. It floats 16 above the tab bar, the wizard footer or the keyboard, whichever is tallest (the code's 8, TOAST_CHROME_GAP, becomes 16 — resolved 2026-10-09, library section 13, K1: it matches the FAB's own 16 offset, one seam), and 16 above a FAB when one is on screen. Swipe down: it follows the finger 1:1 (upward is resisted to 15%), past 40 px or 500 px/s it dismisses with a 150 ms fade, otherwise it springs back. The action is a tap target of the whole pill; reduced motion snaps instead of springing. Role status, announced politely. Errors that belong to a field are never toasts.
Replaces
ToastHost (shadowed, r24).
8.5CoachmarkA one-time pointer at a control the user has not met: spotlight, one sentence, one tap.Overlays
Library spec ink surface · r12 · an 8px arrow · body 15/22 white · 다음 ghost in ball text · the spotlight unchanged.
States none of its own: one is shown at a time and 다음 is a ghost Button carrying the Button states (7.1).
Anatomydrawn 1:1 · pointing down at the FAB, and up at a header glyph
Above a FAB · arrow down
card 280 · pad 16 16 8 · foot 44 · arrow 8
경기
매치
매치 일정은 여기서만들 수 있어요
1 / 3다음
16 padbody2 × 224foot448 padarrow 8
Below a header glyph · arrow up
spotlight circle r26 around the 44 target
경기
코트와 클럽을이름으로 찾아보세요
1 / 3다음
Behaviour (library section 2)
The spotlight and its order are unchanged from the code: a backdrop with a hole around the target, the card placed on the side with room, the arrow centred on the target. One step at a time with a quiet `1 / 3`; 다음 advances, the last step reads 확인. Tapping the backdrop does nothing (it is a guided tour, not a dismissible tip); there is always a way out through the screen's own back. Shown once per user and per step, and never over a screen the user has not opened.
Replaces
CoachmarkTooltip.
CStates
Library section 9: what a surface looks like with nothing, with a failure, while loading, and while offline.
9.1EmptyStateNothing here, a failure, no network, or a missing thing: one calm, centred explanation and one way forward.States
Library spec centred · 64 from the top · disc 80 gray100 with a 32 glyph in secondary · title 17/24·600 · body 15/22 secondary (≤ 2 lines) · one md tonal Button, 44 (primary when the empty state is the screen's action) | variants empty / error (AlertCircle + 다시 시도) / offline (WifiOff) / not-found.
States none of its own: the four kinds below are its whole variation; the button carries the Button states (7.1).
Anatomydrawn 1:1 · 304 = 64 + 80 + 16 + 24 + 8 + 44 + 24 + 44
Empty
md tonal button 44 · body two lines
아직 일정이 없어요
새 일정을 만들면여기에 나타나요
일정 만들기
top 64disc8016title 17/24·6008body2 × 2224buttonmd tonal 44disc 80 · glyph 32
Variantsfour kinds, and the primary form
Error
AlertCircle · 다시 시도
불러오지 못했어요
잠시 후 다시시도해 주세요
다시 시도
Offline
WifiOff · 다시 시도 · wins over error
오프라인이에요
연결되면 자동으로다시 불러와요
다시 시도
Not found
search-x · a way back
찾을 수 없어요
삭제됐거나주소가 바뀌었어요
홈으로
Empty · the screen's action
md primary ball 44 when this IS the action
아직 일정이 없어요
새 일정을 만들면여기에 나타나요
일정 만들기
Behaviour (library section 2)
Offline trumps error: a failed query while the device is offline shows the offline variant (the same rule the code applies in QueryErrorState). The button is the only interactive thing (md tonal 44, hit area the box). Reads as one unit for screen readers: glyph hidden, title then body then the button. No card, no illustration, no mint: the disc is the neutral gray100. Compact placement (inside a section) drops the 64 top to 24 and the disc to 64; the full variant owns the screen.
Replaces
EmptyState (mint disc), QueryErrorState, OfflineEmptyState, NotFoundState.
9.2SkeletonThe loading shape of each v2 surface at its exact pitch, so nothing jumps when data arrives.States
Library spec media row (56 square r8 + a 17/24 line at 60% + a 15/22 line at 40%) · person row (40 circle + two lines) · cell (label 40% + value 20%) · block 144 r16 · strip 80 · chart 160 · hero (a 24/32 line at 70% + three 17/24 lines) — all gray100, pulsing between .6 and .9.
States the two pulse frames, opacity .6 and .9, are its states.
The seven shapesthe same stack at the two ends of the pulse: opacity .6 (left) and .9 (right) · rulers on the left stack
Pulse frame A · opacity .6
shapes at their pitch, 16 between · gray100 on white is 1.1:1 by design
media row80person row64cell48strip80block144 · r16chart160hero160
Pulse frame B · opacity .9
the other end of the 800 ms half-cycle
Behaviour (library section 2)
One shared pulse: opacity .6 ↔ .9 with an 800 ms half-cycle, on the UI thread (the same value drives every skeleton on screen, so they breathe together); under reduced motion it pins at .75. A skeleton is the loading state of its own component: same box, same pitch, bars at the height of the type's cap line, never a spinner on a list. Not announced: the container sets aria-busy and the real content replaces it. The shapes are in the gray100 neutral on both themes (dark: the fill).
Replaces
Skeleton, SkeletonCard, SkeletonHero.
9.3SpinnerWork in progress inside a control: the only place a spinner appears.States
Library spec ActivityIndicator, ink, small (20); only inside buttons.
States none: it turns.
Anatomydrawn 1:1
Spinner
20 × 20 · ink · 2px stroke
2020
Four frames of the turn
0 · 90 · 180 · 270 degrees
In a button
label replaced, width held
저장
저장
삭제
Behaviour (library section 2)
The platform's own indicator rotates (the four frames show a quarter turn each); the board draws a 270 degree arc in a 2px stroke. It replaces a button's label at full opacity and the button keeps its width. It never stands alone on a list or a screen (that is a skeleton). Pull-to-refresh uses the native control with an ink tint and shows only for a user pull. On a danger button the arc takes the label colour (white), because ink on #B8332E does not hold contrast.
Replaces
Raw ActivityIndicators (8 files).
9.4Offline stripConnectivity lost while content is on screen: a quiet notice, not an error.States
Library spec Notice (neutral) pinned under the header · 오프라인 + a retry.
States present or absent; 다시 시도 is a text action (pressed .85 dim).
Anatomydrawn 1:1 · min 48 — resolved 2026-10-09 (library section 13, L4): 12 + 22 + 12 = 46 rounds up to the 4-grid, and 48 equals the Cell
Under the header
8 below the 56 header · 350 wide on the rail
경기
오프라인
다시 시도
8stripmin 488
With a body line
68 = 12 + 22 + 22 + 12 · when the cached view needs saying
경기
오프라인저장된 일정을 보고 있어요
다시 시도
Behaviour (library section 2)
Pinned under the header and above the content, so nothing under it moves except by its own 48. It appears the moment the device reports no route and goes the moment one returns (it retries on its own; 다시 시도 forces it). The strip is the screen's only offline message: lists keep their cached rows, and the empty variant of EmptyState appears only when there is nothing cached. Role status; neutral fill, never danger (the user has done nothing wrong).
Replaces
NetworkBanner.
DForms
Library section 10: the wizard field grammar in v2. WizardShell, Field, Choice set, AddRow, a Notice under a field, PickerRow, the remove button, validation and the confirm step's pair.
10.1WizardShellThe frame of every multi-step flow: where you are, what this step asks, one way forward.Forms
Library spec header 56: a 40 × 40 r12 gray100 back square with a 20 ink chevron (an × on step 1, with the exit Dialog) in a 44 target, 건너뛰기 as an sm tonal Button (36, slop to 44) on the right · a 2pt progress line on the header's last 2pt (ink for done + current, #E5E5E5 for remaining; no ball segment) · title 24/32·700 at 24 · fields at 32 seams · footer CTA band with one primary (a pair only on the confirm step).
States the header controls and the CTA carry the Button states (7.1, 7.2); the progress line is the step (n of N); the CTA is disabled (.5) until every required control is answered.
Anatomydrawn 1:1 on a full 390 × 844 frame · the progress line occupies the header's last 2, so 56 holds
Step 3 of 6
status 44 · header 56 · 24 · title 32 · 32 · field 92 (required, unset) · 32 · field 118 · … · band 118 (disabled)
9:41
건너뛰기
클럽 이름을 정해주세요
클럽 이름
클럽 이름을 입력하세요
2–20자
주로 치는 종목
복식단식혼합 복식남자 복식여자 복식
다음
status 44header 56progress 2 on its edge24title24/32·70032 seamField9232 seamField118 · Choice setCTA band16 + 52 + 16 + 34
Header · back square, skip, progress
back 40 r12 in a 44 target · 20 chevron · skip sm 36
건너뛰기
44 target40 square · r12sm tonal36 · slop 4progress 2
The header at steps 1, 3 and 6
× on step 1 · ink done + current · no ball segment
건너뛰기
Behaviour (library section 2)
Back is a gray100 square button (a 40 square in a 44 target) and skip an sm tonal Button, both in the header and never ghost text or a bare glyph: the owner's wizard-navigation LAW (2026-08-15, re-ruled in library section 21.1) and the footer holds a single CTA and nothing else. Hardware back mirrors the header back (useWizardBackHandler). Required controls carry the one asterisk and start unset (the required-marker LAW), so 다음 is disabled until they are answered. The progress line is a stepper, not a loader: it moves with the step and carries no animation but a 150 ms colour change. It carries information, so it needs 3:1 non-text contrast (WCAG 1.4.11): ink for done + current, #E5E5E5 for remaining — resolved 2026-10-09 (library section 13, K6); non-text contrast: ball on white is 1.2:1 (1.28 computed), so there is no ball segment. Sequential reveal and the live-draft model are unchanged (RevealStack keeps its 250 ms reveal); a field that is not yet reached is not drawn. Validation is inline under the field. The CTA edge hairline appears only while fields scroll under the band. The keyboard lifts the band; the toast host reads the band's height, which the band reports itself.
Replaces
WizardShell (the card-grammar header, footer 56 button, brand-tinted progress).
10.2FieldThe wrapper every form control sits in: a label above, the control, a helper below.Forms
Library spec label 13/18·600 + control + helper 13/18 · label → control 4 · control → helper 4 · field → field 32 (the section seam of a wizard).
States carried by its control: Input rest / focus / error / disabled (7.6).
Anatomydrawn 1:1 · two fields at the 32 seam
Two fields, one seam
Input field 92 · seam 32 · Choice-set field 118
클럽 이름
클럽 이름을 입력하세요
2–20자
주로 치는 종목
복식단식혼합 복식남자 복식여자 복식
Fieldlabel 18 · 4 · 48 · 4 · helper 1832 seamField18 · 4 · 44 · 8 · 44
Requiredthe required-marker LAW: one $error asterisk the size of the label · the field starts unset · the label never grows
Required, unset
asterisk 13/18·600 $error · placeholder · 92 as before
클럽 이름
클럽 이름을 입력하세요
2–20자
Required, left empty
danger border + danger helper naming what is missing
클럽 이름
클럽 이름을 입력하세요
클럽 이름을 입력해주세요
Required, answered
the asterisk stays: it marks the field, not its state
클럽 이름
강남 라켓클럽
2–20자
Optional
no marker, no suffix
한 줄 소개
클럽을 한 줄로 소개해주세요
Required Choice set, unset
none chosen · danger helper after a miss
주로 치는 종목
복식단식혼합 복식
Dark
$error asterisk in the dark palette
클럽 이름
클럽 이름을 입력하세요
Variantsthe control slot
No helper
70 = 18 + 4 + 48
클럽 이름
강남 라켓클럽
TextArea
164 = 18 + 4 + 120 + 4 + 18
클럽 소개
매주 토요일 오전, 올림픽공원에서복식 위주로 가볍게 치는 모임이에요.초보도 환영하고, 레슨 코치님이58 / 200
처음 오는 사람이 읽어요
Stepper
label above, the pill left aligned
정원
12
Behaviour (library section 2)
The label is a real label for the control (htmlFor / accessibilityLabelledBy); the helper is announced with the control and turns danger text on error (the same node, so the swap never shifts layout by more than the 18 it already holds). A Field with no helper is 70 (18 + 4 + 48) and does not reserve the helper's space; a wizard still keeps 32 between fields either way. The control is any of Input, TextArea, a Choice set, a PickerRow, a Stepper. A required control's label carries the one asterisk (owner LAW, library section 10 corrected 2026-10-10): `$error`, the size of the label, inside the label's own text; the field starts genuinely unset (no default value, an empty Choice set), and a required Field left empty on leaving it shows the danger helper naming what is missing. An optional field has no marker and no 선택 suffix.
Replaces
LabeledField (the label + input + hint stack).
10.3Choice setPick one or several from a small list, inside a form.Forms
Library spec Choice 44 · wrapped left-aligned · gap 8 both ways · never scrolls sideways.
Anatomydrawn 1:1
Wrapped set
44 chips · 8 between · left aligned
복식단식혼합 복식남자 복식여자 복식
4484488
Variantssingle · several · required and empty
Single
radiogroup · one chosen
복식단식혼합
Several
toggles · aria-pressed · checks show
토요일일요일평일 저녁평일 낮
Required and empty
the asterisk on the label · danger helper under the set
종목
복식단식혼합
하나 이상 골라주세요
Disabled
.5 on the whole set
복식단식혼합
Behaviour (library section 2)
Single choice = role radiogroup (arrow keys move, one selected at all times after the first pick); several = a group of toggle buttons with aria-pressed (selected chips show the check). The set wraps by itself at 350, so two to nine short options fit; more than that is a PickerRow into a sheet. The set keeps its own validation: none chosen on a required field shows the danger helper under it (never a toast). Disabled .5 on the whole set.
Replaces
SelectionChip groups in wizards.
10.4AddRowAdd another to a list: an open slot among the rows already there.Forms
Library spec 56 · dashed #D4D4D4 · r12 · glyph 20 + 8 + 15/22·600 label.
Anatomydrawn 1:1
AddRow
350 × 56 · dashed 1.5px · glyph 20 + 8 · label 15/22·600
코트 추가
56r12208label
Statesrest · pressed · hover · focus · disabled
Rest
default
코트 추가
Pressed
.98 scale · .80 dim
코트 추가
Hover
opacity .85
코트 추가
Focus
ring 2 · offset 2
코트 추가
Disabled
.5
코트 추가
Variantsbelow the rows it adds to
After two rows
rows 48, then 12, then the slot
올림픽공원 1번 코트
올림픽공원 2번 코트
코트 추가
Behaviour (library section 2)
The whole 56 is the target and it carries a label, never a bare glyph. It lives in the content, not the footer (the footer is the one CTA). Pressed .98 scale / .80 dim (row physics), hover .85, focus ring 2px ink offset 2, disabled .5 (a maximum was reached: the label says so in the helper under it). Activating opens the add flow in a sheet or appends an empty row and focuses it, whichever the wizard defines; the row then sits 12 below the new item.
Replaces
AddCard (brand-coloured dashed tile).
10.5Notice under a fieldContext that belongs to one field: a neutral Notice directly under it.Forms
Library spec a Notice (neutral gray100, r12, pad 12 16, glyph 20 + 12) 8 under its Field.
States none: it is static and not interactive.
Anatomydrawn 1:1
Field + Notice
Field 92 · 8 · Notice 48 (one line)
모임 장소
올림픽공원 테니스장
코트 이름까지 적어주세요
장소는 가입한 멤버에게만 보여요
Field928Noticemin 48
Variantsone line · title and body
One line
48 min · text 15/22 400
장소는 가입한 멤버에게만 보여요
Title and body
68 = 12 + 22 + 22 + 12
클럽 공개 범위공개로 하면 누구나 검색해서 볼 수 있어요
Behaviour (library section 2)
It replaces AttachedPanel, the bordered panel that used to hang off a field. Neutral only: a warning or an error is the Field's own helper, never a toned Notice (the form carries no tone but danger text). It is static, not dismissible, and read after the control (aria-describedby). One line is 48 (min), a title + body is 68; it never carries an action in a wizard (the CTA is the action).
Replaces
AttachedPanel.
10.6PickerRowA field whose value is chosen elsewhere: a Cell with a value and a chevron that opens a sheet.Forms
Library spec Cell 48 · label 15/22·400 secondary left · value 15/22·500 ink + chevron 16 on the right · the cell hairline inset to the rail between rows.
Anatomydrawn 1:1 · rows are full-bleed 390, content on the rail
PickerRow · two rows
48 · value + chevron 16 · hairline inset 20
지역서울 강남구
요일토요일
4881620
Statesrest · empty · pressed · focus · disabled · error
Rest
value chosen
지역서울 강남구
Empty
placeholder 선택 in secondary
지역선택
Pressed
.98 scale · .80 dim
지역서울 강남구
Focus
ring inside the row
지역서울 강남구
Disabled
.5
지역서울 강남구
Error
required and empty: danger helper 4 under
지역선택
지역을 선택해 주세요
Behaviour (library section 2)
The whole 48 is the target and opens the ModalPanel sheet with the options as check rows (8.1). Pressed .98 scale / .80 dim, hover .85, focus ring 2px ink inside the row, disabled .5 An empty value shows `선택` in secondary ink; a required empty one adds the danger helper 4 under the row, never a toast. Role button with the current value in its accessibility label (`지역, 서울 강남구`).
Replaces
PickerRow (bordered field row), the Cell-with-chevron lists in wizards.
10.7RemoveIconButtonRemove one item from a list: an IconButton in the danger tone.Forms
Library spec IconButton (44 target, 24 glyph) in danger #B8332E, the trash glyph; at the end of its row, the glyph's right edge on the rail.
Anatomydrawn 1:1 · in its row
Row with a remove button
48 row · target 44 · glyph 24 ending on the rail
올림픽공원 1번 코트
4844 target24
Statesthe danger row of the IconButton matrix
Danger · five states
rest · pressed · hover · focus · disabled
rest
default
pressed
.92 scale · .78
hover
opacity .85
focus
ring 2 · offset 2
disabled
.5
Remove
Behaviour (library section 2)
Not a new component: IconButton with tone danger and `aria-label` naming the item (`1번 코트 삭제`). Pressed .92 / .78, focus ring ink. Removing an item still in the draft is immediate and offers 실행 취소 in a toast; removing a saved object opens a Dialog (8.2) with the destructive pair. Never the only way to remove: the same action is in the row's menu on web.
Replaces
RemoveIconButton.
10.8Validation errorSay what is wrong where it is wrong: an inline danger helper under the field.Forms
Library spec helper 13/18·500 in #B8332E, 4 under the control · the control's border turns #B8332E (Input, TextArea) · never a toast.
States this is the error state of the four controls below; their other states are on their own specimens.
Anatomydrawn 1:1 · the swap happens inside the helper's own 18 line
Input in error
border #B8332E · helper 13/18·500 #B8332E, 4 under the control
클럽 이름
강
2자 이상 입력해주세요
label 18control 48border 1px4helper 18
Variantsone error pattern across the four controls
Input
border + helper
클럽 이름
강
2자 이상 입력해주세요
TextArea
border + helper, counter stays
클럽 소개
아1 / 200
10자 이상 입력해주세요
Choice set
helper only
주로 치는 종목
복식단식혼합 복식
하나 이상 골라주세요
PickerRow
helper only
지역선택
지역을 선택해 주세요
Behaviour (library section 2)
The helper swaps in for the field's normal helper in the same 18 line, so the form does not jump. It names the fix (`2자 이상 입력해주세요`), not the failure. Colour is never the only signal: the text is. Timing (on blur, on the CTA) follows the wizard's live-draft model, unchanged. The first field in error takes focus when the CTA is pressed with errors; the error clears the moment the value is valid. A choice set or a PickerRow carries the same helper with no border change (it has none to tint).
Replaces
Toast-and-banner validation, hand-drawn red hints.
10.9Confirm step CTA pairThe one place a wizard footer holds two buttons: go back, or commit.Forms
Library spec the CTA band with a pair: tonal flex 1 + primary flex 2, gap 12 · 52 · 16 above, 16 below, 20 sides, then the home inset 34.
Anatomydrawn 1:1 · widths from flex 1 : 2 over 350 − 12 = 338
Pair
118 = 16 + 52 + 16 + 34
이전
클럽 만들기
16buttons5216 + 34home inset112.7 · flex 112225.3 · flex 2
Statesrest · committing · scrolled
Rest
both enabled
이전
클럽 만들기
Committing
primary loading, width held · tonal inert .5
이전
클럽 만들기
Content scrolls under it
edge hairline #D4D4D4 on the top row
이전
클럽 만들기
Every other step
one primary, the full rail
다음
Behaviour (library section 2)
Only the confirm step shows the pair; every other step is the single primary (the wizard-nav law). The tonal button goes back one step (it is not 취소: leaving is the header's back). The primary names the commit (`클럽 만들기`, never 확인) and shows its loading state (the width is held) while the mutation runs; both buttons are inert during it. The edge hairline #D4D4D4 draws on the band's top only while content lies below it (scroll).
Replaces
BottomCtaBand (16 sides, permanent hairline) and WizardShell's two-button footer.
EDark mode
Library section 1 dark values: canvas #0F1211, surface #1A201B, ink #DFE4DC, secondary #A9B3AB, hairlines white α0.09 and α0.14, the ball unchanged with ink text, danger #FA9B94. The same components, one palette swap.
Darkthe same components re-rendered through the dark palette, no other change
Button set
primary · tonal · danger · ghost md · tonal sm · loading
참석하기
나중에
클럽 삭제
다시 시도
수정
저장
Input
rest · focus · error
클럽 이름
클럽 이름을 입력하세요
2–20자
클럽 이름
강남 라켓
2–20자
클럽 이름
강
2자 이상 입력해주세요
Switch, Choice, Segmented
off · on · disabled · selected
알림 받기
일정 시작 1시간 전 알림
이메일 요약
복식단식지역
공개비공개
Sheet
surface #1A201B over the black α0.6 scrim
경기
공개 범위
공개
클럽 멤버에게만
비공개
적용
Toast
#232824 + 1px hairline · status glyph · ball action text
경기
홈
클럽
경기
기록
프로필
참석을 취소했어요
실행 취소
EmptyState · offline
disc #232824 · 32 glyph #A9B3AB
오프라인이에요
연결되면 자동으로다시 불러와요
다시 시도
Dark values this board derived, because library section 1 gives none — accepted 2026-10-09, gated at S1 (library section 13, K4)
Tonal fill, search field, disc#232824the dark block colour; visible on the canvas #0F1211 and on a sheet #1A201B
Input border, edge hairlineswhite α0.14the section 1 edge value; the same 1.4:1 the light #D4D4D4 gives
Cell hairlinewhite α0.09section 1
Danger solid button#FA9B94 fill · #0F1211 labelsection 1 gives the dark danger #FA9B94; white on it fails, so the label is the canvas colour
Switch off track · thumbwhite α0.24 · #DFE4DCno section 1 value; the thumb reads against both tracks
Switch on track · thumb#DFE4DC · #0F1211ink track, canvas thumb (the light switch inverted)
Backdropblack α0.6ink α0.4 would lighten a dark screen; black α0.6 matches the dim of the light one
Toast surface#232824 + 1px white α0.14the code keeps one inverse surface in both themes with a faint hairline in dark; lifted to #232824 so it reads on #0F1211
Focus ring, ball, ball text#DFE4DC · #D6F04A · #0D100Ering = dark ink; the ball is unchanged with ink text (section 1)
FVerification and judgement calls
What was measured, and every place the library could not be drawn exactly as written.
123 of 123 marked specimens measure to their spec. Each row is a rendered element carrying a `data-fk` mark and its library spec; a scratch script in a real browser (Chromium, window 1800) reads its border box. Text: 552 text nodes checked against the type scale (size / line-height), 0 off. Width-held pairs: btn-w 98.78 = 98.78. Clipped text: 0.
Button scale
84 marked buttons across the 12 tone × size combinations (primary, tonal, ghost, danger × sm, md, lg): sm 36 · r10 · pad 0 16, md 44 · r12 · pad 0 20, lg 52 · r12 · pad 0 20; type 15/22·600 at sm and md, 17/24·600 at lg; a ghost pads 0 12 at every size. 0 off the scale (14 flex-pair buttons are drawn with padding 0 by design).
EmptyState button
6 EmptyState buttons, all md 44 (5 tonal, 1 primary); the Search cancel is a ghost md 44 (the wizard 건너뛰기 is an sm tonal Button since the 2026-08-15 wizard-navigation LAW).
Toast clearance
16 between the toast and the tab bar when there is no FAB; with a FAB, 16 between the toast and the FAB and 16 between the FAB and the bar (the toast sits 80 above the bar). Toast height 48 in all three slices.
Toast glyph
5 status glyphs, each 20 × 20 and 12 before the text: success #75D1C6, warning #E1B75C, danger #FA9B94, info #C9B2F2 (info again on the dark #232824 surface); the surface itself stays ink.
Wizard progress
5 progress lines, 30 segments, all 2 tall inside the 56 header; every segment is ink (done + current) or #E5E5E5 (remaining): 0 ball segments.
Unchecked checkbox
2 unchecked squares draw a 2px #4F5851 edge; 6 checked squares are ink.
Offline strip
The one-line Notice and the offline strip measure 48.
componentspec w × hmeasured
Button / primary full width350 × 52350 × 52pass
Button / primary content widthauto × 52145.52 × 52pass
Button / size sm anatomy80 × 3680 × 36pass
Button / size md anatomy80 × 4480 × 44pass
Button / size lg anatomy80 × 5280 × 52pass
Button / ghostauto × 4479.44 × 44pass
Button / primary lg (states)auto × 5269.39 × 52pass
Button / tonal lg (states)auto × 5269.39 × 52pass
Button / danger lg (states)auto × 5269.39 × 52pass
Button / ghost md (states)auto × 4479.44 × 44pass
Button / tonal sm (states)auto × 3657.94 × 36pass
Button / primary smauto × 3657.94 × 36pass
Button / primary mdauto × 4465.94 × 44pass
Button / primary lgauto × 5269.39 × 52pass
Button / tonal smauto × 3657.94 × 36pass
Button / tonal mdauto × 4465.94 × 44pass
Button / tonal lgauto × 5269.39 × 52pass
Button / ghost smauto × 3679.44 × 36pass
Button / ghost mdauto × 4479.44 × 44pass
Button / ghost lgauto × 5286.81 × 52pass
Button / danger smauto × 3657.94 × 36pass
Button / danger mdauto × 4465.94 × 44pass
Button / danger lgauto × 5269.39 × 52pass
Button / primary (full)350 × 52350 × 52pass
Button / tonal (full)350 × 52350 × 52pass
Button / danger (full)350 × 52350 × 52pass
Button / rest (pair)auto × 5298.78 × 52pass
Button / loading (pair)auto × 5298.78 × 52pass
Button / pair tonalauto × 52112.67 × 52pass
Button / pair primaryauto × 52225.33 × 52pass
IconButton / ink anatomy44 × 4444 × 44pass
IconButton / ink44 × 4444 × 44pass
IconButton / secondary44 × 4444 × 44pass
IconButton / danger44 × 4444 × 44pass
Choice 36 anatomyauto × 3683.94 × 36pass
Choice 44 anatomyauto × 4467.94 × 44pass
Choice input modeauto × 3696.91 × 36pass
Choice 36 restingauto × 3659.94 × 36pass
Choice 36 selectedauto × 3683.94 × 36pass
Choice 44 restingauto × 4467.94 × 44pass
Choice 44 selectedauto × 4491.94 × 44pass
componentspec w × hmeasured
Segmented 2350 × 40350 × 40pass
Segmented 4350 × 40350 × 40pass
Switch off48 × 2848 × 28pass
Switch on48 × 2848 × 28pass
Input / control (glyphs)350 × 48350 × 48pass
Input / field (label + helper)350 × 92350 × 92pass
Input / rest350 × 48350 × 48pass
Input / required350 × 48350 × 48pass
Input / filled350 × 48350 × 48pass
Input / focus350 × 48350 × 48pass
Input / error350 × 48350 × 48pass
Input / disabled350 × 48350 × 48pass
Input / glyphs350 × 48350 × 48pass
TextArea / minimum350 × 120350 × 120pass
TextArea / empty350 × 120350 × 120pass
TextArea / required350 × 120350 × 120pass
TextArea / filled350 × 120350 × 120pass
TextArea / grown350 × 200350 × 200pass
TextArea / scrolling350 × 200350 × 200pass
TextArea / focus350 × 120350 × 120pass
TextArea / error350 × 120350 × 120pass
Search / filled350 × 44350 × 44pass
Search / rest350 × 44350 × 44pass
Search / header takeover390 × 56390 × 56pass
Stepper136 × 36136 × 36pass
Slider / single350 × 82350 × 82pass
Slider / band350 × 82350 × 82pass
Check rows / single390 × 144390 × 144pass
Check rows / multi390 × 144390 × 144pass
Picker sheet / time390 × 390390 × 390pass
Picker sheet / date390 × 390390 × 390pass
Sheet / title + content + CTA390 × 334390 × 334pass
Sheet / no CTA390 × 238390 × 238pass
Dialog / confirm350 × 188350 × 188pass
Dialog / destructive350 × 188350 × 188pass
Menu / five rows240 × 242240 × 242pass
Toast / plainauto × 48139.48 × 48pass
Toast / with actionauto × 48214.92 × 48pass
Toast / two linesauto × 68245.33 × 68pass
Coachmark / above280 × 116280 × 116pass
EmptyState / empty350 × 304350 × 304pass
componentspec w × hmeasured
EmptyState / error350 × 304350 × 304pass
EmptyState / offline350 × 304350 × 304pass
EmptyState / not found350 × 304350 × 304pass
EmptyState / primary350 × 304350 × 304pass
Skeleton / media row390 × 80390 × 80pass
Skeleton / person row390 × 64390 × 64pass
Skeleton / cell390 × 48390 × 48pass
Skeleton / strip390 × 80390 × 80pass
Skeleton / block390 × 144390 × 144pass
Skeleton / chart390 × 160390 × 160pass
Skeleton / hero390 × 160390 × 160pass
Spinner20 × 2020 × 20pass
Offline strip350 × 48350 × 48pass
Wizard / title350 × 32350 × 32pass
Wizard / field 1350 × 92350 × 92pass
Wizard / field 2350 × 118350 × 118pass
Wizard / CTA band390 × 118390 × 118pass
Wizard / header390 × 56390 × 56pass
Wizard / header (anatomy)390 × 56390 × 56pass
Field / input350 × 92350 × 92pass
Field / required unset350 × 92350 × 92pass
Field / choice set350 × 118350 × 118pass
Field / no helper350 × 70350 × 70pass
Field / textarea350 × 164350 × 164pass
Choice set / wrapped350 × 96350 × 96pass
AddRow350 × 56350 × 56pass
Notice / under a field (one line)350 × 48350 × 48pass
Notice / under a field (title + body)350 × 68350 × 68pass
PickerRow / two rows390 × 96390 × 96pass
RemoveIconButton / row390 × 48390 × 48pass
RemoveIconButton44 × 4444 × 44pass
Validation / error field350 × 92350 × 92pass
Confirm CTA band (pair)390 × 118390 × 118pass
Dark / primary350 × 52350 × 52pass
Dark / tonal350 × 52350 × 52pass
Dark / danger350 × 52350 × 52pass
Dark / input rest350 × 48350 × 48pass
Dark / switch on48 × 2848 × 28pass
Dark / sheet390 × 334390 × 334pass
Dark / toastauto × 48246.92 × 48pass
Dark / empty state350 × 304350 × 304pass
Toast clearanceResolved 2026-10-09 (library section 13, K1). The library says 16 above the tab bar and the FAB clears it; the code floated the toast 8 (TOAST_CHROME_GAP) above whatever chrome owns the bottom. Ruled and drawn: 16 above the tab bar, and 16 above a FAB when one is present (so 80 above the bar when one is on screen); the code's 8 changes to 16.
Unchecked checkbox edgeResolved (K2). The library gives the checked square (ink) and no unchecked one. Ruled and drawn as a 2px #4F5851 edge: 7.4:1 on white against the 3:1 WCAG 1.4.11 asks of a control boundary; the 1px #D4D4D4 hairline is 1.48:1 and an empty square has no text to carry it.
Button sizesResolved (K3). One size scale for every tone instead of size-by-tone: sm 36 · md 44 · lg 52. lg is for the CTA band and full-width form submits, md for inline actions (empty states, notices, sheet content), sm inside rows and notices. The EmptyState's button is md tonal; the former "small tonal" is the sm size.
Ghost button paddingResolved (K6). Button says content width pad 0 20. A ghost has no fill, so the padding is only hit area: pad 0 12 at every size, so a ghost label can end on the rail (the box bleeds 12).
Wizard progress lineResolved (K6). Drawn as the header's own last 2 (so header + line = 56 and the 4-grid holds), not an extra 2 under it. Ink for done + current, #E5E5E5 for remaining; the ball segment is gone. Non-text contrast: ball on white is 1.2:1 (1.28 computed) against the 3:1 the line needs, because it carries information.
Dialog pair ratioSection 8 says "CTA pair (tonal + primary)" and section 3 gives the pair as flex 1 : 2. Drawn 1 : 2 in the dialog too, with padding 0 on the pair so the ratio is exact (112.7 / 225.3 on 350).
Sheet content rowsCheck rows inside a sheet are full-bleed (margin 0 -20) so their text sits on the sheet's 20 rail like the title, and their hairlines inset to 20.
Offline stripResolved (library section 13, L4). A Notice has no minimum height in the library; 12 + 22 + 12 = 46 is off the 4-grid, so the strip (and a one-line Notice) is min 48, on the grid and equal to the Cell. Pinned strip drawn inset to the rail (350, r12), 8 under the header.
Spinner on dangerLibrary: ink spinner. On #B8332E ink is 3.4:1, so a loading danger button takes the label colour (white).
Option labels in check rowsCell labels are secondary ink, but an option row has no value to carry: its label is the content, so ink 400 (600 when chosen).
Toast status glyphResolved (K5). The code puts a 20 success / error / warning / info glyph in the toast (the surface never tints). Library section 8 omitted it; kept: 20, before the text, in the dark-theme tone values on the ink surface (success #75D1C6, warning #E1B75C, danger #FA9B94, info #C9B2F2). The light tone values fail on ink, and a status glyph is the state, not decoration.
Dark valuesAccepted, gated at S1 (K4). Section 1 gives canvas, surface, block, ink, secondary, hairlines, band, ball, success, warning, danger. Tonal fill, switch tracks and thumbs, backdrop, toast surface and the danger-button label are derived and listed under the dark strip; the owner accepted them on 2026-10-09 and they ship only behind the S1 dark contrast gate.
Resolved 2026-10-09 (owner: go with best practices and your recommendation) — K1 to K6 are ruled in library section 13. Owner’s word still needed → Adopt these controls, overlays, states and forms as the shared library?
Spec (grounding, anatomy)

Proposal (2026-10-09), Phase B of the flat grammar v2 migration: the controls, overlays, states and forms of docs/design/flat-v2-component-library-2026-10-09.md (sections 7 to 10), each drawn as a specimen at 1:1 on a 390 stage: anatomy with pt rulers, a states row (rest, pressed, hover, focus, disabled, loading, selected, error where they apply), variants, the section 2 behaviour and what it replaces, then a dark-mode strip of the same components through the section 1 dark values and a DOM verification table. Chrome, surfaces, rows and labels are the sibling board flat-v2-components. Built from local primitives only (Fk*): no WFPhone, WFCard, WFChip, WFBadge, WFSelectChip, WFInput or WFSwitch. The groundedIn list is the set of real components these controls replace.

route · packages/ui/src/button.tsx · pressable.tsx · pressable-visual.ts · motion.ts · selection-chip.tsx · labeled-field.tsx · search-bar.tsx · modal-panel.tsx · header-actions-menu.tsx · empty-state.tsx · skeleton.tsx · shimmer.ts · stepper.tsx · wizard-shell.tsx · add-card.tsx · picker-row.tsx · toast-host.tsx · query-error-state.tsx grounded in 18 source file(s)
packages/ui/src/button.tsxpackages/ui/src/pressable.tsxpackages/ui/src/pressable-visual.tspackages/ui/src/motion.tspackages/ui/src/selection-chip.tsxpackages/ui/src/labeled-field.tsxpackages/ui/src/search-bar.tsxpackages/ui/src/modal-panel.tsxpackages/ui/src/header-menu.tsxpackages/ui/src/empty-state.tsxpackages/ui/src/skeleton.tsxpackages/ui/src/shimmer.tspackages/ui/src/stepper.tsxpackages/ui/src/wizard-shell.tsxpackages/ui/src/add-card.tsxpackages/ui/src/picker-row.tsxpackages/app/src/presentation/components/toast-host.tsxpackages/app/src/presentation/components/query-error-state.tsx