← Cockpit Wireframes Pages by level Navigation map
Library — chrome and navigation▾
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 — chrome and navigation

The bars and entries around every screen: the header, tabs, tab bar, FAB, CTA band, media band, wizard chrome and search layer, each in its states.

Tabs

Switches the panes or filters of one screen (예정, 매치 찾기, 테니스장). It is not app-level navigation and not a form choice.

예정매치 찾기테니스장
9월 19일 (토)내일
올림픽공원 테니스장
19:00~21:00
6/8
Underline tabs; the first label sits on the rail.
Variants
예정매치 찾기12테니스장3
With a count: the Tag is 4 after the label, centred on its line.
참석6대기2
A count that needs action turns danger.
홈3채팅순위멤버회비
Active with a count: the underline spans the label and its count.
States
예정매치 찾기테니스장
Pressed: dim .85, no settle.
예정매치 찾기테니스장
Focus: an ink ring inside the tab.
예정매치 찾기테니스장지난 경기 기록클럽 일정
Long labels scroll; they never shrink or wrap.
올림픽공원 테니스장
19:00~21:00
6/8
대치유수지체육공원
20:00~22:00
4/4
예정매치 찾기테니스장
The hairline shows only while content scrolls under.
Dark
예정매치 찾기테니스장
Dark: the underline stays ink.
200% text
예정매치 찾기12테니스장3
200%: labels cap at 1.3; the count Tag is 28 (20 at 100%).
English
UpcomingPickupsVenues3
English labels: one word, at most 8 characters.
Tabs
AnatomyRow min-height 48. Labels are content-width, the first label text on the rail, 24 between labels (each tab carries 12 of invisible hit padding per side). Label 15/22: active 600 ink with a 2 ink underline, inactive 500 secondary. A count Tag follows the label through a line slot of the label’s own line (22 at 100%, 29 from 130%, the chrome cap), so it is centred on the label’s first line at every size; label and count are one row (4 apart) and the underline is that row’s width, the label and its count together. Scrolls horizontally when the labels pass the width. Spacing below: 24 to a title or day header, 16 to a row.VariantsPane tabs (tablist and tab, one panel each); filter tabs (group and buttons with aria-pressed, never tab without panels); with a count (a count Tag in the label’s line slot, 4 after the label, neutral, danger when it needs action, 99+ cap; on the active tab the underline spans the label and the count). The count Tag grows with its numeral: 20 tall at 100%, 28 from 130% (chrome cap 1.3).StatesRest, pressed (dim .85, no settle), hover (.85), focus (ink ring), selected (underline and 600), 200% text (chrome cap 1.3: the label is 19.5/29 and the count Tag 28; the row grows to fit and scrolls), long labels (scroll, never wrap).ContentKorean: a short noun phrase. English: one word, at most 8 characters. A count caps at 99+.AccessibilityName is the visible label, then the count in speech. Web: roving tabindex, arrows, Home and End, automatic activation. The pane swap is instant.MotionThe underline slides on navSlide (about 311 ms). Reduce Motion: the underline cross-fades in 150 ms.Replaces in codePillNav in both variants on list surfaces and SegmentedTabs (pill-nav.tsx, pill-nav-underline.tsx, pill-nav-shared.ts, segmented-tabs.tsx).

Tab bar

The five app destinations, always at hand on the tab roots. It is neutral and never carries brand colour.

