Overview
Compare
Inspect
Creating a session is a 7-step WizardShell wizard — 언제 → 어디서 → 어떻게 치나요 → 몇 명이서 → 옵션 → 경기 규칙 → 확인 — while editing (edit-session-screen.tsx) is 9 per-field EditFieldRow rows, each opening a focused EditControlModal, with 종목·로테이션·모집 마감 locked outright, 참가비 (U-06, 2026-09-17) conditionally locked once the first payment hold exists, and 실력 always the 클럽 기준 그대로/범위 설정 fork rather than a bare tier-range field.
route · apps/mobile/app/create-session.tsx → sessions/src/create-session-screen.tsx (wizard) · sessions/[sessionId]/edit.tsx → sessions/src/edit-session-screen.tsx
grounded in 14 source file(s)
packages/features/sessions/src/create-session-screen.tsxpackages/features/sessions/src/create-session-step1.tsxpackages/features/sessions/src/create-session-step2.tsxpackages/features/sessions/src/create-session-step3.tsxpackages/features/sessions/src/create-session-step-players.tsxpackages/features/sessions/src/create-session-step4.tsxpackages/features/sessions/src/create-session-step5.tsxpackages/features/sessions/src/create-session-step6.tsxpackages/features/sessions/src/create-session-draft.tspackages/features/sessions/src/edit-session-screen.tsxpackages/app/src/presentation/components/style-axis-options.tspackages/ui/src/wizard-shell.tsxpackages/app/src/config/i18n/ko/sessions.tspackages/app/src/config/i18n/ko/clubs.ts
Spec
1
Foundations · 디자인 시스템
Spacing · 4px grid
$1 – $4
4 · 8 · 12 · 16 px
$5 – $10
20 · 24 · 32 · 40 · 48 · 64 px
Radius
$2 8 · $3 12 · $4 16 · $7 9999 (pill)
Color · Classic Grass (light)
Surface
$background #F8FBF9 · $card #FFFFFF · $surfaceTertiary #E5FAEA
Brand
$primary #006D37 · $primarySubtle #E5FAEA
Text
$text #0D100E · $textSecondary #4F5851 · $textTertiary #626C64
Border
$border #CAD4CC · $borderSubtle #DFE7E1
Status
$success #14804A · $warning #946200 · $error #B8332E
Badge intents
accent #E0F8F3/#007B6B · warning #FFF4D6/#946200 · error #FDECEC/#B8332E
Court
$courtClay #B84A2E · $courtHard #247BA0 · $courtGreen #006633
Typography · Pretendard
cardTitle · navTitle
16 / 24 · 600 · 17 / 22 · 600
cardBody · cardMeta
14 / 22 · 400 · 13 / 18 · 500
badge · micro
12 / 18 · 600 · 10 / 14 · 600
display Sm/Md/Lg/Xl
20/28 · 28/36 · 36/44 · 48/56 · 700
Primitives · @twomore/ui
Layout
YStack · XStack · Card (default/flat) · Well · Divider
Controls
Pressable · Badge · Icon · PillNav · MainTabShell · BottomCtaBand
Data viz
DateTile · CapacityRing · ProgressBar · AvatarStack · SegmentedDonutChart
Domain cards
SessionCard · ClubCard · VenueHero · SessionCompactRow · member row
Box sizes
AVATAR XS 28 · SM 32 · MD 40 · LG 44 · 2XL 88 · TOUCH_TARGET 44
Rationale
Every part is built from these — one 4px grid, the Pretendard type scale, the Classic-Grass palette, and the shared @twomore/ui primitives + domain cards.
2
Wizard chrome · NAV LAW (2026-08-15)
Structure
Header
WizardShell (wizard-shell.tsx) — NO title. Left: ✕ on step 0, a ChevronLeft back button once step > 0. Center: tap-navigable progress segments + "n / N". Right: 건너뛰기 (secondaryLabel) ONLY when the current step is skippable
Footer
owns exactly ONE job — the single primary CTA (+ optional primaryHint), never a back or secondary slot, on EVERY step including 6/7 경기 규칙 — the step-owned two-button footer that step used to be the sanctioned exception for is RETIRED (owner 2026-08-27); it keeps only its own CTA label override (이대로 진행 instead of 다음)
Step title
each step's own heading renders as the FIRST LINE OF THE BODY (Text role="pageTitle", e.g. "언제 만나나요?" on step 1) — never shell chrome
3
1/7 · 언제 — 정기 일정 불러오기 + 날짜 (club: calendar)
Structure
Sequential reveal
날짜 confirmed by a pick, THEN 시간 reveals — never both fields shown flat together (create-session-step1.tsx RevealStack)
날짜 (club)
SessionDateCalendar — inline month grid marked with the club's existing sessions (dot marker), $primary fill on the selected date; pickup uses a plain PickerRow instead
4
1/7 · 언제 — 시간
Structure
시간
ONE TimeBandField band — overnight-capable 00:00→30:00 domain
5
2/7 · 어디서 — picked venue + 사용 코트 수
Structure
Component
Step2 — VenuePickPanel (favorites/search/browse) collapses to PickedVenueCard (name + 확인됨/미확인 trust chip + 변경) once answered, with an attached CourtUsagePanel (which courts, numbered chips) tied to the SAME answer
Answer rule
다음 stays disabled until a picked court OR explicitly-committed free text (typed search text alone is not an answer)
6
3/7 · 어떻게 치나요 — 형식 → 구성 → 실력 → 성격
Structure
Component
Converges onto create-club-step4.tsx's axis grammar, RevealStack order per create-session-step3.tsx:366-404: 형식 (the ONE required axis — club-seeded from club.preferredFormat once that query resolves, U-33 2026-09-17, same as 구성 below; pickup keeps the true null-until-answered start, no chip pre-selected) → 구성 (club-seeded default, SelectionChip row + a first-encounter glossary caption, gates 형식's 혼복 option) → 실력 (two-tile fork + TierRangeSelector AttachedPanel, default "클럽 기준 그대로") → 성격 (친선/경쟁 SelectionCard pair, default 친선) — the instant 형식 answers, all three ride-along sections become visible together
No tier field
there is no bare numeric tier-range — only the fork
7
4/7 · 몇 명이서 — 경기 시간 → 최대/최소 인원 (live suggestion)
Structure
Component
DURATION-FIRST redesign (owner 2026-08-27, create-session-step-players.tsx): 경기 시간 leads as a SelectionChip row (시간 미정 / 20 / 30 / 40 / 60분 / 직접 입력) — NO 1인당 경기 수 Stepper anymore (matchesPerPlayer retired from this UI). A duration pick unlocks calculateSessionNeeds receipt AttachedPanel + SuggestedCountField 최대 인원 (live suggestion, pin/unpin) + 최소 인원; picking 미정 instead swaps in plain min/max Steppers with no math, no receipt (RevealStack: 경기 시간 gates the 최대/최소 인원 ride-along)
8
5/7 · 옵션 — 참가비 / 참석 마감 / 반복 / 소개
Structure
Component
Step 4 (skippable) — 참가비 (club: toggle reveal / pickup: plain amount) + 참석 마감 DropdownChip standalone (default 없음) + 반복 일정 DropdownChip standalone (default 반복 없음, with 반복 종료 ride-along) + 한 줄 소개 (200-char, save-then-proceed edit scope)
Skip
건너뛰기 is the header-RIGHT secondary button (secondaryLabel, create-session-screen.tsx:348-361, gated on nav.step === 4) — the footer keeps its normal single primary CTA (다음); this is the ONLY skippable step
9
6/7 · 경기 규칙 — the whole card IS the edit affordance
Structure
Component
Step 5 (create-session-step5.tsx) — a hero summary Card (이전에 사용한 규칙 OR 한국 표준 규칙), the WHOLE card Pressable (opens MatchRulesEditorSheet on tap, trailing chevron) — IA repair owner 2026-08-27: no separate 수정하기 row/button anymore. The facts line converged onto MetaSegments (diff since 2026-09-12) — all 5 facts (세트/게임/차이/타이브레이크/무승부), each its own hairline-broken segment, never a "·"-glued string; 듀스 OFF now reads 노애드 (was "듀스 ✕")
Shell primary restored
proceeding is back to the shell's normal single bottom-pinned primary CTA, keeping this step's own label (이대로 진행, wizard-copy.ts nextLabel step-5 special-case) instead of 다음 — the step-owned two-CTA footer this step used to be canon's cited exception for is retired
10
7/7 · 확인 · 리뷰
Structure
Component
Step 6 — Card of WizardReviewRows (icon + label/value + 수정 jumps back to the OWNING step) — includes 구성/실력 rows (both edit into step 3) alongside 일시/장소/형식과 인원/참가비/경기 규칙 and the conditional 한 줄 소개/참석 마감/반복 rows. 일시 is TWO lines (date, then time range — owner ruling 2026-09-14, never one "·"-glued string); 경기 규칙 shows only ONE headline fact (matchRulesLabel() = summaryFacts(...)[0], WizardReviewRow's value has no array slot) — the full breakdown is one 수정 tap away
CTA
primary 일정 만들기 (loading → 처리 중…)
11
수정 · DetailShell + 설정 SectionBlock
Structure
Component
edit-session-screen.tsx → edit-session/config-form.tsx — DetailShell ground="canvas" + one FlatColumn holding a single SectionBlock variant="flat" title 설정, with 9 EditFieldRows below (each opening a focused EditControlModal); editable only while status = open
Locked
종목 · 로테이션 · 모집 마감 have no row/UI at all — each would invalidate a commitment (format-sized roster, in-flight rotation, an already-RSVP'd deadline)
참가비 (U-06, 2026-09-17)
now its OWN row, CONDITIONALLY locked: editable until the first session_payments hold exists (useSessionPayments, mirrors DB trigger trg_block_fee_edit_with_holds), then the row disables and a caption below the list reads "입금이 시작되어 참가비를 바꿀 수 없어요" — was previously blanket-locked with no row at all