← Cockpit Wireframes Pages by level Navigation map
Library — behaviour: text scaling, motion, accessibility▾
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 · 하단 고정
Approved 2026-10-11 — the build spec until it ships

Library — behaviour: text scaling, motion, accessibility

How every component presses, disables, scales, moves, speaks and sits on the screen: drawn once here, assumed by every page board.

Press, hover, focus

Every pressable answers a touch inside itself: it settles and dims, with no band over it. A pointer dims it, the keyboard rings it.

Button
참석하기
Rest the look on every page
참석하기
Pressed settles and dims; the dashed line is the rest size
참석하기
Hover dims; a real pointer only (web)
참석하기
Focus a ring drawn outside the control; keyboard only (web)
Pill
복식단식
Rest
복식
단식
Pressed
복식단식
Hover
복식단식
Focus
Row
올림픽공원 테니스장
19:00~21:00
6/8
Rest
올림픽공원 테니스장
19:00~21:00
6/8
Pressed
올림픽공원 테니스장
19:00~21:00
6/8
Hover
올림픽공원 테니스장
19:00~21:00
6/8
Focus
Icon
경기
Rest
경기
Pressed
경기
검색
Hover
경기
Focus
FAB
매치
Rest
매치
Pressed
매치
Hover
매치
Focus
Block
다음 일정내일, 9월 19일 (토)
일정 보기
09:00
올림픽공원 테니스장
☀️ 18°
Rest
다음 일정내일, 9월 19일 (토)
일정 보기
09:00
올림픽공원 테니스장
☀️ 18°
Pressed
다음 일정내일, 9월 19일 (토)
일정 보기
09:00
올림픽공원 테니스장
☀️ 18°
Hover
다음 일정내일, 9월 19일 (토)
일정 보기
09:00
올림픽공원 테니스장
☀️ 18°
Focus
Press physics (scale / dim)
button .97 / .92Button, ProviderButton (Kakao: dim .85 with no settle, its proportions are fixed).pill .96 / .85Choice.row .98 / .80Rows, Cells, roster cells, Strip columns.card .98 / .90Block: the whole block presses.icon .92 / .78IconButton, header icons, tab-bar items.fab .96 / .90FAB.TimingThe settle is a spring on the UI thread: pressIn {damping 30, stiffness 400, mass 0.7}, pressOut {26, 300, 0.7} (ζ 0.9, no visible overshoot, settled in about 196 and 226 ms). The dim runs 80 ms in and 150 ms out. Feedback starts within 100 ms of the touch.No press physicsA Switch (the toggle is the feedback). A map band presses with dim .85 and no settle.Hover (web)Real pointers only: opacity .85 over 100 ms (fadeFast) and the pointer cursor. A header icon also shows its registry name as a tooltip.Focus (web, keyboard)A 2 ink ring, offset 2, drawn outside the control; it appears at once and never animates; on a ball button the ring is still ink. An Input shows a 2 ink border instead. Sticky chrome never covers the ring (scroll-padding equals the bar heights).Reduce MotionPress feedback is the dim only: the settle is dropped. Hover and focus are unchanged.HapticsMark outcomes only (saved, confirmed, failed, landed on the waitlist, a value chosen), never a press, a tab switch, a scroll, a sheet opening or a toast.Replaces in codeVARIANTS in pressable-visual.ts (its row dim is .70, here .80); the press springs in motion.ts ({20, 400, 0.7} and {15, 300, 0.7}, here ζ 0.9); the ring is ink in the code too (one INK_FOCUS_RING; the primary-coloured FOCUS_RING is gone, batch 59).

Disabled, soft-disabled, loading

Three ways a control waits. Disabled is inert. Soft-disabled is grey but still answers. Loading keeps its label.

Disabled
취소저장 중…저장
In flight the sibling of the pressed action is inert
참석하기
Role wall inert at .5; no reason line beside it
Soft-disabled
1/5
클럽 이름을 알려주세요
클럽 이름
예: 강남 라켓클럽
9:41
다음
Waiting grey, focusable; a press shows what is missing
1/5
클럽 이름을 알려주세요
클럽 이름
예: 강남 라켓클럽
클럽 이름을 입력해 주세요
9:41
다음
ㅂㅈㄷㄱㅅㅛㅕㅑㅐㅔ
ㅁㄴㅇㄹㅎㅗㅓㅏㅣ
⇧ㅋㅌㅊㅍㅠㅜㅡ⌫
123스페이스완료
After a press the missing field takes focus and its line
1/5
클럽 이름을 알려주세요
클럽 이름
강남 라켓클럽
9:41
다음
ㅂㅈㄷㄱㅅㅛㅕㅑㅐㅔ
ㅁㄴㅇㄹㅎㅗㅓㅏㅣ
⇧ㅋㅌㅊㅍㅠㅜㅡ⌫
123스페이스완료
Ready the primary turns ball once the name is in
Loading
저장저장 중…저장
Primary an ink spinner beside the in-progress label
초기화초기화 중…초기화
Tonal ink on grey
다시 보내기보내는 중…다시 보내기
Ghost ink on no fill
삭제삭제 중…삭제
Danger white on the danger fill
Disabled, soft-disabled, loading
DisabledOpacity .5, not-allowed. Inert, aria-disabled. A disabled primary draws no reason line (1.16): what the screen already shows says why. Use it only where nothing the person can do on this screen helps (a role wall), and for the siblings of an action in flight.Soft-disabledgray100 fill, a secondary 600 label (6.59:1), the same size as the primary. Focusable, aria-disabled="true", announced with its reason. A press gives a haptic, focuses the missing control and shows its danger line; the primary becomes ball when the requirement is met. Use it for the primary of a step with exactly one pending decision whose missing part is visible (one field, one option, one consent).Enabled and validatingThe normal primary. A press validates, focuses and announces the first invalid field. Use it for two or more pending inputs, or when the missing part is off-screen (long forms, the review page).LoadingA small spinner beside the in-progress label ("저장 중…"), full opacity, the width held at the wider of the two labels. Ink spinner on ball, tonal and ghost; white on danger. Inert, aria-busy; the accessible name follows the visible label. Use it for any action that waits on the network.SpinnerA spinner appears inside a button only. Pull to refresh uses the native control; a failed refresh shows a Notice.No helper under the bandA soft-disabled primary draws no helper (1.16). What is missing is the field's error on the press, in the field's own words.

Text scaling

Text follows the system size from 85% to 200%. Each kind of text stops growing at its own cap, and from 150% the layouts stack so nothing is cut.

