Flat mobile grammar v2 — designed from the ratios (2026-10-09)
Status: Draft · Date: 2026-10-09 · Owner brief: "If we really are going for flat layouts, reconfigure all of the components in the page and work out their ratios as well. Don't rely on the old layouts. Design from scratch with best practices in mind." This is the system: one reference frame, one type scale, one spacing ladder, one set of component proportions, and six screens composed only from them. Nothing here reuses the card grammar. Colour comes from colour-system-research-2026-10-09.md §2 and is not restated. Evidence for the numbers: Apple HIG (Dynamic Type Large: body 17/22, subhead 15/20, footnote 13/18, caption 12/16, title 2 22/28, 44pt targets, 20pt inset on grouped lists), Material 3 (type scale 11–57, list items 56/72/88, 48dp targets, 4dp grid, 16dp margins), Toss TDS (T7 13 → T1 30 in roughly 1.15 steps, ListRow padding 12/24, min 44), Seed (12/13/14/16/18/20/22/24 … 48, list padding 12, gutter 16, avatar 48/56, radius 4/8/12/16/20/24) — URLs in the lane appendices of the two research notes.
1. Reference frame and grid
- Reference device 390 × 844 pt (iPhone 15 class); the board draws at 1:1 CSS px.
- Grid: 4pt. Every size, gap and position is a multiple of 4; text line-heights are multiples of 2 and stack onto 4.
- Rail: 20 (Apple grouped-list inset 20; Toss 24; Seed 16). Content width 350.
- Touch target: 44 minimum (Apple), 48 preferred for primary actions (Material).
2. Type scale (Pretendard; size / line-height · weight)
Anchor: body 15/22 (Apple subhead 15, Toss T6 15 — the Korean body size peers use). Steps are the Toss/Apple intervals, not a single multiplier: 12 → 13 → 15 → 17 → 20 → 24 → 40.
| Role | Size / LH | Weight | Ratio to body | Use |
|---|---|---|---|---|
| display | 40 / 48 | 700 | 2.67 | the block numeral (count, ELO) |
| hero | 24 / 32 | 700 | 1.60 | detail title; identity name; stat numerals (stat strip uses 24/32) |
| title | 20 / 28 | 700 | 1.33 | screen header title |
| headline | 17 / 24 | 600 | 1.13 | row line 1; section titles; hero facts (500); primary button label |
| body | 15 / 22 | 400 · 500 | 1 | row line 2 (400, secondary ink); cell values (500); tab labels (600 active / 500); block meta |
| footnote | 13 / 18 | 500 · 600 | 0.87 | row trailing meta; tags (600); eyebrows; section actions; day headers |
| caption | 12 / 16 | 500 | 0.80 | tab-bar labels (11/14 on iOS is allowed), counters |
Line-height ratio 1.4–1.47 for text, 1.2 for display and hero (Apple 17/22 = 1.29, Toss 15/22.5 = 1.5, Seed 16/22 = 1.375). Weight ladder: 400 body · 500 meta/values · 600 headline/tags · 700 numerals/titles. No bold below line 1 in a row (ledger row 8; lane I).
3. Spacing ladder (roles, not just sizes)
| Step | Role |
|---|---|
| 4 | line 1 → line 2 in a row; numeral → unit |
| 8 | icon → text; tag padding; eyebrow → numeral in the block; section title → first row |
| 12 | row vertical padding; thumb → text |
| 16 | gap under tabs; chart inset; block internal element gap |
| 20 | rail; block padding; FAB offset |
| 24 | block → section header; hero top |
| 32 | section seam (between sections); hero → first section |
| 40 | bottom CTA band top padding to the last content |
Ratios: seam ladder 8 : 16 : 32 (1 : 2 : 4) for title→row, tabs→content, section→section; block padding = rail (20) so the block's inner edge lines up with the page's text edge; row padding 12 = 0.6 × rail.
4. Component proportions
| Component | Geometry | Proportion / rationale |
|---|---|---|
| Status bar | 44 (drawn in the mock for 1:1 honesty, quiet) | iOS |
| Header | 56; title 20/28·700 at the rail; right actions 24 glyphs in 44 targets, gap 8; no divider | icons are action targets (owner); 56 = Apple nav bar + 12 |
| Underline tabs | row 44; label 15/22 600 ink (active) / 500 $textSecondary; label padding 0 12; underline 2pt ink under the label width; 16 below | Toss, 카카오뱅크, 배민 |
| Brand block | width 350, radius 16, padding 20, min 144: eyebrow 13/18·600 at α0.8 + chevron 20 right · 8 · numeral 40/48·700 with unit 15/22·600 on the baseline · 8 · meta 15/22·500 at α0.85; ELO variant adds 8 · progress 6 (optic on α0.2 track) · 8 · caption → 158 | 144/844 = 17% (peers 16–18%); width : height = 2.4 : 1; left-stacked; pressable; no button inside |
| Section header | title 17/24·600; action 13/18·500 secondary + chevron 16; 32 above, 8 below | 1 : 2 : 4 seam ladder |
| Row, media (sessions, clubs) | padding 12; thumb 56 r8 (club logo r12); gap 12; L1 17/24·600 ink; 4; L2 15/22·400 secondary; trailing 15/22·600 tabular or one Tag; pitch 80; no divider, no fill | thumb : pitch = 0.7 (Toss 혜택 0.77); text block 50 inside a 56 thumb; one trailing element |
| Row, person (members, dues, ranking, results) | padding 12; avatar 40; gap 12; L1 15/22·600; 4; L2 13/18·500 secondary; trailing 15/22·700 tabular (score, amount, ELO) or one Tag; pitch 64 | club-detail canon; Seed avatar 40–48 |
| Day header | 32 tall; 15/22·600 ink date + 13/18·500 secondary relative day at the right; 16 above | — |
| Tag | height 24; padding 0 8; radius 6; 13/18·600; weak / solid tiers from the ledger tone table; ≤ 1 per row | label : row = 24 : 80 = 0.3 |
| Choice (control) | height 36; padding 0 14; radius full; 15/22·500; 1px $border resting; ink fill + check selected | settings and wizards only |
| Primary button | 52; radius 12; 17/24·600; brand solid | Toss 52–56, Apple 50 |
| Bottom CTA band | button 52 inside padding 16 + home inset 34; a hairline above only when content scrolls under it | one CTA per detail page |
| FAB | extended pill 48; padding 0 20; icon 20 + 8 + label 15/22·600; brand solid; 20 from the right rail and 16 above the tab bar | 당근 글쓰기; the one coloured action on list screens |
| Tab bar | 56 + home inset 34; icons 24 stroke 2; labels 11/14·500; active = filled glyph + ink + 600 | Apple / M3; neutral, never brand |
| Stat strip | 80; three columns; numeral 24/32·700; label 13/18·500 secondary; 1 × 24 hairline between columns | numeral : label = 1.85 |
| Hero (detail) | top 24; eyebrow 13/18·500 secondary; 4; title 24/32·700; 16; three facts 17/24·500 with a 20 glyph (stroke 2, label colour) + 8; fact gap 8; 32 below | ledger §5 tiers: the only glyphs on the screen |
| Cell row (facts) | 48; label 15/22·400 secondary left; value 15/22·500 ink right; no divider | club-detail canon |
| Roster | avatar 48 in a 4-up grid (gap 16); name 13/18·500 below (4); level Tag 24 below (4) | Playtomic |
| Chart | 160 tall, inset 16, one ink line, no fill | — |
| Avatars | 40 rows · 48 roster · 56 identity block | Seed 48/56 |
| Radii | 6 tag · 8 thumb · 12 button and club logo · 16 block · full avatar / pill | Seed 4–24 |
| Icons | 24 actions · 20 hero facts · 16 chevrons | Apple 17–24 |
4b. Seams — how flat surfaces divide (added 2026-10-09, lane K)
Owner: "Are we adding dividers? What's the best practice on flat surfaces?" Evidence: lane K — 22 measured peer screens across 15 apps, Apple HIG (plain vs inset-grouped lists, scrollEdgeAppearance), Material (lists, dividers, liftOnScroll), Seed's three-tier separator statement (inset line = weak, full line = medium, 8px band = strong), Toss TDS Border (full / padding24 / height16), GOV.UK section break, Gestalt proximity and common region (Wertheimer 1923, Palmer 1992). No controlled study of dividers vs whitespace on mobile lists exists; these are guideline-and-practice rules, stated with the numbers peers actually use.
| Seam | Rule | Value | Evidence |
|---|---|---|---|
| Row → row (media 80, person 64) | no line; the 12 padding is the rhythm | — | 9 of 17 peer screens with consecutive rows use spacing only, all with a 30–56pt anchor and a 59–81 pitch (Toss 혜택, 카카오뱅크, Monzo); lines appear only on 88–121pt photo rows and inside cards |
| Cell → cell (single-line 48 label/value: 정보, settings, dues ledger) | quiet hairline, inset to the rail (20 both sides) | #E5E5E5 1px (1.26:1), the heaviest measured Korean row hairline (네이버); peers 1.10–1.26 | Apple inset-grouped separators, Material 1 lists, GOV.UK summary list, Carbon; 테니스프렌즈 내 정보 — a 48pt cell has no anchor, so proximity alone does not group it (amends the 2026-09-06 FactRow rule; D8) |
| Section → section | whitespace + the section title, no line | 32 ink-to-ink (the edge rule trims the last row's bottom padding, so the box gap is 32) | flat peers 27.7–55.8pt ink-to-ink, median 36; none of the 22 screens draws a line between ordinary sections |
| Zone change (hero zone → content; content → comments/discussion) | one full-bleed band, at most one per screen | #F2F2F2 (L* 95.5), 12pt, 24 above and 24 below | 카카오페이 band 13.1pt with 33/37 air; published Toss 16, Seed 8, Material heavy 8; median of 12 values 11.7; measured and published bands sit at L* 96.1–96.2 |
| Block / hero → content | spacing only | 24 | peer median 25 (21–34, 8 of 10); the coloured block is already a container |
| Tab row → content | spacing | 24 before a title or day header (was 16) | peers 22–31 under underline tabs |
| Sticky header / tab row | hairline only while content scrolls under it | full-bleed #D4D4D4 (1.48:1) | Apple scrollEdgeAppearance (transparent at rest), Material liftOnScroll; 4 of 6 peers draw it under the tab row, the sharpest at 1.45 (Toss) and 1.49 (배민) |
| Tab bar / CTA band | hairline only while content lies below; no shadow | #D4D4D4 | same; only floating pill tab bars (당근, 카카오뱅크, 네이버) carry a shadow |
| Dark mode | edges white α0.14, cells white α0.09; the band goes darker than the canvas | #000000 band under an ink canvas | Toss, Seed, Apple dark grouped |
Hairline tiers, therefore two: edge #D4D4D4 (sticky edges, tab rows) and cell #E5E5E5 (single-line cell groups). Nothing else draws a line.
Why this amends the Divider law of 2026-09-06 ("dividers separate sections"): that law was written after a build that drew hairlines between rows and whitespace between sections, so a section edge read like one more row. In this grammar rows carry no line, every section opens with a 17/600 title on 32 of air, and the row rhythm (80/64) is taller than the old 40pt rows — the failure mode the section line fixed cannot occur. The section line is retired (D9); the band replaces it for the one case a change of kind needs more than air (D10).
Developer test: delete the line and keep the gap — if the two things still read as separate groups, there is no line. A line is allowed only at a sticky or tab edge while content passes under it, between single-line cells of one group, and as the one band where the kind of content changes.
5. Screens (390 × 844; stacks top → bottom; every number from §2–§4)
홈 — status 44 · header 56 ("홈", bell) · 8 · block 144 (eyebrow "내일, 9월 19일 (토)" + chevron; "2" + "개 일정"; "첫 시작 09:00 · 올림픽공원") · 24 · section "내일 일정" / "전체 보기 ›" · 8 · 2 media rows (80) · 32 · section "이번 주" · 8 · 2 media rows · FAB "+ 일정" · tab bar. Trailing on rows: "4/8" or one Tag (참석 success-weak).
경기 · 번개 찾기 — status · header ("경기", search, bell) · underline tabs (내 경기 / 번개 찾기 / 저장됨) · 16 · day header "9월 18일 (금) · 오늘" · 3 media rows (LIVE danger-solid Tag; "6/8"; 마감 임박 warning Tag) · 16 · day header "9월 19일 (토) · 내일" · 2 rows · FAB "+ 번개" · tab bar.
클럽 — status · header ("클럽", invite code, plus) · tabs (내 클럽 / 탐색) · 16 · media rows with club logo 56 r12: L1 club name, L2 "서초구 · 멤버 42 · NTRP 3.0–4.0", trailing unread count Tag (neutral) or nothing · 탐색 variant: trailing 모집 중 brand-weak Tag · tab bar.
기록 — status · header ("기록", bell) · 8 · ELO block 158 (eyebrow "ELO · NTRP 3.5–4.0" + chevron; "1,482"; progress; "마스터까지 518점") · 24 · tabs (우리 클럽 / 친구 / 지역 / 전국) · 16 · stat strip 80 (47 총 경기 · 68% 승률 · 3승 연승) · 32 · section "최근 ELO 추이" · 8 · chart 160 · 32 · section "클럽 랭킹" / "전체 보기 ›" · 8 · 3 person rows (rank number 13/18·600 secondary in a 24 column before the avatar; trailing ELO 15/22·700 + level Tag) · tab bar.
프로필 — status · header ("프로필", settings) · 8 · identity block 144 (avatar 56; name 24/32·700 + level Tag; "강남 테니스 클럽 · 3년 차" 15/22 α0.85; edit 24 ghost top-right) · 24 · stat strip (47 총 경기 · 68% 승률 · 92% 출석률) · 32 · section "친구 24명" / "전체 ›" · 8 · avatar row 40 × 6 (gap 8) · 32 · section "최근 경기" / "전체 보기 ›" · 8 · 3 person rows (L1 opponents, L2 date; trailing score 17/24·700 tabular + 승/패 Tag) · 32 · section "업적" · tab bar.
세션 상세 — status · header (back; bookmark, more) · hero (eyebrow "강남 라켓클럽"; "올림픽공원 테니스장"; facts: 📅→calendar "9월 19일 (토) 09:00–11:00", pin "올림픽공원 테니스장 · 하드", card "무료") · 32 · section "참가자 6/8" · 8 · roster 4-up (two rows, two empty dashed slots) · 32 · section "정보" · cell rows (종목 복식 · 대상 NTRP 3.0–4.0 · 호스트 김민준 · 참가비 무료) · 40 · CTA band "참석하기".
6. What leaves
Cards on list surfaces; the segmented pill on lists; the capacity bar, price, bookmark, three emoji lines and status word on rows; $primarySubtle as a surface; the 21-glyph vocabulary (4 concrete glyphs stay, hero only); any component not in §4. Web follows the mobile grammar later.
7. Verification the board must pass
Every drawn element's top edge, height and left edge are multiples of 4; the rail measures 20 on every screen; media rows pitch exactly 80 and person rows 64; the block measures 144 (158 for ELO); the tab bar 56 + 34; type sizes and line-heights match §2 to the pixel; one coloured action per list screen (the FAB) and one block per hub; one trailing element per row; zero glyphs in rows. Measured from the DOM, printed under each screen.