Skip to content

Lane D — colour usage: does TwoMore spend its brand colour too freely? ​

Status: Reference · Date: 2026-10-08 · Evidence appendix for visual-benchmark-2026-10-08.md (§6 colour budget). Frame, crop and script paths refer to the session scratch directory (not committed).

Date: 2026-10-08 · Author: lane D (colour usage) · Evidence for docs/design/research/visual-benchmark-2026-10-08.md §1–§3 · English only · all paths below are relative to the repo unless they start with bench/ (scratch dir …/scratchpad/bench/). Repo files were read, never modified.

Owner hypothesis under test: "Other major apps use colour to draw attention and restrain from using colour when not necessary. We are perhaps using too much of the primary colour everywhere and not using enough colours to accentuate contents."

0. Verdict in one screen ​

The hypothesis is right about hue and role, wrong about quantity. We paint a quarter of the peer median of saturated area, but one green does every job and almost none of it is spent on content.

Claim inside the hypothesisMeasuredVerdict
"We use too much primary colour" (as area / object count)Saturated area on our hub and list frames: median 1.4% (range 0.1–3.4%) vs peers median 5.1% (IQR 2.6–9.3, n = 72 app rows). Large solid saturated objects per frame: ours 0–1 (plus the CTA band on detail pages; 4 saved-bookmark glyphs on the 테니스장 list) vs peers median 2.5 (IQR 1.0–3.7).Refuted. We paint about a quarter of the peer median.
"…everywhere" (as hue exclusivity)Brand green is 96% of our saturated pixels (median of 11 frames, range 65–100; 100% of solid fills). Peers' own brand hue is 50% raw / 61% solid (n = 11 apps with a known brand hue); the top hue of any peer is 52% raw / 69% solid (n = 72). 57 of 72 peers carry ≥ 2 hue families at ≥ 10% of their saturated pixels; 10 of our 11 frames carry exactly one.Confirmed. One hue does every job.
"…everywhere" (as role)307 brand-token sites in shipped UI: 66% chrome/controls, 23% content accents, 10% decoration. Of all content-accent token sites, 88% are brand green (72 of 82); the court / medal / accent / Saturday-blue palette has 10 direct sites in scope. 12 of 17 peers I inspected render tabs, segments and selected chips in ink or neutral; none fills a selected segment solid brand on a brand-tinted track.Confirmed. Brand colour sits on controls, where peers keep ink.
"…not enough colours to accentuate content"10 of 17 inspected peers show ≥ 3 hues in content objects (pictograms, per-account discs, per-calendar dots, level/category chips). Our token palette has the content hues (optic, court surfaces, medals, ELO tiers, club identity hues) but only profile.png shows any (orange + yellow = 29% of its saturated pixels, the 업적 row); optic has 0 consumers.Confirmed. The palette exists and is unspent.
Side effect worth knowingPale-tinted fills: ours are all green (hub median 2.3% of the frame, 17–21% on the two detail frames with the map placeholder); peers' median is 0.9% tint and 5.2% neutral grey. Brand #006D37 and success #14804A are ΔE76 7.5 apart, so green is brand, selection and "paid/confirmed".New finding: "restrained colour" also means peers fill with grey and we fill with mint.