List
강남 라켓클럽
다음 일정
전체 보기
올림픽공원 테니스장
09:00~11:00
6/8
대치유수지체육공원
19:00~21:00
마감 임박
양재 시민의숲 코트
일요일 14:00~16:00
2/4
멤버24명
전체 보기
김민준
총무
이서연
운영진
박
박지훈
멤버
9:41
100% MediaRow and PersonRow
강남 라켓클럽
다음 일정
전체 보기
올림픽공원 테니스장
09:00~11:00
6/8
대치유수지체육공원
19:00~21:00
마감 임박
양재 시민의숲 코트
일요일 14:00~16:00
2/4
멤버24명
전체 보기
김민준
총무
이서연
운영진
박
박지훈
멤버
9:41
130% the same layout, larger text
강남 라켓클럽
다음 일정
전체 보기
올림픽공원 테니스장
09:00~11:00
6/8
대치유수지체육공원
19:00~21:00
마감 임박
양재 시민의숲 코트
일요일 14:00~16:00
2/4
멤버24명
전체 보기
김민준
총무
이서연
운영진
박
박지훈
멤버
9:41
150% trailing elements drop under line 2
강남 라켓클럽
다음 일정
전체 보기
올림픽공원 테니스장
09:00~11:00
6/8
대치유수지체육공원
19:00~21:00
마감 임박
양재 시민의숲 코트
일요일 14:00~16:00
2/4
멤버24명
전체 보기
김민준
총무
이서연
운영진
박
박지훈
멤버
9:41
200% the header holds 56; names wrap, never cut
Detail
강남 라켓클럽

올림픽공원 테니스장

9월 19일 (토)09:00~11:00
송파구 올림픽로 424하드 4면
참가비 10,000원
정보
종목
복식
대상
NTRP 3.0–4.0
호스트
김민준
취소 규정
시작 24시간 전까지 무료 취소
9:41
참석하기
100% Hero, Cells and the CTA band
강남 라켓클럽

올림픽공원 테니스장

9월 19일 (토)09:00~11:00
송파구 올림픽로 424하드 4면
참가비 10,000원
정보
종목
복식
대상
NTRP 3.0–4.0
호스트
김민준
취소 규정
시작 24시간 전까지 무료 취소
9:41
참석하기
130% the hero and the Cells grow in place
강남 라켓클럽

올림픽공원 테니스장

9월 19일 (토)09:00~11:00
송파구 올림픽로 424하드 4면
참가비 10,000원
정보
종목
복식
대상
NTRP 3.0–4.0
호스트
김민준
취소 규정
시작 24시간 전까지 무료 취소
9:41
참석하기
150% Cells stack: the label over its value
강남 라켓클럽

올림픽공원 테니스장