올림픽공원 테니스장
19:00~21:00
6/8
대치유수지체육공원
20:00~22:00
4/4
양재 시민의숲 코트
07:00~09:00
3/6
홈클럽경기기록프로필
Bar 64 + inset, icon 12 down; the current tab is filled.
Variants
올림픽공원 테니스장
19:00~21:00
6/8
대치유수지체육공원
20:00~22:00
4/4
양재 시민의숲 코트
07:00~09:00
3/6
홈클럽경기기록프로필
Badges on two tabs at most; a count only to act on.
States
올림픽공원 테니스장
19:00~21:00
6/8
대치유수지체육공원
20:00~22:00
4/4
양재 시민의숲 코트
07:00~09:00
3/6
홈클럽경기기록프로필
Pressed: the item settles to .92 and dims to .78.
올림픽공원 테니스장
19:00~21:00
6/8
대치유수지체육공원
20:00~22:00
4/4
양재 시민의숲 코트
07:00~09:00
3/6
홈클럽경기기록프로필
Focus: an ink ring around the item.
올림픽공원 테니스장
19:00~21:00
6/8
ㅂㅈㄷㄱㅅㅛㅕㅑㅐㅔ
ㅁㄴㅇㄹㅎㅗㅓㅏㅣ
⇧ㅋㅌㅊㅍㅠㅜㅡ⌫
123스페이스완료
Keyboard up: the bar and the FAB hide in 100 ms.
Dark
올림픽공원 테니스장
19:00~21:00
6/8
대치유수지체육공원
20:00~22:00
4/4
양재 시민의숲 코트
07:00~09:00
3/6
홈클럽경기기록프로필
Dark: the canvas continues under the system bar.
200% text
올림픽공원 테니스장
19:00~21:00
6/8
대치유수지체육공원
20:00~22:00
4/4
양재 시민의숲 코트
07:00~09:00
3/6
홈클럽경기기록프로필
200%: the bar holds 64; the label caps at 1.3.
English
Olympic Park
19:00–21:00
6/8
Daechi Park
20:00–22:00
4/4
Yangjae Forest
07:00–09:00
3/6
HomeClubsGamesRecordsProfile
English labels: one word, at most 8 characters.
Tab bar
Anatomy64 plus the bottom inset (98 at 34), canvas fill continuing under the system bar; five equal items. Item: 12 above a 24 icon (y 12 to 36, stroke 2), 2, the label 12/16 (y 38 to 54), 10 below. Active: filled glyph, ink, 600. Inactive: outline glyph, secondary, 500. Badge on an icon: a 6 danger dot with a 1.5 canvas ring, or a count Tag 16 tall, at the icon’s top-right corner (the ring’s corner 3.5 above and beyond the icon box), so the ring clears the bar’s top edge by 8.5 (at least 8). Edge hairline while content scrolls under the bar; none at rest; no shadow.Badge ruleA badge on at most two tabs at once. A count only where the number is actionable (the 클럽 tab’s "읽지 않은 게시글 2개"); a dot where something is only pending (홈, "진행 중"). Never both on one tab.StatesRest, pressed (.92 / .78 on the item), selected, focus, hidden while the keyboard is open. 200% text: the bar holds 64 plus the inset; the iOS label does not scale (Large Content Viewer), Android caps it at 1.3.ContentLabels are one word: 1 to 3 Korean syllables, at most 8 English characters.AccessibilityiOS button, Android tab, web navigation labelled 주 메뉴. The name is the label plus the badge tail ("홈, 진행 중인 일정 있어요"; "클럽, 읽지 않은 게시글 2개"). aria-selected; no "n of 5".MotionNone between tabs (screens detach). Press feedback only; hairline 100 ms.Replaces in codeThe inline bar in apps/mobile/app/(tabs)/_layout.tsx and CornerCountBadge (corner-count-badge.tsx).

FAB

The screen's single create action on a list root. It is the screen's one coloured action, so a page with a CTA band has no FAB.

올림픽공원 테니스장
19:00~21:00
6/8
대치유수지체육공원
20:00~22:00
4/4
일정
홈클럽경기기록프로필
Extended pill on the rail, 16 above the tab bar.
Variants
올림픽공원 테니스장
19:00~21:00
6/8
대치유수지체육공원
20:00~22:00
4/4
홈클럽경기기록프로필
Collapsed after 120 pt of downward scroll.
올림픽공원 테니스장
19:00~21:00
6/8
일정
Without a tab bar: 16 above the home inset.
States
올림픽공원 테니스장
19:00~21:00
6/8
대치유수지체육공원
20:00~22:00
4/4
일정
홈클럽경기기록프로필
Pressed: .96 and dim .90.
올림픽공원 테니스장
19:00~21:00
6/8
대치유수지체육공원
20:00~22:00
4/4
일정
홈클럽경기기록프로필
Focus: the ink ring, also on a ball fill.
Dark
올림픽공원 테니스장
19:00~21:00
6/8
대치유수지체육공원
20:00~22:00
4/4
일정
홈클럽경기기록프로필
Dark: ball and ink label do not change.
200% text
일정
200%: label cap 1.3, the pill widens, keeps 56.
English
Olympic Park
19:00–21:00
6/8
Daechi Park
20:00–22:00
4/4
Pickup
HomeClubsGamesRecordsProfile
English: the label is the noun created.
FAB
AnatomyExtended pill, min-height 56, padding 0 20, radius full; icon 20, 8, label 15/22 600; ball fill, ink icon and label. Right edge on the rail; bottom = tab-bar top + 16 on tab screens (114 from the frame’s bottom), inset + 16 elsewhere (50); the list tail padding clears 56 + 16. Collapsed: a 56 circle with the icon only.StatesPressed .96 / .90, hover .85, focus ring, disabled .5 (rare). Collapse after 120 pt of cumulative downward scroll; expand after 24 pt upward or at offset 24 and below; ignore offsets under 0 and programmatic scrolls. Hidden behind the keyboard in 100 ms. 200% text: the label is chrome class (cap 1.3).ContentThe label is the noun created (일정, 매치).AccessibilityButton. The name is the visible text first ("일정 만들기"), also when collapsed. Reached after the list in focus order.MotionLabel out 100 ms, width 200 ms standard, label in 150 ms. Reduce Motion: the width changes at once and the label fades 150 ms.Replaces in codeNone today; only the physics entry variant="fab" exists in pressable-visual.ts.

CTA band

The one pinned primary action at the foot of a detail page, form or sheet. One band, one primary, never a second action.