The six numbers to remember: 96% (our brand-hue share) vs 50%/61% (peers' brand-hue share) · 1.4% vs 5.1% (saturated area) · 1 vs 2 (hue families ≥ 10%, median) · 66% of brand sites are chrome · 12 of 17 peers use ink/neutral for selection · 0 consumers of optic.

1. Hue budget, measured ​

1.1 Method (and three traps I hit) ​

  • Script: bench/sample.py --hues <frames> [--crop top,bottom] (saturated = C*ab > 30; family by CIELAB hue angle). Raw outputs are saved next to this file: bench/lane_d/sample_hues_{ours_full,ours_crop9,kr,cat,glob_m}.jsonl. The coordinator recalibrated the buckets mid-task (red 350–40, orange 40–75, yellow 75–105, lime 105–130, green 130–185, teal 185–245, blue 245–292, violet 292–350); everything here was re-run after that change. bench/lane_d_hues.py imports sample.HUE_BUCKETS, so it follows any further recalibration. Brand green #006D37 = 151°, optic #C6E84B = 115° (lime), Toss blue 284°, 당근 orange 52°.
  • Two views per frame. Raw = every saturated pixel (what --hues prints; photos, text and icon strokes included). Solid-fill = saturated pixels whose ±2 px neighbourhood is uniform (≤ 8 levels per channel): pills, buttons, bars, blocks, dots, discs; photo texture and glyph strokes drop out (bench/lane_d_hues.py). A family "counts" when it holds ≥ 10% of the frame's saturated pixels. Per-app rows pool frames with equal weight.
  • Peer inputs (72 app rows = 68 distinct apps; Toss, Playtomic, Spond and TeamSnap appear in two crop sets): crops/*.png (Korean, 18 apps), b_crops/*.png (category, 35 apps), global = the inset crops_c/m_*.png crops plus gmail_* (19 apps; Apple's own appledev/appstore dropped). The un-inset crops_c/<app>_N.png files still contain the App Store's saturated marketing background (Monzo coral, Playtomic blue, Cash App green, Wise lime), so they are not used; bench/lane_d/peers_per_frame.tsv has them in its glob rows, ignore those and use bench/lane_d/glob_m_per_frame.tsv.
  • Our frames: ours-phone/*.png (780×1688), ours-1440/*.png; ours-dark/* excluded from the headline (peers are light-mode). The dev-tools pill and CTA band matter: on the venue-detail and session-detail frames the Next "1 Issue" pill (#CA2A30, 0.5% of the frame) is 18–20% of the saturated pixels (10% on the clubs frame), and the green CTA band (#006C36) is 3.4–4.5% of the frame on four phone frames. Cutting the bottom 9% removes both (and, on tab roots, the tab bar): sessions_*.png 5.8% → 0.1%, courts.png 6.2% → 0.7%, directory-venues_*.png 6.4% → 0.7%; tab-root frames barely move (root 1.3 → 1.4, records 2.5 → 2.6) because their tab icon is tiny. Headline numbers use the cropped values; both are in the table.

1.2 Our frames ​

clubs (phone) is dominated by a clay-court photo (orange 45–77% of its saturated pixels), so it is shown but excluded from the medians.

frame (dir)cropsat %top hue (%)2nd hue (%)families ≥10%green 130–185° %solid-fill sat %solid top (%)
클럽 clubs (photo hero) (phone)full14.1orange 45green 343347.46green 57
클럽 clubs (photo hero) (phone)bottom 9% cut9.0orange 77blue 11212.58orange 97
테니스장 list (phone)full6.2green 97yellow 11975.38green 100
테니스장 list (phone)bottom 9% cut0.7green 77yellow 71770.26green 100
코트 상세 venue detail (phone)full6.4green 81red 182815.17green 84
코트 상세 venue detail (phone)bottom 9% cut0.7green 100red 011000.35green 100
public share page (phone)full1.4green 97yellow 21971.18green 100
public share page (phone)bottom 9% cut1.6green 97yellow 21971.30green 100
프로필 profile (phone)full1.1green 65orange 222650.17green 98
프로필 profile (phone)bottom 9% cut1.2green 65orange 232650.18green 98
기록 records (phone)full2.5green 98orange 11981.53green 100
기록 records (phone)bottom 9% cut2.6green 99orange 01991.67green 100
홈 root (phone)full1.3green 97yellow 11971.09green 100
홈 root (phone)bottom 9% cut1.4green 98yellow 11981.20green 100
일정 상세 session detail (phone)full5.8green 80red 202804.92green 83
일정 상세 session detail (phone)bottom 9% cut0.1green 99red 11990.00green 100
클럽 clubs, photo hero (1440)full3.8green 91lime 51911.09green 99
클럽 clubs, photo hero (1440)bottom 9% cut3.4green 91lime 41911.19green 99
경기 matches (1440)full0.8green 84blue 51840.53green 100
경기 matches (1440)bottom 9% cut0.9green 84blue 51840.58green 100
기록 records (1440)full3.0green 97yellow 11971.57green 100
기록 records (1440)bottom 9% cut2.8green 96yellow 11961.61green 100
홈 root (1440)full1.2green 91yellow 21910.88green 100
홈 root (1440)bottom 9% cut1.4green 92yellow 21920.97green 100

Reading it: outside the photo frame our saturated pixels are 65–100% brand green; the one frame below 90% is profile.png, where the 업적 (achievements) row adds orange 22% + yellow 7% (bronze/gold medal tones) and the red 4% is the avatar photos. Families ≥ 10%: 1 on 10 of 11 frames (profile has 2).

1.3 Peers, per app (frames pooled) ​

Columns: saturated % (raw), top and second hue family with their % of the saturated pixels, number of families ≥ 10% (raw); the same for solid fills. Per-frame rows: bench/lane_d/peers_per_frame.tsv, bench/lane_d/glob_m_per_frame.tsv. Photo-led apps (당근, 무신사, 컬리, 오늘의집, 크림, 올리브영) inflate the raw families, so read their solid columns.

setappframessat % (raw)top hue %2nd hue %families ≥10%solid-fill sat %solid top %solid families ≥10%
categoryace22.3blue 95yellow 410.00blue 1001
categoryatpwta212.3violet 92red 513.65violet 991
category밴드33.8violet 25green 2340.89green 432
categorybaseline38.1blue 85violet 1021.98blue 892
category클래스10122.7orange 50red 3230.43orange 682
categoryCourtMate32.2lime 91orange 410.74lime 1001
categoryespn22.6blue 54red 3820.56blue 842
category플랩풋볼310.1lime 63green 2924.70lime 911
categoryfotmob32.3blue 71red 1320.03blue 1001
category프립35.2blue 31violet 2841.25blue 433
category골프존34.4blue 88red 512.27blue 991
category그린라이트22.0blue 34red 2930.34blue 892
categoryHeja16.3blue 60green 3623.13blue 652
category김캐디37.1lime 46green 2231.56lime 772
categoryMeetup211.1red 53green 1440.98red 842
category문토33.4teal 29orange 1950.88teal 543
categorynrc326.8orange 55teal 36221.28orange 572
categoryonefootball33.2red 42blue 2430.06lime 572
categoryPlaytomic38.1violet 26blue 2241.81violet 722
categoryralli28.7lime 36yellow 3632.41lime 542
categoryrallyracket24.8lime 46green 4220.62green 612
categoryrallyup34.4yellow 58lime 2131.01yellow 1001
categoryrunday21.8violet 85orange 1020.34violet 981
categorysmartscore317.1lime 44blue 4224.45blue 752
category스매시22.5blue 41orange 3540.60orange 512
category소모임35.1blue 61yellow 1421.08blue 961
category숨고21.5orange 23green 2150.02violet 722
categorySpond34.7lime 37red 3341.33red 493
categorystrava26.1orange 91yellow 510.07orange 782
categoryswingvision27.2green 47orange 4222.61green 522
categoryteamsnap212.7blue 92lime 413.41blue 1001
category테니스프렌즈36.6lime 91green 813.54lime 951
categorytennispal22.8lime 85red 610.56lime 981
categoryusta235.2blue 100violet 0128.01blue 1001
categoryutr21.1blue 35green 3230.02green 872
globalairbnb414.2orange 50red 3525.00red 542
globalcashapp42.4green 83orange 911.47green 981
globalduolingo35.6blue 33yellow 3224.70blue 352
globalgmail31.0blue 64green 2330.68blue 692
globalklarna24.5red 98yellow 113.92red 991
globallinear22.1violet 86orange 311.69violet 971
globalmonzo310.5red 60orange 2226.48red 761
globalnotion40.9blue 48red 1540.39blue 663
globalPlaytomic26.5red 36orange 1941.45red 662
globalrevolut23.8orange 99yellow 112.21orange 1001
globalrobinhood212.3yellow 59lime 25311.61yellow 603
globalSpond310.6red 39lime 1947.32red 483
globalspotify26.8orange 29violet 2352.91violet 473
globalstripe31.3green 72violet 2320.79green 882
globalteamsnap33.8blue 59orange 1921.78blue 682
global토스43.1orange 24lime 2050.57yellow 314
globaluber20.1green 58yellow 3120.06green 692
globalvenmo414.7blue 67violet 13210.75blue 712
globalwise423.8lime 49green 40218.91lime 522
KR29CM31.1orange 58yellow 2520.13orange 752
KR배민417.8violet 50green 17311.48violet 612
KR쿠팡43.8green 30yellow 2130.84green 423
KR당근42.8orange 76blue 811.67orange 871
KR카카오뱅크42.8yellow 44orange 4222.01yellow 472
KR카카오맵314.0teal 49yellow 1838.68teal 741
KR카카오페이56.1blue 41red 2244.39blue 464
KR카카오톡38.8blue 55lime 2925.51blue 841
KR크림49.4orange 54red 2437.04orange 632
KR컬리413.7violet 43orange 3035.88violet 841
KR밀리의서재35.9red 32orange 1851.71violet 284
KR무신사45.2orange 70red 1223.38orange 821
KR네이버410.0violet 35green 3526.51green 412
KR네이버지도48.0orange 41yellow 2332.16yellow 365
KRNOL47.5blue 34violet 2444.74blue 333
KR오늘의집41.5blue 50orange 3520.84orange 452
KR올리브영42.5red 79orange 711.49red 911
KR토스42.9orange 22red 2150.58yellow 295

1.4 The statement the owner wants tested ​

Brand-hue share of saturated pixels (raw / solid)Hue families ≥ 10% (median / mean)Saturated area (raw median)
TwoMore (11 frames, bottom 9% cut)green 96% / 100% (range 65–100 / 98–100)1 / 1.1 (10 of 11 frames = 1)1.4%
Peers with a known brand hue (n = 11: 토스 blue, 당근 orange, 배민 violet, 네이버 green, 밴드 green, 카카오뱅크 yellow, 카카오톡 yellow, 테니스프렌즈 lime, Monzo coral, Wise lime+green, Cash App green)median 50% / 61% (range 8–91 / 7–98)——
All 72 peers, top hue familymedian 52% / 69%raw 2 / 2.56, solid 2 / 1.98; ≥ 2 families: 57 of 72 raw, 42 of 64 solid5.1% (IQR 2.6–9.3)
Korean mainstream (18)top family 46% / 47%raw 3 / 2.78; 16 of 18 have ≥ 26.0%
Category (35)top family 54% / 84%2 / 2.464.8%
Global (19)top family 59% / 67%2 / 2.534.5%

Per-peer brand-hue shares (raw / solid): 토스 18 / 23 · 당근 76 / 87 · 배민 50 / 61 · 네이버 35 / 41 · 밴드 23 / 43 · 카카오뱅크 44 / 47 · 카카오톡 8 / 7 · 테니스프렌즈 91 / 95 · Monzo 60 / 76 · Wise 88 / 95 · Cash App 83 / 98.

Honest limits of the test: (1) three peers are as single-hued as we are, 테니스프렌즈 (95%), Wise (95%) and Cash App (98%), so "one hue" is not wrong in itself; the difference is where that hue sits (§3: a lime identity card and a FAB, an ink-black CTA, a green ring) rather than on every control. (2) Raw numbers carry photos and pictograms; the solid view removes them and still gives a peer median of 2 families. (3) Our own saturated area is low, so any fix is a re-allocation, not a reduction.

Two supporting measures (scripts bench/lane_d_blobs.py, bench/lane_d_tint.py; outputs bench/lane_d/blob_stats.txt, tint_stats.txt):

  • Large solid saturated objects per frame (components ≥ 90 px² at 390 px width): peers median 2.5; ours: 홈 1 (segmented pill), 기록 1 (pill), 프로필 0, 코트 상세 1 (#006D37, 612 px², the filled 저장 취소 disc), 테니스장 list 4 (the four saved-bookmark glyphs, 110 px² each), 일정 상세 0 once the CTA band is cut. With the band: the CTA band alone is 3.4–4.5% of the frame on 4 of 4 phone frames that carry it; the median peer's entire solid-fill saturated area is 1.7%.
  • Pale-tint vs neutral-grey fills (flat fills, C* 6–30 and L* ≥ 80 vs C* < 6 and L* 80–97.5): ours on hubs median 2.3% pale tint (홈 3.5, 기록 4.2, 프로필 2.3, 1440 홈 1.7, 기록 3.6, 클럽 1.6, 경기 0.0; all green-hued) and 0.04% neutral grey; on 코트 상세 / 일정 상세 the pale-green map placeholder is 17.4% / 20.9%. Peers (n = 72): median pale tint 0.9%, median neutral-grey fill 5.2% (IQR 1.1–10.9). Only 6 of 72 peers exceed 5% tint, and each is one bounded hero block (오늘의집 15.1, 컬리 10.3, 네이버지도 10.3, 카카오뱅크 8.6, 배민 7.9, Klarna 5.9).

2. Where our brand colour goes: code census by role ​

Scope: packages/ui/src, packages/features/*/src, packages/app/src/presentation (1,310 non-test .ts/.tsx files; 168 reference a brand token), comment lines dropped, the dev panel (19 sites, not shipped UI) listed separately. Tokens counted: $primary, $primarySubtle, $primaryMuted, $primaryText, theme.primary, colors.primary, primary[…]; $brand and $iconActive do not exist in the code. Lines classified by pattern rules plus 32 manual overrides (bench/lane_d/census_classify.py; the classified list is bench/lane_d/census_classified.json). Counts are token sites (lines), a lower bound for impact because shared primitives multiply them (§2.2).

2.1 Counts per role ​

RoleSitesShare of 307Sub-roles (count)Representative site
(a) chrome / controls20366%selection / active / toggle state (107); link-style text and nav icons (56); UI control primitives (27: chips, search, pickers, action circles, toggle); button/pill/toggle fill (8); $primary selection borders (4); focus ring (1)packages/ui/src/selection-chip.tsx:122-123 (selected = $primary border + $primarySubtle fill); packages/ui/src/button.tsx:79 (primary fill; defaultVariants.variant = 'primary' at :138); packages/ui/src/pressable-visual.ts:85 (web focus ring $primary); packages/ui/src/section-header.tsx:162 (link)
(b) content accents7223%content primitives: badge / avatar / meter / date tile / banner / stat tone (26); feature icons (17); meters, dots, series, team accents (22); numerals (7)packages/ui/src/avatar-bubble.tsx:139,151 (initials: $primarySubtle bg + $primary text); packages/ui/src/progress-bar.tsx:90-112 (fill $primary, track $primarySubtle); packages/app/src/presentation/components/elo-fact-row.tsx:52 and packages/features/records/src/cards/hero-card.tsx:59 (ELO numeral and ring 1)
(c) status semantics188(separate)$error 75, $success 29, $warning 24, badge-pair tokens 84 ($badgeWarning* 30, $badgeError* 17, $badgeSuccess* 12, neutral 10, muted 5, info 4, accent 3, premium 2, surface 1)packages/app/src/presentation/components/status/status-variant-maps.ts:43-137 (every status → one of nine badge variants); packages/ui/src/badge.tsx:64-72
(d) decoration3210%tint fills and panels behind content ($primarySubtle backgrounds, monogram letters, hero veils)packages/ui/src/empty-state.tsx:78,84 (64 px disc, 101 EmptyState sites); packages/app/src/presentation/components/tier-badge-utils.tsx:55 (tier chip fill); packages/ui/src/summary-card.tsx:27
Non-brand content palette (court / medal / accent / Saturday blue)10 in scope—$accentWarm 4 (team 2 in match-scoreboard.tsx:331,371), $calendarSat 4, $accentCool 1, $medalGold 1; court tokens reach the UI through visual-accents.ts (outside the census scope)packages/app/src/config/visual-accents.ts:84-92 (court surfaces), :117-122 (medals)
Dev panel (excluded)19——packages/app/src/presentation/components/dev-panel/scenarios-pane.tsx

Brand-token sites (a+b+d) = 307; brand = 61% of all colour-token lines (307 of 307 + 195 semantic/content lines, 3 of them overlapping). Of the 82 content-accent sites (brand 72 + non-brand 10) 88% are brand green.

2.2 Components that paint brand green on every screen (call-site multipliers) ​

ComponentWhere it paints brandCall sites in scopeProof
Tab bar active icon + labeltabBarActiveTintColor: primary5 tab rootsapps/mobile/app/(tabs)/_layout.tsx:62; ours-phone/records.png (trophy + 기록)
Segmented control (PillNav / SegmentedTabs)solid $primary sliding pill on $surfaceTertiary (#E5FAEA) track, label $primaryText13 PillNav + 7 SegmentedTabs; MainTabShell hosts it on the 홈, 클럽, 기록 and 메시지 roots (frames: 홈, 기록, 클럽 at 1440)packages/ui/src/pill-nav.tsx:223,400 (indicator), :80,:164 (track), :140 (label), :192 (overflow-pill fill); packages/ui/src/main-tab-shell.tsx:120; ours-phone/root.png, records.png, ours-1440/clubs.png
Primary Button / BottomCtaBand$primary fill; band is the screen's only bottom elementButton 63 (12 explicit primary, 27 outline, 4 ghost, 3 secondary, 2 destructive, ~15 default or dynamic = primary); BottomCtaBand 45packages/ui/src/button.tsx:79,138; ours-phone/sessions_*.png, courts.png, directory-venues_*.png, clubs_*.png
Selection chips / cards / checks / togglesselected = $primary border + $primarySubtle fill + $primary 700 labelSelectionChip 106, SelectionCard 19, DropdownChip 23, TogglePill 17, SelectionCheck 2packages/ui/src/selection-chip.tsx:122-123; chip-label-visual.ts:24; selection-card.tsx:45-46; toggle-pill.tsx:40; selection-check.tsx:70-71; ours-phone/courts.png ("전체 145")
Initials avatarsdefault tone primary: pale-green disc + green initialsAvatarBubble 27 (6 neutral, 7 explicit primary, ~14 default)packages/ui/src/avatar-bubble.tsx:160 (tone = 'primary'), :139,151; sessions_*.png ("D나"), profile.png ("DE")
Empty-state disc$primarySubtle 64 px disc behind a stroke iconEmptyState 101packages/ui/src/empty-state.tsx:78,84
Links and section actions$primary text and chevronSectionHeader 25, HeaderTextAction 6, LinkifiedText 1, plus 56 link-style sitespackages/ui/src/linkified-text.tsx:68; header-text-action.tsx:58; section-header.tsx:162,165; courts.png ("제안 내역")
Bookmark / savedsaved icon $primarySaveButton, session card, venue rowspackages/app/src/presentation/components/sessions/save-button.tsx:104,125; session-card-v2.tsx:392; courts.png (four green bookmarks)
Progress and rings$primary fill on $primarySubtle track; ELO ring = primary + success + tealProgressBar 15, MiniBarMeter 3, CapacityRing 2packages/ui/src/progress-bar.tsx:112; hero-card.tsx:59-61; records.png
Stat numeralstone="primary" → $primarytone="primary" appears on 21 JSX sites (StatStrip, Well, AvatarBubble, rows)packages/ui/src/tone-colors.ts:29; stat-grid.tsx:38; profile.png ("50+", "64%")
Focus ring (web)outlineColor: '$primary' on every Pressableallpackages/ui/src/pressable-visual.ts:85
Web sidebaractive item $primary ink on $primarySubtle; "번개 만들기" primary buttonevery desktop screenapps/web/components/app-nav.tsx:92,111,120,250,260; ours-1440/root.png

2.3 Every colour token we have for content, and where it is used ​

Token (value, hue)DefinedConsumersState
primary ladder #E5FAEA … #006D37 … #022711 (151°)packages/ui/src/tokens.ts:30-41, classic-grass.theme.ts:30-44307 sites (§2.1)Overloaded: control, number, icon, ring, text, selection
optic #C6E84B (115°, L* 87)tokens.ts:47none in packages/ or apps/ (only wireframes docs/twomore_design_system/wireframes/visual-tone.jsx and seed logos)Unused
courtGreen #006633, courtClay #B84A2E (42°), courtHard #247BA0 (245°)tokens.ts:89-91, classic-grass.theme.ts:45-49packages/app/src/config/visual-accents.ts:84-92 (surface accents), :100-106 (facility), :161-175 (signal categories) → VenueLink pill and tile (venue-link.tsx:75-98), venue card/row/hero/tileUsed on venue surfaces only
accentWarm #8A5A12 on #FFF4D6, accentCool #4B0082 on #F2E8FF (314°)classic-grass.theme.ts:78-81match scoreboard team 2 (match-scoreboard.tsx:331,371), indoor / shop / social / chat accents (visual-accents.ts), one settings iconSparse
medalGold #D9A621, Silver, Bronze, Platinumtokens.ts:99-102, shared-semantics.ts:32-37ACHIEVEMENT_TIER_COLORS (visual-accents.ts:117-122) → 프로필 업적 row (profile.png: bronze rings, gold trophy), podium (leaderboard-podium.tsx:44-47), king court border (court-card.tsx:231)Used on profile / ranking only
ELO tier palette #CD7F32 bronze, #8E8E93, #D4A017, #9B59B6, #E74C3C (+ dark variants)packages/app/src/domain/rules/tier.config.ts:67-130groupTierColor → GroupTierDot (ranking-tab.tsx:416, row-cells.tsx:24, club-groups-section.tsx:253) and the TierRangeBadge dotDropped where it matters: TierBadgeChrome fills with $primarySubtle (tier-badge-utils.tsx:55), so every tier chip ("NTRP 3.0–3.5" in records.png, "신뢰 멤버" in profile.png) is the same mint whatever the tier
Club identity: 8 flag presets at 45° hue steps (152, 197, 242, 287, 332, 17, 62, 107) plus a custom huepackages/ui/src/flag-medallion-presets.ts:132-139 (hueFlagPalette)FlagMedallion, club-card previews, create-club step 8Built; the fallback logo bubble is brand tint (club-logo-bubble.tsx:45), not the identity hue
Status: success #14804A (153°), warning #946200 (76°), error #B8332E (33°), info #4B0082 (314°); eight light/dark badge pairs incl. accent teal #007B6B on #E0F8F3 (179°), neutral #F0F0F3, muted #ECEEF0shared-semantics.ts:19-23, status-badge-palette.ts:52-61188 sites; info badge for waitlist / approvalConsistent, but success is ΔE76 7.5 from brand and its tint #EBF8F1 is ΔE76 5.5 from $primarySubtle
calendarSat #1565C0, $error for Sundayshared-semantics.ts:274 calendar sitesUsed (Korean calendar convention)
Third-party kakao #FEE500, naver #03C75Atokens.ts:94-96naver-badge.tsx:19 ($naver); $kakao has no consumer in the census scopePartly used
Chart seriesnone: charts take resolved hexELO ring = primary, success, badgeAccentText (hero-card.tsx:59-61: two greens and a teal); outcome donut = success / error / textSecondary (outcome-visuals.ts:16); heatmap = primary / primarySubtle (activity-heatmap-card.tsx:126-128)No categorical palette

What the frames show (not what the tokens allow): ours-phone/records.png paints the segmented pill, the NTRP chip tint, the ELO numeral, the tri-ring, the progress bar, the "골드까지 200점" text, the three 개인 기록 icons, the heatmap cells and the active tab in one green. Colour cannot tell the owner which of those is a button.

3. Component colour matrix vs peers ​

Legend: BS brand solid · BT brand tint · N neutral (ink or grey) · S semantic (success / warning / error colours) · M multi-colour · – not visible in the frames on disk. Frame ids are crops/<app>_f<N> (Korean), b_crops/<app>_<N> (category), crops_c/m_<app>_<N> (global); published tokens are cited from docs/design/research/visual-benchmark-2026-10-08/lane-a-korean-consumer.md §2 and lane-c-platform-global.md §2. Composite sheets I looked at: bench/lane_d/sheets/*.png.

ComponentTwoMore (code + frame)토스당근카카오뱅크배민
Segmented control / selectionBS pill on BT track: pill-nav.tsx:223,400, track :80; ours-phone/root.png, records.pngN grey track + white pill (toss_f2 "현재가 | 평가금"); tabs ink underlineN ink-filled chip (danggeun_f1 "전체"); ink toggle (_f7)N ink underline tab (kakaobank_f2 "통장"); outlined chipsN ink underline (baemin_f2 "전체")
Active tab (bottom / top)BS icon + label: (tabs)/_layout.tsx:62; records.pngN ink underline (toss_f2, _f3)N ink icon in capsule (danggeun_f1, _f2)N ink icon in grey capsule (kakaobank_f0)N ink underline (baemin_f2–_f5)
Primary CTABS full-width band, 3.4–4.5% of frame: button.tsx:79, BottomCtaBand; sessions_*.png, courts.pngBS blue CTA (lane C frames_c/toss/839333328_4, #4880EE; TDS blue500 #3182F6); none in cropsBS orange FAB "+ 글쓰기" (_f1, _f2); 결제 is ink (_f5)– (이체 is grey)BS violet block + violet icon buttons (baemin_f3, _f4)
Secondary buttonsN secondary fill + 1 px $borderStrong, outline (button.tsx:98-120); no frameN grey #F2F4F6 "송금 / 내역" (toss_f0)N grey "충전 / 송금", white outline "자세히" (danggeun_f5)N grey pill "이체" (kakaobank_f0, _f2)–
LinksBS text: linkified-text.tsx:68, section-header.tsx:162; "제안 내역" courts.pngBS blue "포인트 받기" (toss_f5)N grey "더 많은 매물 보기" (danggeun_f7)BS blue "비즈니스 알림", count "9" (kakaobank_f6, _f2)BS violet / blue (baemin_f4, _f2)
Chips / filtersN outline resting; selected BT fill + BS border + BS 700 text: selection-chip.tsx:122-123; courts.png–N outline; selected ink fill (danggeun_f1, _f2)N white outline pills (kakaobank_f0, _f2)N outline tags (baemin_f2)
Status badgesS palette; RSVP confirmed BS (status-variant-maps.ts:63); "참석" mint in sessions_*.pngS red "66%" pill, N grey "방송중" (toss_f3, _f5)BT orange "바로구매", N grey tags (danggeun_f1, _f2)N outlined "계좌번호" (kakaobank_f3)S red "30%" pills (baemin_f3)
Progress / metersBS fill on BT track (progress-bar.tsx:112); ring BS + green + teal (hero-card.tsx:59-61)––––
Avatars / initialsBT disc + BS initials by default (avatar-bubble.tsx:160); "D나", "DE"M 3D pictogram discs (toss_f5)M pictogram discs, photos (danggeun_f5, _f7)M per-account colour discs (kakaobank_f0, _f2)M brand tiles (baemin_f5)
Section headersN ink title + BS link (section-header.tsx:162; "개인 기록" records.png)N ink; grey meta (toss_f0)N ink bold (danggeun_f5)N ink; grey "전체보기" (kakaobank_f2)N ink (baemin_f2)
Icons: nav vs featurenav N stroke ($iconMuted); feature BS stroke (personal-records-section.tsx:108; records.png)nav N; feature M 3D (toss_f5)nav N; feature M (danggeun_f5, _f7)nav N; feature M (kakaobank_f0)nav N; feature M tiles (baemin_f5)
Hero / identity blockno solid block; BT map placeholder 17–21% of detail frames (directory-venues_*, sessions_*)N white cards on grey (toss_f0)N white cards (danggeun_f5)BT pale blue #E1F3FF 19.8% (kakaobank_f3) + yellow-gradient account card (_f0)BS violet 26.2% (baemin_f3); BT mint banner (_f2)
NumeralsBS ELO / stat figures (hero-card.tsx:101, elo-fact-row.tsx:52; profile.png) + NN ink big; S red gain (toss_f2)N ink bold (danggeun_f5)N ink bold (kakaobank_f2)N ink; S red % (baemin_f3)
DividersN hairline $borderSubtle #DFE7E1 (C* 4, green-grey)none, tone gapN hairline #F3F4F5 (lane A)N hairline (kakaobank_f2)N hairline (lane A)
Component밴드테니스프렌즈PlaytomicMonzo
Segmented control / selectionBS only on the selected date disc (band_3) and the poll check + bar, blue (band_6)–N navy-ink fill: selected date, duration, slot (b_crops/playtomic_4); tabs ink underlineBS blue-teal toggle ON (m_monzo_4 "Face ID")
Active tabN ink filled icon (band_2 bottom bar)BS green icon + label (tennisfriends_3 bottom bar)N ink underline "Book" (playtomic_4)BS blue-teal icon + label in grey capsule (m_monzo_0 bottom)
Primary CTABS green FAB (band_3); blue pill (band_2)BS green FAB "+" (tennisfriends_3)–BS coral card with dark / white pills (m_monzo_0 "Add money")
Secondary buttonsN icon + text "밴드 만들기 | 밴드 가입" (band_2)–N white 1 px outline (playtomic_4 "90 min")N dark translucent pills on the card (m_monzo_0)
LinksBS blue "Today" (band_3)BS green list icons (tennisfriends_5)BS blue "Playtomic Open Match", "+ Available" (playtomic_2, _3)BS green "Last updated: Now" (m_monzo_4)
Chips / filtersBT green "참가신청서", violet "투표 중" (band_3, _6)M lilac "총 3명", lime "Lv 2.0-4.5", grey "복식" in one card (tennisfriends_3)N white 1 px; selected navy (playtomic_4)–
Status badgesS red LIVE and count dots (band_2, _6)N grey "마감"; S red text "차단 관리" (tennisfriends_3, _0)–S green check "24/7", green / red amounts (m_monzo_4, _0)
Progress / metersBS blue vote bar + N grey (band_6)N white bar on lime (tennisfriends_5)–S green savings bar (m_monzo_5)
Avatars / initialsphoto squircles (band_2)photo with cyan ring; brown monogram tile "민호" (tennisfriends_3)photo + lime level chip with dark numeral (playtomic_3)M merchant logos (m_monzo_0)
Section headersN ink; grey "전체보기" (band_2)N ink (tennisfriends_5)N ink (playtomic_4)N ink (m_monzo_0 "Activity")
Icons: nav vs featureN ink stroke (band_2); calendar dots M (band_3)nav N grey; feature BS green; danger S red (tennisfriends_5, _0)N stroke; red heart S (playtomic_2)nav N; feature M logos
Hero / identity block– (photo squircles)BS lime #CCFD01 identity card 12.1% (tennisfriends_5; lane B)photo hero (playtomic_4)BS coral balance card 13.8% (m_monzo_0; lane C)
NumeralsN ink; BS blue vote count (band_6)N ink on lime "36.5 / 100" (tennisfriends_5)N on lime chip "1.2" (playtomic_3)N ink; S green money-in (m_monzo_0)
DividersN hairlineN hairline in white cards on greyN hairlineN hairline / tone

Row summary: where the nine columns spend BS on a control, it is a FAB, a CTA, one hero block or a toggle (당근, 밴드, 테니스프렌즈, Monzo, 배민); where they spend it on selection or tab state it is the minority (테니스프렌즈, Monzo). TwoMore is the only column that fills the selected segment, the tab icon, the chip, the avatar, the empty-state disc, the numeral and the progress bar with it.

Additional peers inspected for the tallies in §4 (not in the table for width): 카카오페이 (crops/kakaopay_f1, _f5: ink tab underline, ink capsule bottom nav, blue numerals, multi-colour pictograms), 네이버 (naver_f2–_f4: ink tabs, green only on the centre-tab ring and a few links, multi-colour pictograms), 크림 (kream_f3: ink tabs and bottom bar), 무신사 (musinsa_f2: all ink plus one red-orange promo block), 스매시 (b_crops/smash_1, _5: ink-bold active tab, neutral outlined filters, orange tier glyph), 문토 (munto_3, _4: ink-filled chip, red "마감임박" pill), Cash App (crops_c/m_cashapp_0, _2: ink-black pills, green ring), Spond (b_crops/spond_0, _1: blue solid "Attending", red-tint "Decline", orange / red / blue status icons, multi-colour group avatars), TeamSnap (crops_c/m_teamsnap_1, _3: blue header block with segmented tabs, green ✓ / red ✗ / grey ? RSVP squares, blue active tab).

4. Falsifiable observations ​

Each names the evidence and what would disprove it.

  1. One hue does every job. Median 96% (min 65, max 100) of our saturated pixels are brand green across 11 non-photo frames, 100% of our solid fills; peers' own brand hue is 50% raw / 61% solid (n = 11) and their top family 52% / 69% (n = 72). 10 of our 11 frames hold exactly one family ≥ 10%; 57 of 72 peers hold ≥ 2. Disproved by a representative set of our content screens, with seeded data, where a second hue exceeds 10% (today only profile.png, via medals, and the photo hero of clubs).
  2. We do not use more colour than peers; we use it on different things. Median saturated area 1.4% vs 5.1%; large solid saturated objects 0–1 vs 2.5 per frame; 11 of 11 non-photo frames sit below the peer median. Disproved by any peer-set re-measure that puts the peer median below 2%.
  3. Brand sits on controls. 203 of 307 brand sites (66%) are chrome; 107 of those are selected / active states. In the 17 peers I inspected (토스, 당근, 카카오뱅크, 카카오페이, 네이버, 배민, 크림, 무신사, 밴드, 테니스프렌즈, Playtomic, 스매시, 문토, Monzo, Cash App, Spond, TeamSnap), 12 render tab, segment and selected-chip state in ink or neutral (토스, 당근, 카카오뱅크, 카카오페이, 네이버, 배민, 크림, 무신사, Playtomic, 스매시, 문토, Cash App), 4 colour the active tab icon/label only (테니스프렌즈, Monzo, Spond, TeamSnap), 1 mixes (밴드: ink tab bar, brand selected-date disc), and 0 fill a selected segment solid brand on a brand-tinted track (TeamSnap's segmented tabs sit inside a solid blue header block). Disproved by finding a shipping app with a brand-tint track + solid-brand pill.
  4. Peers fill with grey where we fill with mint. Pale-green flat fills: 3.5% (홈), 4.2% (기록), 17–21% (detail frames) vs peers median 0.9% tint and 5.2% neutral grey; ours neutral grey on hubs 0.04%. Only 6 of 72 peers exceed 5% tint, each a single bounded hero block. The segmented track ($surfaceTertiary #E5FAEA, C* 11) is the largest repeat offender; our own near-neutral rungs exist (gray100 #F0F5F1 C* 2.7, badgeNeutralBg #F0F0F3 C* 1.5).
  5. Our CTA band alone out-weighs a peer's entire solid-colour budget. Band #006C36 = 3.4% (일정 상세), 3.4% (코트 상세), 3.7% (클럽), 4.5% (테니스장) of the frame; the peer median of all solid saturated fills is 1.7%; the peers that spend more are bounded hero blocks (Monzo 6.5%, 배민 11.5%, Wise 18.9%), not a bottom bar.
  6. Green means three things. #006D37 (brand, 151°) vs #14804A (success, 153°): ΔE76 7.5; tints #E5FAEA vs #EBF8F1: ΔE76 5.5. In ours-phone/sessions_*.png the "참석" chip and the "오늘" chip on root.png read as the same family; the ELO ring uses primary + success + teal (hero-card.tsx:59-61), two greens in one chart. Peers separate roles by hue (Toss blue vs #03B26C green500; Monzo coral vs green money-in; 밴드 green FAB vs red / blue / violet states) or by ink (Cash App black buttons, green only for the ring and money-in).
  7. The content palette is built and unspent. optic has 0 consumers (tokens.ts:47); the ELO tier palette (5 hues, tier.config.ts:67-130) feeds group dots but the tier chip is brand tint (tier-badge-utils.tsx:55); club identity hues exist (flag-medallion-presets.ts:132-139) but the no-logo fallback is mint (club-logo-bubble.tsx:45); court-surface colours only reach venue pills. Peers use content colour in 10 of 17 (토스, 당근, 카카오뱅크, 카카오페이, 네이버, 배민, 밴드 calendar dots, 테니스프렌즈 chips, Monzo merchants, Spond status icons); seven stay near-monochrome (크림, 무신사, Playtomic, 스매시, 문토, Cash App, TeamSnap).
  8. The nearest peer is mono-hued on purpose. 테니스프렌즈 is 95% lime in solid fills, yet it keeps brand colour for one identity card (12.1% of the frame), a FAB and the active tab, and uses grey for everything else including chips, and ball-yellow for level (Lv 2.0-4.5). The same pattern is in Playtomic (lime level circle with a dark numeral, ink selection). This weakens "mono-hue is bad" and supports "brand on one block, ink on controls".

5. Colour budget proposal for TwoMore ​

Direction (from benchmark §3): white, with one block. Six roles, each tied to frames above; it narrows benchmark §3.2–§3.3 (one brand block, optic as the small numeric accent) with the control-state and content-colour findings of this lane. Token names are the ones that exist today unless marked new.

RoleColourWhereEvidence
Brand solid $primary #006D37 + $primaryTextthe screen's one primary action and one hero blockButton primary / BottomCtaBand / web "번개 만들기"; the 홈 day block, 기록 ELO block, 프로필 identity block (benchmark §3.2); toggle ON, checkbox / radio, selected date disc당근 FAB, 밴드 FAB + date disc, 테니스프렌즈 FAB + lime card, Monzo coral card, 배민 한그릇 block
Brand as text $primaryinline links onlyLinkifiedText, HeaderTextAction, inline "더보기"토스 blue links, Playtomic / Spond / Monzo link colour
Ink / neutral $text, $textInverse, $surfaceSecondary, $borderSubtle, $iconMutedeverything that is a control statetab bar active (ink filled icon + ink label); segmented control = neutral track + white pill with hairline; selected chip = ink fill ($text bg, $textInverse label); section "전체 >" actions; stroke icons; person-initial avatars; progress track12 of 17 peers; Toss toss_f2, 당근 danggeun_f1, Playtomic playtomic_4, Munto, Cash App
Semantic $success, $warning, $error, $info, $badge*status only: paid / unpaid, live, closed, RSVP, deltas (+61 / −), validationstatus badges, outcome donut, ELO delta (completed-row.tsx:171), calendar Sunday / Saturday토스 red / green gains, 밴드 LIVE red, TeamSnap RSVP squares, Monzo money-in
Level accent optic #C6E84B (L* 87) + $text inklevel and rating onlyNTRP / ELO-tier chip under avatars and in session rows, "today" dot, a progress fill in the ELO blockPlaytomic #CCFC04 level circle, 테니스프렌즈 #CCFD01, 8 of 13 racket apps (benchmark §1.5)
Content accents (small, multi)content identity, never chromecourt surface ($courtHard, $courtClay, $courtGreen) as a 12–16 px dot/tile on session and venue rows; club identity hue (FlagMedallion presets) on club monogram, calendar dots and club cards; tier hue (TIER_DEFINITIONS) on the tier glyph ring; medal tones on achievements밴드 calendar dots, 테니스프렌즈 chips, 카카오뱅크 account discs, Toss / 당근 pictograms

5.1 What changes per component ​

Component (file)TodayProposedToken
Segmented control (pill-nav.tsx:80,164,223,400)BS pill on mint trackwhite pill (hairline) on neutral track, ink label; selected = weight + inktrack: new $controlTrack = gray100-class (C* ≤ 3) or $badgeNeutralBg; pill $card; label $text
Tab bar (apps/mobile/app/(tabs)/_layout.tsx:62)BS icon + labelink filled icon + ink label; brand only for the unread dottabBarActiveTintColor: themeHex(theme,'text')
Chips (selection-chip.tsx:122-123, chip-label-visual.ts:24)BS border + BT fill + BS textresting outline; selected ink fill + inverse label$text / $textInverse (alias new $chipOn, $chipOnText)
Selection card / dropdown chip (selection-card.tsx:45-46, dropdown-chip.tsx)BS border + BT fill2 px ink border, white fill; keep BS only for the check mark$text
Avatar default (avatar-bubble.tsx:160)tone primarytone neutral (grey disc, $textSecondary initials); club monogram uses identity hue$badgeNeutralBg / $badgeNeutralText
Empty-state disc (empty-state.tsx:78,84)$primarySubtle$badgeNeutralBg disc, $iconMuted strokeexisting
Section header action (section-header.tsx:162,165)BS text + chevron$textSecondary text + chevron (Toss "내 계좌 >", 카카오뱅크 "전체보기")existing
Stat numerals (stat-grid.tsx:38, tone-colors.ts:29, profile 50+ / 64%)BS$text; BS only for the single hero numeral inside the brand block (white on $primary)existing
Feature icons (personal-records-section.tsx:108, head-to-head-screen.tsx:156)BS stroke$iconMuted stroke (nav grammar); category-coloured only in entry tilesexisting
Bookmark saved (save-button.tsx:104,125)BSink filled (크림 SAVED, 당근 heart); not brand$text
Tier chip (tier-badge-utils.tsx:55)$primarySubtle fillwhite / neutral chip with a 6–8 px tier-hue dot or ring from TIER_DEFINITIONSnew theme slots tierBronze…tierRed aliasing the domain hexes
Level chip (new use of optic)none22 px circle / pill, optic fill, $text bold numeralnew $levelChipBg = optic, $levelChipText = $text
Progress (progress-bar.tsx:112)BS on BT trackBS only for the ELO goal inside the hero block; elsewhere status hue (capacity / recruitment tone via fillColor) on $borderSubtle trackexisting
Chart seriesnonefive-hue categorical set built from existing huesnew $series1…5 → primary, courtHard, courtClay, accentWarm, accentCool
Success vs brandΔE76 7.5with brand off controls, green on a chip means "paid / confirmed / win" and on a button means "do this"; re-measure ΔE after the move, retire primarySubtle badges—

Scope of the edit: nine primitive files carry the call-site multipliers of §2.2 (pill-nav.tsx, selection-chip.tsx, chip-label-visual.ts, avatar-bubble.tsx, empty-state.tsx, section-header.tsx, (tabs)/_layout.tsx, tier-badge-utils.tsx, progress-bar.tsx) plus tone defaults in stat-grid.tsx / tone-colors.ts. Roughly 150–210 of the 307 brand sites retarget; I have not built or rendered any of this, so the number is an estimate from the §2.1 classification.

5.2 Targets a re-measure should hit (same script, same frames) ​

  • Brand hue share of saturated pixels on hub frames ≤ 70% (peers' brand-hue median 50% raw / 61% solid; 테니스프렌즈 / Wise / Cash App at 88–98% are the upper bound).
  • ≥ 2 hue families ≥ 10% on 기록, 프로필 and the club hub (peer median 2).
  • Hub saturated area 3–8% (peer median 5.1%; the one block carries it), list frames ≤ 3%.
  • Pale green-tint fills ≤ 1% on lists (peer median 0.9%), neutral-grey fills 3–6% (peer median 5.2%).
  • Largest solid saturated object on a detail page is the CTA, ≤ 2.5% of the frame (peers' whole solid budget median 1.7%).

6. Caveats and side findings ​

  • Frames are vendor-curated store art and our web rung (headless Chromium). Hue values carry ±2–3 levels of JPEG noise; family splits near a boundary (Wise #9FE870 at 130°, Duolingo green at 130°) can flip. Raw peer numbers include photos and 3D pictograms, which is why the solid view is shown beside it.
  • The brand-hue test uses 11 peers whose brand hue I could name and see; the 17-app ink / neutral tally is a visual read of the frames on disk, not an automated measure, and "–" cells mean "not visible", not "absent".
  • Possible bug, not touched: packages/features/clubs/src/dues/dues-attest-modal.tsx:162-169 renders $primaryText (white in light, #0D100E in dark) on a $primarySubtle background (#E5FAEA light, #0A1F14 dark): white on mint in light mode, about 1.1:1.
  • Mislabelled --hues families in the first version of sample.py: before the recalibration it put Toss blue under "purple", Toss red under "orange" and optic lime under "green". Any lane that ran --hues before the recalibration message needs a re-run.
  • ours-dark/* was measured but not used; ours-1440/matches.error.txt sits next to the matches frame (not investigated; the frame was measured as delivered).

7. Files ​

  • This report: bench/lane-d-colour-usage.md
  • Scripts: bench/lane_d_hues.py (families, raw + solid), bench/lane_d_glob_m.py, bench/lane_d_blobs.py, bench/lane_d_tint.py, bench/lane_d/census_classify.py, bench/rerun_all.sh
  • Data: bench/lane_d/{peers_app_summary,glob_m_app_summary,ours_frames,blobs,tint,census_classified}.json, peers_per_frame.tsv, glob_m_per_frame.tsv, census_primary_lines.txt, census_semantic_lines.txt, peer_table.txt, peer_stats.txt, brand_share.txt, blob_stats.txt, tint_stats.txt, ours_table.txt
  • Sheets I read: bench/lane_d/sheets/*.png

Markdown remains the source of truth. Run yarn docs:check before handoff.