9월 19일 (토)09:00~11:00
송파구 올림픽로 424하드 4면
참가비 10,000원
정보
종목
복식
대상
NTRP 3.0–4.0
호스트
김민준
취소 규정
시작 24시간 전까지 무료 취소
9:41
참석하기
200% the band grows with its button
Form
3/6
어떤 경기인가요?
종목
단식복식혼합복식팀전
모집 인원
8
명
한 줄 소개(선택)
예: 매주 토요일 오전
9:41
다음
100% Choice set and Fields
3/6
어떤 경기인가요?
종목
단식복식
혼합복식팀전
모집 인원
8
명
한 줄 소개(선택)
예: 매주 토요일 오전
9:41
다음
130% fields and choices grow
3/6
어떤 경기인가요?
종목
단식복식
혼합복식팀전
모집 인원
8
명
한 줄 소개(선택)
예: 매주 토요일 오전
9:41
다음
150% the same layout; nothing clips
3/6
어떤 경기인가요?
종목
단식복식
혼합복식팀전
모집 인원
8
명
한 줄 소개(선택)
예: 매주 토요일 오전
9:41
다음
200% the title stops at 1.7; choices wrap
Caps by class
reading
참석 4명, 예비 2명
label
마감 임박
hero
올림픽공원
numeral
1,482
chrome
경기
100% each class at its own size
reading
참석 4명, 예비 2명
label
마감 임박
hero
올림픽공원
numeral
1,482
chrome
경기
130% chrome has reached its cap
reading
참석 4명, 예비 2명
label
마감 임박
hero
올림픽공원
numeral
1,482
chrome
경기
150% numeral stops here
reading
참석 4명, 예비 2명
label
마감 임박
hero
올림픽공원
numeral
1,482
chrome
경기
200% hero stopped at 1.7; reading and label go on
Stacking from 150%
대치유수지체육공원
19:00~21:00
마감 임박
김민준
총무
Rows 100%
대치유수지체육공원
19:00~21:00
마감 임박
김민준
총무
Rows 150% the trailing element drops under line 2
취소 규정
시작 24시간 전까지 무료 취소
호스트
김민준
Cell 100%
취소 규정
시작 24시간 전까지 무료 취소
호스트
김민준
Cell 150% the label sits over its value
호스트
김민준
3.5
이서연
4.0
박
박지훈
3.5
최유나
3.0
Roster 100%
호스트
김민준
3.5
이서연
4.0
박
박지훈
3.5
최유나
3.0
Roster 150% two rows of two
총 경기47
승률68%
연승3
ELO1,482
Strip 100%
총 경기47
승률68%
연승3
ELO1,482
Strip 150% reflows to 2-up when a figure does not fit
참석을 취소할까요?시작 24시간 전까지는 무료로 취소할 수 있어요
닫기참석 취소
Dialog 100%
참석을 취소할까요?시작 24시간 전까지는 무료로 취소할 수 있어요
닫기참석 취소
Dialog 150% the buttons stack, the primary on top
마감이 가까워요
지금 신청하기
참석했어요
되돌리기
Notice, Toast 100%
마감이 가까워요
지금 신청하기
참석했어요
되돌리기
Notice, Toast 150% the action drops under the text
9월 19일 (토)내일
내일 일정2개
전체 보기
Day header 100%
9월 19일 (토)내일
내일 일정2개
전체 보기
Day header 150% the relative day drops under the date
예정매치 찾기테니스장
매치
홈클럽경기기록프로필
Chrome 100%
예정매치 찾기테니스장
매치
홈클럽경기기록프로필
Chrome 200% tabs, FAB and tab bar stay on one line
Names are never cut
이서연 시니어 클럽 회장 겸 총무
운영진
100% one line
이서연 시니어 클럽 회장 겸 총무
운영진
150% stacked: the name has the full width
이서연 시니어 클럽 회장 겸 총무
운영진
200% wraps, three lines at most; never an ellipsis
AM
Alexandria Montgomery-Hargreaves
Host
English, 320, 200% three lines, no sideways scroll
Text scaling
Range85% to 200% on both platforms, following the OS. iOS xSmall to AX2 follow Apple's per-style curves; AX3 to AX5 render the AX2 layout. Android 0.85 to 2.0.reading, cap 2.0iOS dynamicTypeRamp headline, subheadline, footnote, caption1. reading, body, headline, title as a content heading, caption; day header, section title, row text, Cell, input text, lg and md button label, notice, toast, dialog, block meta.label, cap 2.0iOS ramp footnote, subheadline. Footnote and wrapping footnote: Tag, level numeral, Choice, sm button label, section action, field label, support line, block eyebrow.hero, cap 1.7iOS ramp title1. The hero role: detail title, identity name, wizard step title.numeral, cap 1.5iOS ramp largeTitle. display, and the strip figures.chrome, cap 1.3iOS ramp title3, headline, subheadline, caption2. Tab-root and pushed header title, header mode controls, scope-tab label, FAB label, tab-bar label, count Tag, chart labels.One tableEach text role belongs to one class; one table in packages/ui/src/text-scale.ts feeds the textRole variants (maxFontSizeMultiplier on every platform, dynamicTypeRamp on iOS; the smaller of the two wins) and a parity test walks every role.Size at 100 / 130 / 150 / 200reading 15 → 15 · 19.5 · 22.5 · 30. label 13 → 13 · 16.9 · 19.5 · 26. hero 24 → 24 · 31.2 · 36 · 40.8. numeral 40 → 40 · 52 · 60 · 60. chrome 20 → 20 · 26 · 26 · 26. Line heights keep their ratio, rounded to whole points.Never offallowFontScaling={false} appears only on avatar initials inside their fixed circle and on the iOS tab-bar label. A lint flags every other use (the escape is a same-line // scale-exempt: reason). adjustsFontSizeToFit is banned.Heights are floorsEvery number on a text-bearing box is a minHeight with the padding held. The header (56) and the tab bar (64 + inset) never grow; everything else grows with its text.Stacked from 1.5×scale = min(fontScale, 2.0) and stacked = scale ≥ 1.5, from useTextScale(). Only the components in the frames branch on it: MediaRow (trailing under line 2, 8 below; line 2 may take 3 lines; a picked row keeps its ring on line 1 and its trash at the end), PersonRow (figure or Tag under line 2, 4 below; the rank column stays), Cell (label over value, 4 apart, a second line 4 under the value), Strip (labels wrap; a column whose figure does not fit reflows 2-up), Roster (two rows of two; the door stays last), Segmented (radio Cells, vertical), Dialog (stacked, primary on top), Notice and Toast (action under the text), Day header (relative day under the date). Unchanged: Section head (the title wraps, the Door pill keeps its line), scope tabs and FAB (the 1.3 cap keeps them on one line).Wrap versus clampNames never cut: person names wrap to 2 lines, 3 from 1.5×. Everything else wraps before it clamps, and a clamp is allowed only where the full text is one press away and the press is signalled; the accessible label always carries the full text. Venue and club names: 2 lines (3 from 1.5×), then a tail ellipsis. L1 time, counts, amounts: one line, never an ellipsis. Button, Choice, Cell label and value, Notice, Toast: wrap, never an ellipsis (a toast that needs more is a Notice). Tag, count Tag, level numeral: one line, flexShrink 0. Block meta and explanatory copy: 2 lines (3 from 1.5×). Header title: one line with a tail ellipsis; the hero repeats it.Glyphs and imagesInline glyphs beside text (hero fact 20, notice glyph 20, Choice check 16, FAB icon 20) grow by min(scale, 1.5). Header glyphs, tab-bar icons, chevrons, avatars, thumbs, map tiles and dots do not scale. The Level tag circle grows with its numeral: 24 · 32 · 36 · 36, and the count Tag with its own (chrome cap 1.3): 20 · 28 · 28 · 28. A Tag, count, glyph or dot beside text attaches through the line slot, so it stays centred on the label’s first line at every size (Foundations, inline adornments).ChromeThe iOS tab-bar label does not scale and relies on the Large Content Viewer (accessibilityShowsLargeContentViewer, accessibilityLargeContentTitle); the same props are available to header icon buttons. On Android a custom tabBarLabel caps the label at 1.3.Bold Text (iOS)One step up inside the four loaded weights (400 to 600, 500 to 600, 600 and 700 to 700), in Text only.WebType is in px; page zoom and reflow at 320 CSS px keep it usable. A text-spacing check (line 1.5, paragraph 2, letter 0.12, word 0.16) covers three real screens.Stops to verify100, 130, 150 and 200%, plus a 320-wide frame at 200%. Device rung: adb shell settings put system font_scale 2.0; xcrun simctl ui booted content_size accessibility-large.Replaces in codeNo scaling policy ships today: no maxFontSizeMultiplier, dynamicTypeRamp or useTextScale anywhere in packages/ or apps/ (the roles are in packages/ui/src/text.tsx).

Motion

Motion has a purpose or it is absent. Effects fade, spatial moves may spring, nothing makes a person wait, and Reduce Motion replaces a move with a short fade.