올림픽공원 테니스장
19:00~21:00
6/8
참석하기
Base: 102 with the home inset; content ends 40 above.
Variants
참가비 10,000원
참석하기
Helper: the total the press commits, money (132).
합계 30,000원
3명 입금 확인
Helper: a live total of what is ticked, changing as the rows tick (132).
월 회비 30,000원
가입하기
Helper: the price of joining, on a club’s join band (132).
결제에 실패했어요. 카드를 확인해 주세요.
다시 시도
Danger helper: the action failed; the button stays on.
다음
Soft-disabled: grey, no helper; a press shows the fields’ errors (102).
초기화결과 12개 보기
Pair: tonal flex 1, primary flex 2, gap 12.
알림 3개 삭제
Danger tone: a Dialog first, unless a Toast undoes it (알림 n개 삭제 commits and offers 되돌리기).
카카오 로그인
이메일로 로그인
이용약관개인정보 처리방침
Entry: sign-in only, no hairline (188).
Entry: the sign-up route is the owner’s decision; this is the recommendation
카카오 로그인
네이버 로그인
Apple로 로그인
이메일로 로그인
이용약관개인정보 처리방침
iOS: Kakao, Naver, Apple (316).
카카오 로그인
네이버 로그인
Google 로그인
이메일로 로그인
이용약관개인정보 처리방침
Android and web: Kakao, Naver, Google (306).
카카오 로그인
네이버 로그인
Apple로 로그인
이메일로 로그인
이용약관개인정보 처리방침
Busy: only the tapped provider spins; the rest wait inert.
States
참석하기
Pressed: the button settles to .97 and dims to .92.
저장하기
Loading: a spinner beside the progressive label.
참석하기
Disabled: a role wall; the page’s own fact says why, so the band has no helper.
올림픽공원 테니스장
19:00~21:00
6/8
대치유수지체육공원
20:00~22:00
4/4
참석하기
Scrolled: the hairline shows under moving content.
올림픽공원 테니스장
19:00~21:00
6/8
다음
123
456
789
0⌫
Keyboard up: the band rides it; the inset is 0.
Dark
참가비 10,000원
참석하기
Dark: the ball keeps its ink label.
200% text
결제에 실패했어요. 카드를 확인해 주세요.
다시 시도
200%: the button grows to 76, the danger helper wraps in full.
English
Fee 10,000 KRW
Join
English: at most two words on the button.
CTA band
AnatomyCanvas fill; padding 16 above, the rail on the sides, and max(16, inset) below (the inset is the padding, Toss’s rule: the button’s bottom sits on the safe-area boundary, 21 above the home indicator); lg Button full width of the content. 102 at inset 34; 16 below with no inset or the keyboard up (84). Helper line (only where 1.16 keeps one) above the button, 15/22 400 secondary, 8 above the button, at most 2 lines (132 with one line, 154 with two). Edge hairline while content scrolls under; none at rest; no shadow. Content above ends 40 clear of it.Helper rulesWarranted only by (the owner, 2026-10-20; 1.16): the total the press commits (money, as 전자상거래법 requires: “참가비 10,000원”); a live total or count of what the person picked or marked on the screen (“합계 90,000원”, “미응답 2명”, “3명 중 1명”); the price of joining on a club’s join band; or the action’s failure (the danger helper). Never an instruction, a reason, terms, a consequence, who to ask or a figure the page already shows: a disabled button’s reason is the fields’ errors on the press, so a soft-disabled button has no helper. Otherwise there is no helper and the band is 102. Decision facts only: numbers and dates, one sentence or one MetaSegments line; the total, never a part; no promotion, no echo of the button, no consent folded into a label, no link to long legal text (a Cell “취소 규정” opens it), no second action. One line by default, two at most; more is a Cell.VariantsBase, helper (the total a press commits, a live total or count of a selection, the price of joining), danger helper (a 16 alert glyph in the line slot and 15/22 600 $errorText, one line, enabled for the retry), soft-disabled and enabled-and-validating primary (no helper: a press shows the fields’ errors), pair (tonal flex 1 and primary flex 2, gap 12, in a Sheet footer; a wizard footer and an edit sheet footer are always a single CTA), entry (sign-in only: provider buttons 52 tall, r12, 12 apart, then 12, a tonal md Button (이메일로 로그인; a verify band’s 다른 방법으로 인증 is the same Button), 12 and the legal links row (이용약관 · 개인정보 처리방침, the one place a link stays text) with 44 targets by hit slop: 188 with one provider, 316 with three; the recommendation is Kakao, Naver and Apple on iOS, Kakao, Naver and Google on Android and the web, and the sign-up route is the owner’s decision), danger tone.StatesRest, pressed (button .97 / .92), soft-disabled, disabled (role wall: the page’s own fact says why, no helper), loading (spinner beside "…중"; in the entry only the tapped provider spins, the other buttons and the tonal Button wait inert at .5), keyboard up (the band rides the keyboard; the home inset is 0), 200% text (the button grows to 76, the helper wraps in full and never ellipsizes; the band grows).ContentButton label: the specific action, a verb that names the result, aimed at 2 to 5 syllables and up to 7 when the verb needs its object (일정 만들기 is 5; 입금 완료 알리기 is 7). English: at most two words. A helper that is more than two lines is rewritten, never clamped.AccessibilityThe button is the only action. A soft-disabled button is focusable with aria-disabled and its reason spoken. A danger helper is role="alert" when it appears. Loading sets aria-busy and the name follows the visible label.MotionHairline 100 ms fadeFast. A helper change is instant. Press per the button physics.Replaces in codeBottomCtaBand (bottom-cta-band.tsx, bottom-cta-band-visual.ts), the WizardShell footer and its primaryHint, and the fee and gate helpers in session-detail/actions/primary-action.tsx.