Durations and curves
0100200300 ms
exitFast
hairline, hover, menu out, toast out, switch track
100
fast
skeleton to content, header title, menu in, image fade, switch thumb
150
toggle, modalExit
chevron turn, FAB width, notice collapse, sheet and dialog exit
200
entrance
list reveal, expand reveal, toast in
250
modal, slow
sheet rise, takeover rise, progress fill
300
the five durations; only loops and platform pushes run longer
standard
anything that stays on screen: a tap, a hover, a collapse
enter
things entering: a sheet, a toast, a reveal
exit
things leaving: it accelerates out
press spring
the press (solid) and its release (dashed): quick, no visible overshoot
three easings, and the press spring
Sheet rise and exit
정렬
가까운 순
시작 시간순
참석자 많은 순
Start the page
정렬
가까운 순
시작 시간순
참석자 많은 순
Halfway, 37 ms rising and fading in; it slows as it lands
정렬
가까운 순
시작 시간순
참석자 많은 순
End, 300 ms open; exit reverses it in 200 ms
정렬
가까운 순
시작 시간순
참석자 많은 순
Reduce Motion, 30 ms no rise: it fades in place
FAB collapse
매치
Start extended
Halfway, 140 ms the label has left; the width is halfway
End, 300 ms collapsed; expanding reverses it
instant swap
Reduce Motion the width jumps; the label fades in 150 ms
Header title on scroll
9:41
Start over the photo, no title
9:41
올림픽공원 테니스장
9:41
Halfway, 30 ms the title and solid layer fade together
올림픽공원 테니스장
9:41
End, 150 ms solid, with the title and the edge line
already a fade
Reduce Motion unchanged
예비 to 참석, your own move
참석
3/85자리 남음
호스트
김민준
3.5
이서연
4.0
박
박지훈
3.5
예비1명
나
예비 1번
Start you wait
참석
4/84자리 남음
호스트
김민준
3.5
나
3.5
이서연
4.0
박
박지훈
3.5
예비1명
나
예비 1번
Halfway, 40 ms your row has left; the others glide
참석
4/84자리 남음
호스트
김민준
3.5
나
3.5
이서연
4.0
박
박지훈
3.5
End, 250 ms you are in; the 예비 section is gone
참석
4/84자리 남음
호스트
김민준
3.5
나
3.5
이서연
4.0
박
박지훈
3.5
Reduce Motion, 30 ms no glide: your cell fades in place
Toast in and out
참석 취소
Start the band
참석 취소
참석했어요
되돌리기
Halfway, 31 ms rising and fading in
참석 취소
참석했어요
되돌리기
End, 250 ms at rest; it leaves by fading in 100 ms
참석 취소
참석했어요
되돌리기
Reduce Motion, 30 ms no rise: it fades in place
Skeleton to content
Start one skeleton for the screen
올림픽공원 테니스장
09:00~11:00
6/8
대치유수지체육공원
19:00~21:00
4/4
양재 시민의숲 코트
14:00~16:00
2/4
Halfway, 30 ms a cross-fade
올림픽공원 테니스장
09:00~11:00
6/8
대치유수지체육공원
19:00~21:00
4/4
양재 시민의숲 코트
14:00~16:00
2/4
End, 150 ms the content
already a fade
Reduce Motion unchanged; the shimmer rests
Virtualised lists
올림픽공원 테니스장
09:00~11:00
6/8
대치유수지체육공원
19:00~21:00
4/4
양재 시민의숲 코트
14:00~16:00
2/4
Before a poll brings a new session
nothing animates
Between there is no frame between
반포 한강 테니스장
07:00~09:00
1/8
올림픽공원 테니스장
09:00~11:00
6/8
대치유수지체육공원
19:00~21:00
4/4
After applied in one frame
no entering, exiting or layout transition; a lint rejects them in list items
Reduce Motion identical: the list already snaps
Motion tokens
DurationsexitFast 100 · fast 150 · toggle and modalExit 200 · entrance 250 · modal and slow 300 ms; pressIn 80 and pressOut 150; shimmer 800 and pulseDot 1600 loop, each stopping after 5 s per mount. Nothing animates longer than 300 ms except loops and the platform's own push. Feedback starts within 100 ms of the touch.Used forexitFast: edge hairline, hover, menu out, toast out, Switch track, Choice and checkbox colour, FAB label out and hide. fast: skeleton to content, scroll-linked header title, FAB label in, menu in, Notice dismiss, image fade, Switch thumb, wizard progress. toggle and modalExit: chevron turn, FAB width, Notice height collapse, sheet and dialog exit. entrance: RevealStack item, ExpandReveal, toast enter. modal and slow: sheet rise, takeover rise, ProgressBar fill.Easingstandard cubic-bezier(0.2, 0, 0, 1) for anything that stays on screen · enter (0, 0, 0, 1) for things entering · exit (0.3, 0, 1, 1) for things leaving · emphasizedEnter (0.05, 0.7, 0.1, 1) held in reserve for the sheet rise.SpringspressIn {damping 30, stiffness 400, mass 0.7} · pressOut {26, 300, 0.7} (ζ 0.9, no visible overshoot, settled in about 196 and 226 ms) · navSlide {22, 280, 0.8}. Effects (opacity, colour) use timing and never overshoot; the Tamagui springs quick, medium and slow drive spatial motion only.Named animationsfade (timing 150, standard) · fadeFast (100) · sheetIn (300, enter) · reveal (250).RulesAnimate transform and opacity; a layout property only on a single element (the FAB width). At most 100 animated components at once. Nothing makes a person wait: every demo accepts input while it plays.The framesEach in-between frame is drawn at the moment the movement is halfway along its curve, so its time is the curve's: the enter curve is halfway after 12.5% of its duration, the standard curve after 20%.
Every interaction, and Reduce Motion
PressSettle and dim, springs and dim as in Press physics. Reduce Motion: dim only.Hover (web)Dim to .85, 100 ms fadeFast. Unchanged.Focus ringAppears at once. Unchanged.Screen push and popThe native stack default (iOS slide with swipe; Android translate 450 ms or scale 200 ms). The system decides.Tab-bar switchNone; screens detach. Unchanged.Scope-tab switchThe pane swaps at once; the underline slides on navSlide (about 311 ms). Reduce Motion: the underline cross-fades in 150 ms.Sheet48 rise and platform fade; rise 300 ms sheetIn, exit 200 ms. Reduce Motion: fade 150 ms, no rise.DialogPlatform fade, no scale. Unchanged.Menu (600 and up)Fade, no translate or scale; in 150 fade, out 100 fadeFast. Unchanged.ToastRise 12 and fade in, fade out, the drag follows the finger; in 250 reveal, out 100 exitFast. Reduce Motion: fade in 150 and out 100, no rise.Scroll-linked header titleThe title and the solid layer fade together, 150 ms fade, flipping with hysteresis 24. Unchanged.Edge hairlineFade, 100 ms fadeFast. Unchanged.FAB collapse and expandLabel out, then width; expand reverses. Label 100 out and 150 in, width 200 standard. Reduce Motion: instant width, the label fades 150 ms.Pull to refreshThe native RefreshControl, a spinner only for a user pull, tint ink. Unchanged.SwitchThe thumb slides and the track recolours: thumb 150 fade, track 100 exitFast. Reduce Motion: the thumb jumps, the track fades 100 ms.Choice, checkbox, radioThe fill colour changes and the glyph appears at once, 100 ms exitFast. Unchanged.ExpandToggleThe chevron turns 180° (200 toggle) and the reveal slides 8 (250 reveal). Reduce Motion: the chevron at its end state; the reveal fades 150 ms.Notice dismissOpacity then height collapse, 100 + 150 ms. Reduce Motion: opacity only, 100 ms.Skeleton to contentCross-fade 150 ms. Unchanged; the shimmer is static at .75.Wizard stepRevealStack reveals in place (250 reveal); progress segments recolour (150 fade). Reduce Motion: the reveal fades 150 ms; the continuous bar is instant.Image loadPlaceholder pulse, then a 150 ms fade in. Reduce Motion: a static placeholder, fade 150 ms.Chart scrubThe marker follows the finger, no duration. Unchanged.Live dotPulse, 1600 ms loop, 5 s per mount, then solid. Reduce Motion: solid.
Reduce Motion, lists, in-place changes
Replaces, does not cutTranslate, scale and layout are removed and the same element appears with a 100 to 150 ms fade (sheet, dialog, menu, toast, FAB label, header title). Opacity fades keep their duration. Press feedback is dim only. Loops rest on a static frame. Gesture-tracking motion (toast drag, chart scrub, scroll-linked transforms) is unchanged because it mirrors the person's own gesture. Platform transitions follow the system (prefersCrossFadeTransitions on iOS).Virtualised listsFeedList and the rest take no entering, exiting or layout transition, no stagger and no LayoutAnimation; data changes apply in one frame. A lint rejects them inside list item renderers.Short fixed stacksA stack of at most 24 children (roster grid, avatar row, a Cell group) may move siblings with a 200 ms LinearTransition; the mover fades out 100 ms and in 150 ms.Whose changeYour own action (RSVP, cancel, remove): optimistic apply, success haptic, the toast announces. Someone else's change (poll, push): never animate; snap.In-place changesA Tag label or tone, a count, a Cell value, a score and a live counter change instantly: the state is the message. The ProgressBar fill (the ELO bar) uses slow 300 ms with Easing.out(Easing.cubic); the wizard bar uses fast 150 ms.Never animateAnything the person did not cause; anything inside a virtualised list item; live scores and counters; text while it is typed; validation errors (no shake); the focus ring; the tab switch; layout properties inside scroll handlers.Replaces in codemotion.ts holds the tokens: its press springs are {20, 400, 0.7} and {15, 300, 0.7} (here {30, 400, 0.7} and {26, 300, 0.7}), and useReducedMotion() there removes the scale and the move without a fade. MOTION_STAGGER and staggerDelay drive a list entrance that virtualised lists must not use.
Why these values: references
Durations100 to 300 ms is Material 3 short2 to medium2 and Seed d2 to d6 exactly; Seed calls up to 200 ms micro and above macro. Nothing of ours exceeds 300 except platform pushes and loops.EasingThe three curves are Material 3 EasingStandard, StandardDecelerate and StandardAccelerate (Seed uses 0.35, 0, 0.35, 1 and its siblings).Effects, no overshootMaterial 3's effects springs have a damping ratio of 1.0, standard and expressive alike: no overshoot on colour or opacity.PressColour first, scale optional (Seed: scale does not happen when the OS reduce-motion setting is on; HIG Motion: "Make motion optional"). The press spring is the standard class, ζ 0.9: Compose describes it as "basic motion scheme for utilitarian UI elements and recurring interactions"; the expressive class (ζ 0.6) is for hero interactions.Reduce MotionHIG Accessibility asks for "Replacing transitions in x-, y-, and z-axes with fades"; Apple's Reduced Motion criteria warn that "Removing animations entirely can have a negative effect on usability and understandability" and suggest a dissolve, a highlight fade or a colour shift.LoopsWCAG 2.2.2 (A): movement that starts on its own and runs over 5 s needs a way to stop; Apple's criteria ask to consider stopping ongoing motion.ToastsSeed snackbar defaults to 4 s and longer for long text; Material 3 snackbars run 4 to 10 s; HIG Accessibility: "Minimize use of time-boxed interface elements".SourcesApple HIG Motion (2025-09-09) and Accessibility (2025-06-09); App Store Connect evaluation criteria for Reduced Motion, Sufficient Contrast, Differentiate Without Color Alone and Larger Text; Material 3 MotionTokens.kt, MotionScheme.kt and the MDC Motion guide; Seed motion, feedback scale and inclusive design; WCAG 2.2; Android accessibility design; React Native AccessibilityInfo. Read in the research note owner-review-1-lane-a, §8.

Accessibility semantics

A screen reader walks the same screen the eye does: one stop per row, one h1, names composed from the row's own data, and focus that goes back where it came from.

강남 라켓클럽

올림픽공원 테니스장

9월 19일 (토)09:00~11:00
송파구 올림픽로 424하드 4면
참가비 10,000원
참석
6/82자리 남음
전체 보기
호스트
김민준
3.5
이서연
4.0
박
박지훈
3.5
+3
외 3명
정보
종목
복식
대상
NTRP 3.0–4.0
9:41
참석하기
Spoken, in order
1뒤로
2공유
3저장
4강남 라켓클럽
5올림픽공원 테니스장heading 1
69월 19일 토요일, 오전 9시부터 11시까지
7송파구 올림픽로 424, 하드 4면
8참가비 10,000원
9참석, 8명 중 6명, 2자리 남음heading 2
일정 상세 18 stops in visual order, one h1, two h2
One stop per row
올림픽공원 테니스장
19:00~21:00
복식NTRP 3.0–4.0
6/8
Spoken
내일 9월 19일 토요일, 오후 7시부터 9시까지, 올림픽공원 테니스장, 복식, NTRP 3.0부터 4.0, 8명 중 6명 참석
Session row thumb, count and breaks are hidden
김민준
총무
Spoken
김민준, 총무, NTRP 3.5
김민준 메뉴
Row with a control a real control keeps its own stop
종목
복식
프로필 공개
클럽 멤버에게만
경기 알림
Spoken
종목, 복식
프로필 공개, 클럽 멤버에게만
경기 알림, 스위치 켬
Cells no control type or state in the name
Status: polite, it waits its turn
참석했어요
되돌리기
Spoken
참석했어요, 되돌리기
Result toast the message, then the action
오프라인이에요
다시 시도
Spoken
오프라인이에요, 다시 시도
Offline strip a Notice that appears on its own
클럽 이름
예: 강남 라켓클럽
클럽 이름을 입력해 주세요
Spoken
클럽 이름, 필수, 입력 오류, 클럽 이름을 입력해 주세요
Field error when the person leaves the field
Alert: assertive, it interrupts
참석하지 못했어요. 다시 시도해 주세요
참석하기
Spoken
참석하지 못했어요. 다시 시도해 주세요
Danger helper a failure the person caused
결제에 실패했어요카드를 확인하고 다시 시도해 주세요
Spoken
결제에 실패했어요, 카드를 확인하고 다시 시도해 주세요
Danger Notice a warning or danger Notice
클럽 이름
예: 강남 라켓클럽
클럽 이름을 입력해 주세요
Spoken
클럽 이름, 필수, 입력 오류, 클럽 이름을 입력해 주세요
Submit with errors the first invalid field takes focus
A block, and a wait
다음 일정내일, 9월 19일 (토)
일정 보기
09:00
올림픽공원 테니스장
☀️ 18°
Spoken
다음 일정, 내일 9월 19일 토요일 오전 9시, 올림픽공원 테니스장, 8명 중 6명 참석, 맑음 18도
Block one stop; the bar and the chevron are hidden
Spoken
(nothing: a finished load says nothing unless it fails)
Busy aria-busy on the container; no announcement
Focus returns
정렬
올림픽공원 테니스장
09:00~11:00
6/8
대치유수지체육공원
19:00~21:00
4/4
Tap the trigger has focus and is activated
정렬
올림픽공원 테니스장
09:00~11:00
6/8
대치유수지체육공원
19:00~21:00
4/4
정렬 기준
가까운 순
시작 시간순
참석자 많은 순
Open focus moves to the sheet's title
정렬
올림픽공원 테니스장
09:00~11:00
6/8
대치유수지체육공원
19:00~21:00
4/4
Closed focus is back on the trigger
참석을 취소할까요?시작 24시간 전까지는 무료로 취소할 수 있어요
닫기참석 취소
Dialog a confirm starts on its safe action
Drag has an alternative (WCAG 2.5.7)
09:00~13:00
시작09:00
끝13:00
Slider tap the track, or step with ±
9월 셋째 주
일13
월14
화15
수16
목17
금18
토19
Week strip swipe the week, or tap ‹ ›
일정 상세: the focus order
1backbutton뒤로
2sharebutton공유
3savebutton저장
header titleheadinghidden until it fades in as the page scrolls
4eyebrowtext강남 라켓클럽
5hero titleheading 1올림픽공원 테니스장 (the one visible h1)
6fact: whentext9월 19일 토요일, 오전 9시부터 11시까지
7fact: wheretext송파구 올림픽로 424, 하드 4면
8fact: feetext참가비 10,000원 (a Tag is read with its fact)
9section headheading 2참석, 8명 중 6명, 2자리 남음
10section doorlink전체 보기, 참석
11roster cellbutton김민준, 호스트
12roster cellbutton이서연, NTRP 4.0
13roster cellbutton박지훈, NTRP 3.5
14roster doorbutton외 3명, 전체 보기
15section headheading 2정보
16Celltext group종목, 복식
17Celltext group대상, NTRP 3.0부터 4.0
18CTA buttonbutton참석하기
Accessibility semantics
One stop per rowA row, block, roster cell, Strip column or Cell is one accessibility element; its children (thumb, Tag, count, chevron, glyphs, MetaBreak) are hidden from assistive tech because the name carries them. A row with a real second control keeps two stops, the row then the control, the control named for its owner. A name never includes the control type ("버튼"). State goes in aria-selected, aria-checked, aria-pressed, aria-expanded and aria-busy, never in the name; a switch's name does not change with its state.Composed namesA function beside the visible formatter builds the name from the same view model the row renders; nothing hand-types a second string. Order: urgent status first (진행 중, 마감 임박), then when, where, kind, rating, count, and last the viewer's own state; about six facts; joined with a comma and a space.Speech for symbols9월 19일 (토) → 9월 19일 토요일 (오늘 or 내일 first within a week) · 09:00~11:00 → 오전 9시부터 11시까지 (오후 7시 30분 on a half hour) · 4/8 → 8명 중 4명 참석 · NTRP 3.0–4.0 → NTRP 3.0부터 4.0 · 68% → 68퍼센트 · 30,000원, 3위, 1,482 unchanged · the middle dot, ~, /, an en dash and MetaBreak are never spoken.Label in NameA control with one visible label starts its name with that text (WCAG 2.5.3): buttons, tabs, FAB, Choice. A loading button's name follows its in-progress label.Headings and landmarksLevels: 1 the screen title, 2 hero title and section titles, 3 inside a section. Exactly one visible h1 per screen: on a detail page the hero title is the h1 and the header's title is aria-hidden until it fades in. Day headers are h2 on a page with no section heads, h3 inside one. Web: header banner, nav labelled "주 메뉴", one main, aside for a desktop pane, a unique label on a repeated landmark, and a "본문으로 건너뛰기" skip link.Focus orderThe visual order: header, scope tabs, content, FAB, CTA band, tab bar. The FAB is reached after the list; its name keeps it findable by Voice Control. While a sheet, dialog or menu is open nothing behind it is reachable. Decorative glyphs and dots are hidden.Status (polite)role="status"; Android aria-live="polite"; iOS a queued announcement. A result toast, a Notice inserted after load, the offline strip, a field error that appears on leaving the field. A toast shows its message and then its action label.Alert (assertive)role="alert"; Android aria-live="assertive"; iOS an interrupting announcement. A failure the person caused: the CTA danger helper, a warning or danger Notice; on submit the first invalid field takes focus and its error is announced.Busyaria-busy on the container; no announcement. A finished load and a finished refresh say nothing unless they fail. aria-live reaches TalkBack only, so iOS announces explicitly (announceForAccessibility).Focus on open and closeSheet: the title is the first element; closing returns to the trigger. Confirm dialog: the least destructive action (or the title), the safe action before the confirming one in order; closing returns to the trigger. Menu: the first item; the trigger on close, Escape on web. Search takeover: the input; the trigger row on close. Wizard next or back: the new step's title. A removed row: the next row, or the section head when none remains. Submit with errors: the first invalid field, with its error announced. Use sendAccessibilityEvent(ref, 'focus'); setAccessibilityFocus is deprecated.TargetsIcon buttons are 48 and smaller visuals keep a 44 floor by hit slop; the WCAG 2.5.8 minimum of 24 is the absolute floor; a visual under 24 needs an equivalent control.Gesture alternativesToast swipe: a tap, or the × on an action toast. Sliders (TimeBandSlider, RangeBandSlider, Slider): tap the track to move the nearest thumb, plus the 44 Stepper for ± (WCAG 2.5.7), and adjustableActionProps for screen readers. Week strip: the ‹ › buttons. Photo-tile reorder: 앞으로 and 뒤로 이동 in the tile's menu. Chart scrub: tap a point to set the readout, and stepping actions. Sheets: an explicit close. Pull to refresh: the system control.LanguageThe in-app locale is independent of the OS: set accessibilityLanguage from the locale store on each screen root (iOS); web sets lang on html.Web keyboardTabs: roving tabindex, Left and Right, Home and End, automatic activation. Menu button: Enter, Space or Down opens and focuses the first item; Escape closes and returns focus. Dialog: Tab stays inside, Escape closes (on a confirm dialog Escape is its cancel; the scrim does not dismiss it), aria-modal, title through aria-labelledby, alertdialog for destructive confirms. Switch: Space. Radio group and Segmented: arrows move and check.FormsThe label is the input's name. The support line carries the error (no hint, 1.16), which is inline, announced when it appears, and never a toast. React Native has no aria-invalid, so the name suffix "입력 오류" stays. The required asterisk is hidden from assistive tech and "필수" joins the name; no legend is drawn. Choice groups carry a group label; Segmented is a radiogroup.OS settings readReduce Motion, the cross-fade preference and Bold Text. With Increase Contrast or a high-contrast text setting on, tertiary text and the cell hairline are promoted to the secondary and edge tiers. Reduce Transparency changes nothing: scrims only.Also requiredWizards never ask the same value twice (prefill from the profile or the last session). Email sign-in allows paste and password managers. A help entry sits in the same place on every screen.iOS build gateFive tasks (find and RSVP, pay the fee, confirm dues, advance a wizard step, enter a score) under VoiceOver, Voice Control, Larger Text at 200%, Increase Contrast with Bold Text and Reduce Transparency, Reduced Motion, and grayscale; the run records which accessibility labels the app may declare.Replaces in codeaccessibility-props-visual.ts, accessibility-state.ts, tab-accessibility.ts and selection-accessibility.ts translate state into aria-* today. announceForAccessibility is called at a few sites (the create-club steps, the club tab change, RevealStack) and header-actions-menu.tsx uses the deprecated setAccessibilityFocus; accessibilityLanguage, the Large Content Viewer props and sendAccessibilityEvent focus management do not ship yet.