Sticky edges

The one line the flat grammar draws on chrome: a hairline on the edge of a bar while content passes under it.

올림픽공원 테니스장
올림픽공원 테니스장
19:00~21:00
6/8
Hidden: at rest the header draws no line.
올림픽공원 테니스장
19:00~21:00
6/8
대치유수지체육공원
20:00~22:00
4/4
올림픽공원 테니스장
Shown: content scrolls under the header.
Variants
올림픽공원 테니스장
19:00~21:00
6/8
대치유수지체육공원
20:00~22:00
4/4
예정매치 찾기테니스장
Scope tabs: the bottom edge of the row.
올림픽공원 테니스장
19:00~21:00
6/8
대치유수지체육공원
20:00~22:00
4/4
양재 시민의숲 코트
07:00~09:00
3/6
홈클럽경기기록프로필
Tab bar: the top edge.
올림픽공원 테니스장
19:00~21:00
6/8
대치유수지체육공원
20:00~22:00
4/4
참석하기
CTA band: the top edge.
올림픽공원 테니스장
19:00~21:00
6/8
대치유수지체육공원
20:00~22:00
4/4
건너뛰기
3/6
Wizard header: under the progress row.
Sticky edges
AnatomyOne full-bleed line, 1 px, in the edge tier (#D4D4D4; dark white at 14%). No shadow. Header and scope tabs: the bottom edge. Tab bar and CTA band: the top edge. Wizard header: the bottom of the progress row. A Day header or a Feed head that is the pinned layer (§1.14, the pinned stack): the bottom edge of the head.VisibilityOn above scroll offset 8, off at 0 (an 8 band of hysteresis; measured from the bottom for a bottom bar). A screen whose content fits draws none. The cell hairline is not a sticky edge.AccessibilityDecorative; hidden from assistive tech.Motion100 ms fadeFast. Reduce Motion: unchanged.Replaces in codeThe permanent borders on AppHeader, BottomCtaBand and the tab bar. One useScrollEdge(offsetY) hook, fed by the same scroll source as use-collapsing-top-bar.ts.

MediaBand

The full-bleed photo or map at the top of a place-like page. It carries only back and at most two icons; everything else is in the body.

강남 라켓클럽
강남구NTRP 3.0–4.0멤버 42명
9:41
Club hub, member: 208 tall at 390, content 24 below.
Variants
강남 라켓클럽
강남구NTRP 3.0–4.0멤버 42명
9:41
Official: share and the 관리 gear with a badge.
강남 라켓클럽
강남구NTRP 3.0–4.0멤버 42명
9:41
Club page, non-member: share only.
올림픽공원 테니스장
송파구코트 6면
9:41
Venue: save and ⋯; place actions sit in the body.
올림픽공원 테니스장
송파구코트 6면
9:41
Session map: a 24 pin at y 154 opens the venue.
올림픽공원 테니스장
송파구코트 6면
9:41
Session map, host: share and the gear.
Photo counter (owner review 43)
강남 라켓클럽
강남구NTRP 3.0–4.0멤버 42명
9:41
First photo: "1 / 5", bottom right on the rail, 12 up.
강남 라켓클럽
강남구NTRP 3.0–4.0멤버 42명
9:41
Last photo: tabular numerals, so the pill never shifts.
앨범 전체 보기사진 12장
강남 라켓클럽
강남구NTRP 3.0–4.0멤버 42명
9:41
Then the album frame: no counter, the Door pill 앨범 전체 보기 ›; it opens the album.
강남 라켓클럽
강남구NTRP 3.0–4.0멤버 42명
9:41
A white sky: the status bar turns dark; one fill holds 4.8:1.
강남 라켓클럽
강남구NTRP 3.0–4.0멤버 42명
9:41
One photo: no counter.
강남 라켓클럽
강남구NTRP 3.0–4.0멤버 42명
9:41
Dark: the pill belongs to the photo, so it is unchanged.
강남 라켓클럽
강남구NTRP 3.0–4.0멤버 42명
9:41
200%: the numerals grow with text; the circles stay.
States
강남 라켓클럽
올림픽공원 테니스장
19:00~21:00
6/8
대치유수지체육공원
20:00~22:00
4/4
9:41
Scrolled: white header, ink glyphs, title, hairline.
올림픽공원 테니스장
송파구코트 6면
9:41
Map pressed: dim .85 and no settle.
강남 라켓클럽
강남구NTRP 3.0–4.0멤버 42명
9:41
Loading: a gray100 band.
강남 라켓클럽
강남구NTRP 3.0–4.0멤버 42명
9:41
Error: the default cover (a court tile for a map).
Dark
강남 라켓클럽
올림픽공원 테니스장
19:00~21:00
6/8
대치유수지체육공원
20:00~22:00
4/4
9:41
Dark: the scrolled header on the dark canvas.
200% text
강남 라켓클럽
강남구NTRP 3.0–4.0멤버 42명
9:41
200%: glyphs and circles do not scale.
MediaBand
AnatomyPhoto and map share one geometry: full-bleed, cover fit, 2.4:1 on the 4 grid, the Block’s ratio, for one proportion across the bold surfaces. The rule: image height = round(width ÷ 2.4 ÷ 4) × 4, plus the status bar (164 + 44 = 208 at 390; a wider phone gets a taller band, never a literal). A top scrim of ink at 35% fading to 0 over the first 100. The header is transparent over the band: back at the left, at most two registry icons at the right (share first, then save or ⋯), each a 40 circle in the one on-photo fill, ink 60% (80% under Reduce Transparency), with a white 24 glyph in a 48 target, 12 between neighbouring circles (pitch 52: Apple asks about 12 of padding around bezeled controls), the outer circle edges on the rail. Nothing else on the photo: no text, pill, place action or Tag except the photo counter.MapA 24 ink pin centred in the visible area under the header (y 154 of 208). No Tag or chip. A tap opens the venue page; directions live there.PhotoClub: cover always. The band holds up to five photos (the club’s featured set, `club_media` kind featured, else the five latest approved; NN/g: keep a set to five or fewer, never auto-advance), the first the cover, then the album frame (the last photo at ink 60% and the Door pill 앨범 전체 보기 › with "사진 12장"). No photo: the default cover at the same height. Place actions are labelled and in the body: a venue shows 길찾기, 공유, 전화 under its title; a session 단톡방, 문의하기 and 신고 are 정보 Cells.Photo counter (review 43)Look: "1 / 5", 13/18·600 white tabular numerals on the same on-photo fill as the circles (ink 60%: one family for everything on a photo), 24 tall, r12, 8 each side, its outer edge on the rail, 12 above the band’s bottom. It belongs to the photo, not the theme: the same in dark. 60% is the least ink that keeps white 13 text at 4.5:1 over a white sky (4.8:1, WCAG 1.4.3), and it gives the circles’ glyphs more than their 3:1 (1.4.11). The numerals grow with text (the circles do not). Behaviour: shown only with two photos or more; never on the album frame, the default cover or while loading; it changes the moment a swipe settles, without motion; it is not a control and is hidden from assistive tech, because the band is one adjustable element named "클럽 사진, 5장 중 1번째" (a swipe up or down moves it). A tap on a photo opens the album viewer at that photo.Status bar (owner decision D3)Light over the photo, except over a bright cover: when the mean luminance of the cover’s top 100 is above 0.6 the status bar is dark. Measured once at upload with the ThumbHash and stored on the photo, so the band never measures at runtime. The circles and the counter keep their one fill either way.ScrollAfter the last 100 of the photo has passed under the header (offset 108, the band’s height less 100) the header turns white in 150 ms, the circles lose their fill, the glyphs turn ink, the title fades in and the edge hairline appears. Status bar light over the photo and dark after.VariantsClub hub, member: share, ⋯ (클럽 알림, 클럽 나가기). Club hub, official: share, the 관리 gear with a badge. Club page, non-member: share. Venue: save, ⋯ (지도 앱에서 열기, 정보 제보, 신고). Session with a map band: member share, save; host or official share, the 관리 gear with a badge.StatesRest, map pressed (dim .85, no settle), focus (ring inset 2), loading (gray100), error (the court tile for a map; the default cover for a club), scrolled (white header), 200% text (glyphs and circles do not scale). First content starts 24 below the band; the band carries no copy.AccessibilityEach circle is a registry button named by its registry name. The map band is one button named for the venue and its destination. The counter is spoken as a position ("사진 12장 중 1장"). The photo is decorative.MotionHeader turn and title 150 ms fade; image fade 150 ms. Reduce Motion: unchanged.Replaces in codeMediaHero (media-hero.tsx, with its mediaHint pill), CollapsingTopBar over a photo with PhotoScrim, ClubAlbumBanner (pager dots and the 앨범 N pill), club-place-header.tsx, and the session map band in SessionHeroSection. Build note: packages/ui/src/media-hero-visual.ts HERO_BAND_ASPECT becomes 2.4, with its test.

Live row

The one line under the header that says a session is being played now, on the scorecard and the match board.

스코어카드
진행 중
9월 19일 (토)올림픽공원 테니스장
The Live tag, then the date and venue at the rail.
States
스코어카드
진행 중
9월 19일 (토)올림픽공원 테니스장
Pulse: 5 s per mount, then solid.
Dark
스코어카드
진행 중
9월 19일 (토)올림픽공원 테니스장
Dark: the tag keeps its danger fill.
200% text
스코어카드
진행 중
9월 19일 (토)올림픽공원 테니스장
200%: the tag stays one line; the meta wraps.
English
Scorecard
Live
Sat, Sep 19Olympic Park
English: the short date.
Live row
AnatomyDirectly under the header, 40 tall at 100%: the 24 Live tag is attached through the line slot of the meta’s 20 first line, so it overhangs that line by 2 and the row’s 10 of padding leaves it 8 clear above and below. The Live tag, 8, then the date and venue as MetaSegments 13/20 500 secondary at the rail. The meta is one line at 100% (the venue crops, the date never) and wraps from 1.5x text, a break staying with the segment that follows it; the tag stays beside line 1.StatesRest, pulse (the tag 6 white dot pulses 1600 ms for 5 s per mount, then rests solid), 200% text (the tag stays one line; the meta wraps).ContentThe date and venue only. The venue may crop; the date never.AccessibilityOne text group read as "진행 중, 9월 19일 토요일, 올림픽공원 테니스장". The dot is hidden. Not pressable.MotionThe pulse only (1600 ms loop, 5 s per mount). Reduce Motion: solid dot.Replaces in codeSessionLiveAnchor (session-live-anchor.tsx) and the page-level use of SessionLiveBand (session-strip-ui.tsx).

WizardShell header and progress row

The chrome above a wizard step: movement and departure live in the header, orientation in the progress row, and the footer holds one CTA.

건너뛰기
3/6
클럽을 소개해 주세요
한 줄 소개(선택)
예: 매주 토요일 아침에 쳐요
9:41
다음
Step 3 of 6: 12 above and below the buttons; one CTA.
Variants
1/6
클럽 이름을 정해 주세요
First step: × asks the exit Dialog; no skip.
검토
4/6
모임 지역을 골라 주세요
Edit detour: the back names its destination.
건너뛰기
5/9
운영 규칙을 정해 주세요
More than 7 steps: one continuous bar.
6/6
이대로 만들까요?
Last step: every segment is ink; no skip.
2/6
클럽 이름을 정해 주세요
클럽 이름
예: 강남 라켓클럽
다음
Required step: the asterisk and a soft-disabled CTA.
States
건너뛰기
3/6
Pressed: the square settles to .97 and dims.
건너뛰기
3/6
Focus: an ink ring around the square.
건너뛰기
3/6
Skip is disabled while the primary is in flight.
한 줄 소개(선택)
예: 매주 토요일 아침에 쳐요
건너뛰기
3/6
Scrolled: the hairline under the progress row.
Dark
건너뛰기
3/6
클럽을 소개해 주세요
Dark: ink segments on the dark canvas.
200% text
건너뛰기
3/6
클럽을 소개해 주세요
200%: buttons cap at 1.3; the count stays on one line.
English
Skip
3/6
Tell us about the club
English: sentence case, one sentence.
WizardShell header and progress row
HeaderThe row is 56 below the status area, with 8 of top padding: the buttons sit 12 below the status area and 12 above the progress bar. Left, one button: a 40 by 40 r12 gray100 square (y 12 to 52 of the row) with a 20 ink chevron in a 48 target (× on the first step, which asks the exit Dialog). Right: 건너뛰기 as an sm tonal Button on a skippable step. Nothing in the centre.Progress row8 below the header: segments 4 tall, r2, gap 4; ink for done and current, the cell tier for the rest. The count "3/6" sits at the right rail, 13/18 600 secondary, tabular, 12 after the last segment. The bar is 12 clear of the header buttons (the square ends at y 52, the bar starts at y 64). At most 7 segments; beyond 7 one continuous bar of the same height, filled to the current step.Title and footerThe step title (hero 24/32 700) starts 24 below the bar; chrome above the title is 92. The footer is the single CTA band, with a helper only where 1.16 keeps one: 16 above the button and max(16, inset) below it (102 at inset 34, 21 between the button and the home indicator). Hardware back mirrors the header back (useWizardBackHandler). The edge hairline, when content scrolls under, is drawn under the progress row (8 below the bar).VariantsBack, labelled: during an edit-from-review detour the square becomes a content-hugging pill of the same fill and radius with a 20 chevron and the destination noun (15/22 600). Close: × on the first step and on a gate or terminal step.StatesRest, pressed (.97 / .92), focus (ink ring), skip disabled while the primary is in flight, 200% text (header buttons are chrome class, cap 1.3; the bar and the count stay on one line).ContentStep titles are one sentence in 해요체. The skip label is 건너뛰기.AccessibilityBack and skip are real buttons named by their label (뒤로, 건너뛰기; close 닫기). The progress row speaks "6단계 중 3단계" and each step change announces it with the title. Completed segments are buttons named "n단계로 이동" with a 44 hit zone from the header bottom edge (y 56 to 100). Focus moves to the new step title.MotionSegments recolour in 150 ms fade; the continuous bar grows in 150 ms; no motivational animation. Reduce Motion: the bar changes at once.Replaces in codeWizardShell (packages/ui/src/wizard-shell.tsx): the inline 4 px segments between back and skip, its "n / N" count, and the footer primaryHint placement.

Search takeover

The full-screen search layer for an open-ended search inside a flow. It replaces an in-scroll search field.

테니스장 이름이나 동네
취소
올림픽공원 테니스장
송파구
대치유수지체육공원
강남구
9:41
ㅂㅈㄷㄱㅅㅛㅕㅑㅐㅔ
ㅁㄴㅇㄹㅎㅗㅓㅏㅣ
⇧ㅋㅌㅊㅍㅠㅜㅡ⌫
123스페이스완료
Rest: field focused, keyboard up, recents below.
States
올림
취소
올림픽공원 테니스장
송파구코트 6면
올림픽공원 연습장
송파구코트 2면
올림픽 수영장 코트
송파구코트 3면
9:41
ㅂㅈㄷㄱㅅㅛㅕㅑㅐㅔ
ㅁㄴㅇㄹㅎㅗㅓㅏㅣ
⇧ㅋㅌㅊㅍㅠㅜㅡ⌫
123스페이스완료
Typing: results as MediaRows.
올림
취소
올림픽공원 테니스장
송파구코트 6면
올림픽공원 연습장
송파구코트 2면
올림픽 수영장 코트
송파구코트 3면
이 테니스장으로
9:41
ㅂㅈㄷㄱㅅㅛㅕㅑㅐㅔ
ㅁㄴㅇㄹㅎㅗㅓㅏㅣ
⇧ㅋㅌㅊㅍㅠㅜㅡ⌫
123스페이스완료
Staged: a tap stages a row; only the CTA commits.
올림픽 풀장
취소
찾는 결과가 없어요다른 이름이나 동네로 검색해 보세요
찾는 곳이 없나요?
테니스장 정보를 알려 주세요
9:41
ㅂㅈㄷㄱㅅㅛㅕㅑㅐㅔ
ㅁㄴㅇㄹㅎㅗㅓㅏㅣ
⇧ㅋㅌㅊㅍㅠㅜㅡ⌫
123스페이스완료
Empty: the filtered-empty state and a contribution row.
올림
취소
9:41
ㅂㅈㄷㄱㅅㅛㅕㅑㅐㅔ
ㅁㄴㅇㄹㅎㅗㅓㅏㅣ
⇧ㅋㅌㅊㅍㅠㅜㅡ⌫
123스페이스완료
Loading: skeleton rows at the media pitch.
올림
취소
올림픽공원 테니스장
송파구
대치유수지체육공원
강남구
9:41
오프라인이에요
다시 시도
ㅂㅈㄷㄱㅅㅛㅕㅑㅐㅔ
ㅁㄴㅇㄹㅎㅗㅓㅏㅣ
⇧ㅋㅌㅊㅍㅠㅜㅡ⌫
123스페이스완료
Offline: the strip is the only message.
Variants
올림
취소
올림픽공원 테니스장
송파구코트 6면
올림픽공원 연습장
송파구코트 2면
올림픽 수영장 코트
송파구코트 3면
9:41
ㅂㅈㄷㄱㅅㅛㅕㅑㅐㅔ
ㅁㄴㅇㄹㅎㅗㅓㅏㅣ
⇧ㅋㅌㅊㅍㅠㅜㅡ⌫
123스페이스완료
Immediate: each tap commits, no CTA.
테니스장 이름이나 동네
The step keeps a search-field-shaped trigger row.
200% text
올림
취소
올림픽공원 테니스장
송파구코트 6면
올림픽공원 연습장
송파구코트 2면
올림픽 수영장 코트
송파구코트 3면
9:41
ㅂㅈㄷㄱㅅㅛㅕㅑㅐㅔ
ㅁㄴㅇㄹㅎㅗㅓㅏㅣ
⇧ㅋㅌㅊㅍㅠㅜㅡ⌫
123스페이스완료
200%: rows grow; the field and 취소 keep their place.
Search takeover
AnatomyA full-screen layer rising 300 ms with the keyboard up and the field focused. Header row min-height 56: the Search field (40 tall, r12, gray100, 20 search glyph, 8, input 16/24, a clear × when filled) in a 44 target filling the row from the rail; 8; 취소 as an sm tonal Button at the right rail. Body: recent searches as Text rows with a trailing × control; results as MediaRows. Empty: the filtered-empty EmptyState plus the contribution row. Footer (staged mode): the CTA band with the one confirm, present once a row is staged.VariantsStaged (default): a row stages, the CTA confirms ("이 테니스장으로" for one, "N곳 선택" for several). Immediate: a host that owns per-pick choreography commits on each row tap and shows no CTA. The step keeps a search-field-shaped trigger row that opens this layer.StatesRest (recents), typing (results), empty, loading (skeleton rows), offline (the strip), 200% text.ContentThe keyboard 검색 key commits free text inside the layer.AccessibilityFocus lands on the input; on close it returns to the trigger row. Dragging the list dismisses the keyboard (keyboardShouldPersistTaps="handled"). System back closes the layer.MotionRise 300 ms sheetIn. Reduce Motion: fade 150 ms.Replaces in codeVenueSearchTakeover (venue-search-takeover.tsx) hosting VenueFinder in pick intent.

Header icon badge

A mark on a header icon that says something there needs attention, or how many.

Dot
3
12
99+
A dot is pending; a count is a number to act on.
홈클럽경기기록프로필
The tab bar: the same marks, 8.5 clear of its top.
Dark
Dot
3
12
99+
Dark: tone and ring follow the theme.
홈클럽경기기록프로필
Dark: the canvas ring follows the bar.
Header icon badge
AnatomyDot: a 6 $error circle with a 1.5 canvas ring. Count Tag: 16 tall, min width 16, radius full, 12/16 700 white on $error, 1.5 canvas ring, 99+ cap. Placed at the glyph’s top-right corner through the core badged host, overhanging the 24 glyph box by 2 (the ring is drawn outside that, so its corner is 3.5 above and beyond the box); it is a corner mark, never inline. A mark wider than one digit (10 to 99+) keeps the one-digit mark’s left edge and grows to the right, so it never covers the glyph. A dot means something is pending (the 관리 gear, a live tab); a count means a number the person acts on (알림, 메시지). Never both. The tab bar uses the same marks: its icon sits 12 below the bar’s top, so the ring clears the bar’s top edge by 8.5 (the rule: the ring’s top is at least 8 below its host’s top edge), and a badge sits on at most two tabs at once.StatesAbsent, present. A count changes instantly.ContentCounts are digits; 99+ above 99.AccessibilityHidden from assistive tech; the icon’s name carries the state ("알림 3개, 관리 필요 2건 포함", "관리, 처리할 일 있어요").MotionNone; a count changes instantly.Replaces in codeCornerCountBadge (corner-count-badge.tsx: an 18 circle, amber and red tones) and the pill-tab BadgeDot (pill-nav-shared.ts).

Coachmark

A one-time pointer at a control a person would not find alone. It points; it never blocks a flow.

강남 라켓클럽
올림픽공원 테니스장
19:00~21:00
6/8
대치유수지체육공원
20:00~22:00
4/4
관리. 클럽 운영 도구가 여기에 모여 있어요.
확인
Single step: the name, one sentence, 확인.
Variants
강남 라켓클럽
올림픽공원 테니스장
19:00~21:00
6/8
대치유수지체육공원
20:00~22:00
4/4
관리. 클럽 운영 도구가 여기에 모여 있어요.
1/3다음
Multi step: 다음 until the last, position at left.
올림픽공원 테니스장
19:00~21:00
6/8
대치유수지체육공원
20:00~22:00
4/4
일정
새 일정은 여기서 만들어요.
확인
홈클럽경기기록프로필
Pointing down at the create FAB.
Dark
강남 라켓클럽
올림픽공원 테니스장
19:00~21:00
6/8
대치유수지체육공원
20:00~22:00
4/4
관리. 클럽 운영 도구가 여기에 모여 있어요.
확인
Dark: the inverse flips to #DFE4DC with ink text.
Coachmark
AnatomyInverse surface (ink #0D100E; in dark it flips to a light surface, #DFE4DC), radius 12, padding 16, an 8 arrow toward the target, width at most 280. Body 16/24 in the on-inverse colour (white; ink in dark), one or two sentences. Footer row min 44 with the action at the right: a sm primary Button (a command is a Button, owner review 6 D1), ball on the ink surface (14.96:1). The spotlight overlay around the target is the tour library.VariantsSingle step (action 확인) and multi step (다음 until the last).StatesShown, dismissed. A coachmark fires once, ever, per key; two never stack; Settings can reset them (둘러보기 다시 보기).ContentBody only, no title, no icon. The text for a header icon is that icon’s registry name plus one sentence.AccessibilityThe text is announced when it appears (polite). The action is a button. System back or Escape dismisses and marks it seen.MotionFade in 150 ms, out 100 ms; no slide. Reduce Motion: unchanged.Replaces in codeCoachmarkTooltip (coachmark-tooltip.tsx: title, body, 확인 pill), tour-ref-injector.tsx and the CoachmarkKey store (coachmark-seen.store.ts).
Checked with the shared DOM verifier (yarn verify:flat-v2-boards): 125 frames, rail 0, type 0, targets 0, shadows 0, middle dots 0, ball actions 0.
Spec (grounding, anatomy)

The chrome and navigation family of the flat v2 library (header, tabs, tab bar, FAB, CTA band, sticky edges, MediaBand, Live row, WizardShell header, search takeover, header icon badge, coachmark), drawn by the shared kit in flat-v2-kit-chrome.jsx.

route · docs/design/flat-v2-component-library-2026-10-09.md §3 grounded in 31 source file(s)
packages/ui/src/header.tsxpackages/ui/src/header-visual.tspackages/ui/src/main-tab-shell.tsxpackages/ui/src/icon-button.tsxpackages/features/clubs/src/club-detail-screen.tsxpackages/features/sessions/src/session-detail/header/use-session-header.tsxpackages/ui/src/header-menu.tsxpackages/ui/src/collapsing-top-bar.tsxpackages/ui/src/use-collapsing-top-bar.tspackages/ui/src/collapsing-header-layer.tsxpackages/ui/src/pill-nav.tsxpackages/ui/src/pill-nav-underline.tsxpackages/ui/src/pill-nav-shared.tspackages/ui/src/segmented-tabs.tsxapps/mobile/app/(tabs)/_layout.tsxpackages/ui/src/corner-count-badge.tsxpackages/ui/src/pressable-visual.tspackages/features/home/src/page/home-body.tsxpackages/ui/src/wizard-shell.tsxpackages/features/sessions/src/session-detail/view/build-band.tspackages/ui/src/media-hero.tsxpackages/ui/src/photo-scrim.tsxpackages/features/clubs/src/club-detail/club-band-media.tsxpackages/features/clubs/src/club-detail/club-place-header.tsxpackages/features/sessions/src/session-detail/page/session-hero.tsxpackages/app/src/presentation/components/sessions/session-live-anchor.tsxpackages/app/src/presentation/components/sessions/session-strip-ui.tsxpackages/app/src/presentation/components/venue-search-takeover.tsxpackages/ui/src/coachmark.tsxpackages/features/home/src/overlays/fab-layer.tsxpackages/app/src/presentation/stores/coachmark-seen.store.ts