Platform

The system owns the edges. The app paints under the bars, adds the real inset to its own bottom chrome, and gives way to the keyboard.

Safe areas, iOS
경기
예정매치 찾기테니스장
9월 19일 (토)내일
올림픽공원 테니스장
09:00~11:00
6/8
대치유수지체육공원
19:00~21:00
4/4
양재 시민의숲 코트
14:00~16:00
2/4
9월 20일 (일)모레
대치유수지체육공원
19:00~21:00
4/4
양재 시민의숲 코트
14:00~16:00
2/4
9:41
홈클럽경기기록프로필
매치
참석했어요
Tab screen FAB above the tab bar, toast above the FAB
참가비 10,000원
참석하기
Pinned CTA the band holds the inset and paints it
Keyboard
1/5
클럽 이름을 알려주세요
클럽 이름
강남 라켓클럽
9:41
다음
ㅂㅈㄷㄱㅅㅛㅕㅑㅐㅔ
ㅁㄴㅇㄹㅎㅗㅓㅏㅣ
⇧ㅋㅌㅊㅍㅠㅜㅡ⌫
123스페이스완료
Text keyboard the band rides it and drops the home inset
일정 만들기
9:41
모집 인원
8
명
저장
123
456
789
0⌫
Number pad the sheet lifts with the keyboard
올림
취소
올림픽공원 테니스장
09:00~11:00
6/8
9:41
ㅂㅈㄷㄱㅅㅛㅕㅑㅐㅔ
ㅁㄴㅇㄹㅎㅗㅓㅏㅣ
⇧ㅋㅌㅊㅍㅠㅜㅡ⌫
123스페이스완료
Searching the tab bar and FAB hide while the keyboard is up
Edge-to-edge, Android
9:41
경기
예정매치 찾기테니스장
9월 19일 (토)내일
올림픽공원 테니스장
09:00~11:00
6/8
대치유수지체육공원
19:00~21:00
4/4
양재 시민의숲 코트
14:00~16:00
2/4
홈클럽경기기록프로필
Gesture bar transparent: the app's canvas shows through
9:41
경기
예정매치 찾기테니스장
9월 19일 (토)내일
올림픽공원 테니스장
09:00~11:00
6/8
대치유수지체육공원
19:00~21:00
4/4
양재 시민의숲 코트
14:00~16:00
2/4
홈클럽경기기록프로필
3-button the tab bar adds the taller inset above the bar
9:41
경기
예정매치 찾기테니스장
9월 19일 (토)내일
올림픽공원 테니스장
09:00~11:00
6/8
대치유수지체육공원
19:00~21:00
4/4
양재 시민의숲 코트
14:00~16:00
2/4
홈클럽경기기록프로필
Theme seam dark app, light system bar; verify on a device
Swipe back, iOS
강남 라켓클럽
올림픽공원 테니스장
09:00~11:00
6/8
대치유수지체육공원
19:00~21:00
4/4
양재 시민의숲 코트
14:00~16:00
2/4
9:41
An ordinary screen the swipe goes back from anywhere
강남 라켓클럽
다음 일정
전체 보기
올림픽공원 테니스장
내일 09:00~11:00
양재 시민의숲 코트
월요일 20:00~22:00
대치유수지체육공원
수요일 19:00~21:00
9:41
A shelf the shelf owns the pan; only the edge goes back
4/6
언제 치나요?
09:00~13:00
9:41
A slider a wizard step with a slider sets it too
Platform
Frame and insetsThe reference frame is 390 × 844 (iPhone 12 to 14 class). The status area is 44 on it (the real top inset is read at runtime: an iPhone 12 to 14 reports 47, a 15 reports 59) and the header's 56 sits below it. The bottom inset is 34 on an iPhone (home indicator); Android gesture and 3-button insets differ and are read at runtime (24 and 48 are drawn here). Orientation: portrait on phones until the first iOS build decides; the WCAG 1.3.4 departure is recorded; from 600 pt Android ignores the lock, so every layout reflows.Paint-once lawThe device inset is painted exactly once, by whatever reaches the physical edge: the shell's safe area, the tab bar, or the band. A band's base padding overlaps the inset instead of adding to it: its bottom padding is max(16, inset).Pinned stackHeader: status inset + 56 (64 with a subtitle). Tab bar: 64 + bottom inset (98 at 34), the canvas fill continuing under the system bar; it publishes its height to the chrome store. CTA band: 16 + helper (22 per line + 8, when present) + 52 + max(16, inset), on screens without a tab bar: 102 at 34, 84 with the keyboard up. FAB: right edge on the rail; bottom = tab-bar top + 16 on tab screens (114), inset + 16 elsewhere (50). Toast: centred; bottom = max(top of the bottom chrome, top of the keyboard) + 16 (114 over a tab bar, 118 over a band, 186 over the FAB: FAB top + 16). Sheet: a footer takes the band’s geometry (16 above, max(16, inset) below); without one the body ends max(16, inset) above the bottom. Elevation: none; separation is a scrim, an edge hairline or an ink surface.Android edge-to-edgeBottom-pinned chrome paints its own canvas behind the system navigation bar and adds the runtime inset. The status bar is styled from the in-app theme (dark icons on the canvas, light on the dark canvas). A 3-button bar may show a contrast seam when the app and system themes differ; verify on an emulator. Ships as OTA, native fix later.ModalsModalPanel sets statusBarTranslucent and navigationBarTranslucent; the scrim covers the navigation-bar band and the inset is not counted twice.BackPredictive back stays off (predictiveBackGestureEnabled: false). Back closes the topmost of menu, sheet or dialog, search takeover, then the screen; with the keyboard open the first back hides it; a wizard shows its guard. The header back and the hardware back are one action.iOS swipe-backA screen that hosts a horizontal-pan control (band sliders, hue slider, chart scrub, a Shelf) and every wizard step with a slider sets fullScreenGestureEnabled: false; the edge swipe stays. Every other screen keeps the full-screen swipe.Sheets on iOSSwipe-down on the title row dismisses (toast thresholds: 40 pt or 500 pt/s); a sheet with unsaved input asks a Dialog first. Android is unchanged.KeyboardAndroid keeps softwareKeyboardLayoutMode "pan"; per screen, setInputMode resize with KeyboardProvider (preload={false}); never a global pan-to-resize flip. The focused input keeps 16 clear above the keyboard. Text keyboard 291 and number pad 250 include the 34 home inset.Keyboard, by surfaceWizard or form: KeyboardAwareScrollView, the CTA band rides the keyboard in KeyboardStickyView. Sheet with inputs: lifts with the keyboard, the CTA stays visible. Search takeover: field focused on open, dragging the list dismisses the keyboard (keyboardShouldPersistTaps="handled"). Toast: above max(bottom chrome, keyboard) + 16. FAB and tab bar hide while the keyboard is open (100 ms). Composer: pinned above the keyboard.HapticsOutcomes only: success (RSVP confirmed, payment confirmed, saved), error (failed), warning (a result that landed on the waitlist), impact light (an optimistic acknowledgement: RSVP tap, reaction, send), selection (choosing a value). Never for a press, tab switch, scroll, sheet open, navigation or toast. Android uses performAndroidHapticsAsync. A haptic is never the only signal.iOS 26 and native tabsThe JS header, tab bar, sheets and buttons are not Liquid Glass and imitate none. System-owned surfaces (alert, native date picker, share sheet) adopt it. Native tabs are not adopted: they cannot report the bar height the FAB, toast and CTA stack needs.Large screensAt 600 dp and up Android ignores the orientation lock; the phone layout is verified on a 600 dp emulator at 200% text before the Play release.Replaces in codesafe-area.ts and bottom-inset-reservation.tsx read the insets today; ModalPanel and WizardShell use KeyboardAvoidingView, DetailShell automaticallyAdjustKeyboardInsets, and app.json sets softwareKeyboardLayoutMode "pan". use-leave-guard.ts locks the swipe-back with gestureEnabled: false for guarded screens; no screen sets fullScreenGestureEnabled yet.

Loading and toast timing

Nothing is drawn for a wait a person cannot feel, a skeleton that appears stays long enough to read, and a toast stays long enough to act on.

Loading
nothing is drawn: no skeleton, no spinner
0 ms
the request starts
250 ms
the data is in
올림픽공원 테니스장
09:00~11:00
6/8
대치유수지체육공원
19:00~21:00
4/4
양재 시민의숲 코트
14:00~16:00
2/4
Under 300 ms the previous content stays
올림픽공원 테니스장
09:00~11:00
7/8
대치유수지체육공원
19:00~21:00
4/4
양재 시민의숲 코트
14:00~16:00
2/4
250 ms the new content replaces it in one frame
nothing is drawnthe data came at 400 ms; the skeleton stays to 800 ms, then fades
0 ms
the request starts
300 ms
a skeleton appears
950 ms
the content is in
Before 300 ms still nothing
300 ms to 10 s one skeleton for the whole screen
올림픽공원 테니스장
09:00~11:00
6/8
대치유수지체육공원
19:00~21:00
4/4
양재 시민의숲 코트
14:00~16:00
2/4
950 ms a 150 ms cross-fade to the content
it pulses for 5 s, then rests on a static framewhen there is an estimate; or
300 ms
a skeleton appears
over 10 s
a determinate indicator
over 10 s
or the slow-load Notice
300 ms the skeleton pulses
불러오는 중이에요
약 10초 남았어요
With an estimate a bar and the time left
평소보다 오래 걸려요연결을 확인하고 다시 시도해 주세요
다시 시도
Without one the Notice, with a way to retry
Toast
저장했어요
Result saved, joined, done
012345678 s
4 s
참석했어요
되돌리기
Result with an action the person needs time to undo
012345678 s
8 s
참석하지 못했어요. 다시 시도해 주세요
참석하기
Error never a toast
012345678 s
no toast
the failure goes next to the button, in a Notice that stays, or in a Dialog
Skeleton and loading timing
Under 300 msNothing: no skeleton, no spinner; the previous content or the empty frame stays.300 ms to 10 sOne skeleton per screen at the row pitches (media 80, person 68, cell 48, block 144, strip 80, chart 160), gray100 pulsing .6 to .9. Once shown it stays at least 500 ms, then cross-fades to content in 150 ms.Over 10 sA determinate indicator with an estimate, or the slow-load Notice with 다시 시도.Over 5 s of shimmerThe shimmer rests on its static frame (WCAG 2.2.2).SpinnerA spinner appears inside a button only. Pull to refresh uses the native control, and a failed refresh shows a Notice.
Toast timing
Result (saved, joined, done)4 s.Result with 되돌리기8 s.ErrorNever a toast: a failure goes next to the button (the CTA danger helper) or in a Notice that stays, or a Dialog.RulesOn Android the duration is the larger of the table and AccessibilityInfo.getRecommendedTimeoutMillis. The timer pauses while the toast is touched and while a screen reader's focus is on it. One toast at a time. A toast never holds the only copy of something the person needs later.DismissSwipe down (past 40 pt or 500 pt/s) or a tap; a toast with an action also carries a ×. No glyph on a toast.Replaces in codeshimmer.ts and skeleton.tsx pulse .6 to .9 (an 800 ms half-cycle) and hold .75 under reduced motion today; toast.store.ts defaults to 2800 ms and pauses on touch, toast-host.tsx owns the swipe. No delay before a skeleton, no minimum hold and no 4 s or 8 s duration ship yet.
DOM check (scripts/verify-flat-v2-boards.mjs): 132 frames, rail 0, type 0, targets 0, shadow 0, dot 0, ball 0. The diagrams, the in-between frames of the motions and the notes are the board's own drawings: the check covers the kit frames around them.
Spec (grounding, anatomy)

Behaviours every component shares, composed from the shared flat v2 kit.

route · flat-v2-kit.jsx grounded in 31 source file(s)
packages/ui/src/pressable-visual.tspackages/ui/src/pressable.tsxpackages/ui/src/button.tsxpackages/ui/src/button-visual.tspackages/ui/src/motion.tspackages/ui/src/text.tsxpackages/ui/src/modal-panel.tsxpackages/ui/src/modal-panel-visual.tspackages/ui/src/skeleton.tsxpackages/ui/src/shimmer.tspackages/ui/src/pulse-dot.tsxpackages/ui/src/expand-toggle.tsxpackages/ui/src/reveal-stack.tsxpackages/ui/src/collapsing-header-layer.tsxpackages/ui/src/progress-bar.tsxpackages/app/src/presentation/components/toast-host.tsxpackages/app/src/presentation/stores/toast.store.tspackages/ui/src/accessibility-props-visual.tspackages/ui/src/accessibility-state.tspackages/ui/src/tab-accessibility.tspackages/ui/src/selection-accessibility.tspackages/ui/src/keyboard-navigation.tspackages/ui/src/horizontal-pan-gesture.tspackages/ui/src/stepper.tsxpackages/ui/src/safe-area.tspackages/ui/src/bottom-inset-reservation.tsxpackages/ui/src/wizard-shell.tsxpackages/ui/src/detail-shell.tsxpackages/ui/src/main-tab-shell.tsxpackages/app/src/presentation/hooks/use-leave-guard.tsapps/mobile/app.json