Skip to content

Colour system research — every proposed change tested against colour science and UX research (2026-10-09) ​

Status: Active · Date: 2026-10-09 · Purpose: the owner approved the direction of visual-benchmark-2026-10-08.md (§3 "white, with one block", §5 colour budget) and the board /wireframes/visual-tone T0–T7, and asked that every proposed change be grounded in "clean and effective use of colours based on science and UI/UX research and practices". This note is the verdict ledger: each proposal → what the evidence says → KEEP / ADJUST (to what) / DROP → the final value that goes into tokens, the board and the gate. Three evidence lanes with every URL: E — perceptual science of surfaces, blocks and ink, F — UX research and platform practice for controls, chips, rows and icons, G — palette engineering, colour-vision deficiency, categorical sets, dark parity. Every number below was computed with one shared helper (CIELAB/OKLCH, ΔE2000, WCAG 2.x, APCA-W3 0.0.98G-4g with the published fontMatrixG size table, Machado 2009 CVD simulation at severity 1.0); the formulas are reproduced in lane G §3 and lane E §2.

1. What changed because of the research ​

The direction stands. Eleven values changed — (1) control track #E5EBE7 → $borderSubtle #DFE7E1, (2) hairline #DFE7E1 → $border #CAD4CC, (3) meta 13/400 → 13/500 $textSecondary, (4) block labels 12–13px → ≥ 14/700, (5) success #14804A → #00796A / #014B40, (6) warning #946200 → #6B4A00 text + #F5B301 fill, (7) dark error #D4736E → #FA9B94, (8) court trio recoloured, (9) tier bronze/gold/grandmaster recoloured and $series1–5 added, (10) dark ink #EFF3F0 → #DFE4DC, (11) icon stroke 1.8 → 2.0 — plus four rule changes (badges stay rounded rectangles, inline links underlined, a non-colour second cue on every control state, photos only when venue-specific); two claims were dropped as folklore (60-30-10, the muted-ink share), the proportion targets became descriptive, and five decisions go to the owner.

#Proposal (board)VerdictFinalWhy (one line; evidence in the lanes)
1Hub basement #F0F7F3 under white cardsKEEP#F0F7F3 (ΔL* 3.4, ΔE 4.7 = 2.0× the 2.3 JND)Today's #F8FBF9 step is ΔE 2.2 = 0.96× JND — literally at threshold, which is why cards read as one sheet. The green tint shifts the perceived white point by ≤ ΔE 1.4 (CAT16/von Kries, D 0.85–0.9) and the brand green by ΔE 0.55–0.76; chromatic induction is toward magenta, not grey.
2Gate: basement vs card ΔL* 3.0–5.5KEEP, label the ceilingΔL* ≥ 3.0 (≈3× Barten edge threshold at 20,000 lux shade); ≤ 5.5 is aesthetic; add C*ab ≤ 4.5 for L* ≥ 883.0 keeps the edge above ~2.8× threshold in bright shade; the ceiling is the owner's taste (Cash App 5.2 is the top of the peers), not perception; the chroma cap is Material's neutral-surface level and keeps the white-point shift ≤ 1.4.
3Fills on card ≥ 1.2:1; control track #E5EBE7ADJUSTfills ≥ 1.2:1 on a card and ≥ 1.15:1 on the basement; control track = existing $borderSubtle #DFE7E1 (1.26 / 1.16); $surfaceStrong #E5EBE7 retired (1.21 / 1.11 fails on the basement)One token fewer and it passes on both grounds.
4Hairline #DFE7E1 → #CDD6CFADJUSThairline on white 1.4–1.8:1; use existing $border #CAD4CC (1.52) and retire the 1.26:1 hairline rolePeers sit at 1.6–1.7:1; a 1px #DFE7E1 line is 16.5× Barten threshold on a DPR-3 phone but only ~5× at DPR 1 under glare; one existing token covers it.
5List canvas pure white, 1px hairlinesKEEP#FFFFFFNo literature favours tone-only over hairline separation; Material's outlined card is exactly this (surface + outlineVariant 1.6–1.7:1). Hairlines help scanning dense money lists by convention (GOV.UK, Carbon), not by any study (Enders 2008 zebra stripes: null on accuracy).
6One solid brand block per hub, 10–18% of the screenKEEP, ADJUST the rulesexactly one filled brand object > 2.5% of the frame per screen; it holds the screen's answer as 28–44/700 numerals; no promo styling; the 10–18% band stays descriptive (peer medians), not a ruleThe mechanism is salience: a feature pops out in proportion to its difference from a homogeneous field (Wolfe & Horowitz 2017; Duncan & Humphreys 1989) — which is also why the tab bar and segmented control must not be brand. The evidence-backed risk is banner blindness: NN/g eye-tracking found a coloured rectangle on a white page "totally ignored" and a 25%-of-content right rail drew 0.8% of fixations; the block must look like the answer, not an ad. Fatigue/"vibration" from large saturated areas is unsupported (Helmholtz–Kohlrausch brightening of #006D37 is +1.3 L*).
7Labels on the block 12–13px whiteADJUSTwhite labels ≥ 14px/700 or 15px/600; secondary labels white at alpha ≥ 0.8 (#CCE2D7, 70% is 4.01:1 and fails AA); lime #C6E84B on the block only as fills or numerals ≥ 28px/700 (Lc 63)White on #006D37 is Lc 86.6 / 6.48:1 — fine for numerals, below the APCA size floor for 12–13px labels.
8Ink hierarchy: promoted 18/700, body 14/500, meta 13/400, tertiary #626C64ADJUSTpromoted 18/700 $text, title 16/600, body 14/500 $text, meta 13/500 $textSecondary #4F5851 (not 13/400); $textTertiary only at ≥ 16px/500 or for non-essential strings; secondary stays #4F5851 (7.38:1 white, 6.78 basement)13/400 is the weakest cell in the system: measured Pretendard Hangul horizontal stem 0.85 device px at 1× (renders grey), x-height 0.173° at 36 cm (below Legge & Bigelow's 0.2° fluent-reading floor), and APCA needs Lc 113 (unreachable). Tertiary #626C64 is Lc 77 / 71 and fails every APCA row at 13–14px while passing WCAG AA by 0.5–1.0. Size has the strongest record for drawing the first glance; no head-to-head study of weight vs size exists.
9"Muted ink 49% → 30%"DROP as a ruleper-role contrast floors instead: text ≥ 4.5:1 on its real ground; text ≤ 14px ≥ 6.5:1 (#4F5851 passes, tertiary excluded)No study sets a muted-ink share; what exists is directional (NN/g low-contrast, WebAIM Million 83.9% of pages with low-contrast text).
10Controls in ink/neutral; brand for one action + one blockKEEP, with a second non-colour cue per statesegmented: #DFE7E1 track, white pill + 1px hairline, selected label 700 ink vs 500 secondary (the pill edge is only 1.21:1, so selection rides on the label); active tab: filled glyph + ink + 600 label; selected chip: ink fill + inverse label + 700; saved bookmark: outline → filled; section "전체 ›" in $textSecondary with the chevron (NN/g: a link styled as static text costs +22% time / +25% fixations — the chevron is the signifier)Seed's published segmented control and chips are exactly this pattern; Carbon too; Apple HIG: "prefer a monochromatic tab bar when content is colourful" and "prefer filled symbols" for selection; Apple toggles: never colour alone. No study shows ink is faster than brand — the case is the salience budget and peer practice. Our current brand chip does not fail WCAG 1.4.1 (weight + border), so this is a budget move, not a conformance fix. Focus ring stays 2px $primary with offset (2.4.13 is AAA, kept anyway).
11Links stay brand textADJUSTinline links brand + underline; stand-alone link rows no underlineBrand #006D37 vs ink is 2.95:1 (< the 3:1 of WCAG technique G183), vs $textSecondary 1.14:1; NN/g: underline is essential when link colour is red or green.
12Badges as full pills, one saturated tierDROP / ADJUSTbadges stay rounded rectangles ($2 = 8px) with two tiers: solid on photos, weak tint in repeated rows; chips stay full pills; facts inline as glyph + text (Seed Tag Group)The earlier "full pills everywhere" cited Seed wrongly: Seed's badge is 4–6px, only its chip is radius.full; our badge.tsx already states the passive-label distinction; NN/g: non-clickable items that look like buttons is a named failure.
13Photo-led session rows (76–95pt)KEEP only when the photo is venue-specificphoto only when it is the venue's own; otherwise the map tile; one box (size, radius, aspect) for both, map tiles with a 1px inset $border; the time stays the promoted text factNN/g eye-tracking: informative images get looked at, decorative ones are ignored; look-alike Amazon thumbnails drew 0.9 fixations vs 4.4 on text — generic court photos would add load. No public number supports 76–95pt specifically.
14Stroke glyphs 16px, stroke 1.8, always $textADJUSTstroke 2.0 (1.33px at 16px — Pretendard 500 stems measure 1.46px at 14px; 1.8 renders 1.20px and matches 400 only); glyph colour = its label's colour (Seed tag-group rule)Emoji vary by platform (Unicode UTS #51; Miller et al. 2016: one in four sentiment judgments diverge); Apple/Fluent: one colour per icon, outline symbols beside text, filled for selection.
15Optic #C6E84B level chips, 22px, ink numeralKEEP$levelChipBg optic, $levelChipText #0D100E (Lc 84.1, 13.7:1); chips on white get a 1px hairline edge (optic is Lc 19 on white, 13 on the basement — below APCA's Lc 15 "invisible" line); keep lime away from amber (ΔE 5.8 under deutan); 24px target if tappableIts pull is luminance (Y 0.70), not Helmholtz–Kohlrausch (Γ 0.93, among the lowest of the roles); the numeral is the information and lime is the category convention (8 of 13 racket apps).
16Success #14804AADJUST$success #00796A (fill / icon / large figure; dark #75D1C6), $successText #014B40 (small text, badge text)ΔE2000 vs brand today 6.7 under every vision type (one colour); the ΔE ≥ 20 target is unreachable inside the text window (min 11.3 at hue 170, 14.1 at 180, 20 only at blue-teal 195), so the rule is Okabe–Ito parity: ≥ 12 under protan/deutan, ≥ 20 normal, plus a glyph and a label. Final: vs brand 13.4 / 14.7 / 17.2, vs error 55.5 / 16.2 / 24.2; on-white Lc 75.9. Polaris (#047B5D / #014B40), Seed positive (#079171) and Wise do the same hue shift for a green brand.
17Warning #946200 as text and fillADJUST$warningText #6B4A00; $warningFill #F5B301 with ink text (L* 77.1: 3.19:1 vs error, ΔE 46 / 40 / 29.5); dark #E1B75C text, #E2B13C fillThe whole semantic set sits at L* 40–47, so under deutan error vs warning is ΔE 4.2 and today's deadline and live banners are the same colour; a 20-L* gap only yields 2.0–2.2:1 luminance, so the fill has to be bright with ink text — Polaris #FFB800, Atlassian #FBC828, Carbon #F1C21B, Wise #FFD11A all do this.
18Error #B8332E, info #4B0082KEEP (light); dark error ADJUSTlight unchanged; dark error #FA9B94 (today #D4736E is Lc 40.5 on the dark card)Error hue matches every peer (OKLCH h 27 vs 24.5–28.8); brand vs error under protan stays 6.5 for every red tested — carried by glyph + label, documented as irreducible.
19Court trio #247BA0 / #B84A2E / #006633ADJUSTalias the chart series: hard #089AC3, clay #8D3D00, grass #83971F (dark #67D3FC / #DA875A / #BBD068)Grass equals brand (ΔE 2.3), clay equals error (6.6; 2.9 deutan), clay vs grass under protan 1.7 — Okabe–Ito: avoid yellow-green vs green and red vs green pairs. Final trio min ΔE 42.6 / 27.4 / 20.0 / 22.2.
20Tier huesADJUSTbronze #B2580B, gold #D9A00F, grandmaster #9A0F3A (silver, master kept; dark bronze #DA8A11, grandmaster #D8516A); tier dot ≥ 8px, tier name always printedGold #D4A017 is 2.38:1 on white; bronze vs grandmaster under deutan 3.4. Final set min ΔE 25.1 / 22.7 / 18.1 / 11.4 (was 14.8 / 10.0 / 3.4 / 8.7). A scan of every gold that reaches 3:1 on white and basement (best #C87C00, 3.31:1) gets only 10.2 ΔE from bronze under CVD, so no gold passes both; because the tier name is always printed (the dot is redundant, WCAG 1.4.11 does not apply to a decorative mark), the tier rule is CVD separation first, and #D9A00F (2.34:1 on white) is the documented contrast exception.
21Chart series (none today)ADD$series1–5 light #3566B8 #8D3D00 #83971F #883170 #089AC3, dark #6C9EF1 #DA875A #BBD068 #D57CB8 #67D3FCStrictly equal-lightness sets fail CVD (best min ΔE 5.2 under tritan); staggered lightness gives min 22.6 / 15.1 / 14.9 / 8.8 and ≥ 3:1 on white and basement. ≤ 7 hue families per viewport (Healey 1996: up to seven isoluminant colours identified rapidly).
22Dark mode: same budgetADJUSTcard vs canvas ΔL 6–12* (#090C0A/#1A201B = 8.4, inside Material's 3.9–15.8 dark steps); primary text capped at L ≈ 90–92* (#DFE4DC) instead of #EFF3F0 (95.5); block #015228 with white ink (existing colorsDark.primary.dark; white Lc 95.5) — device-test on OLED, the luminance singleton is weak in dark (1.8:1 vs the card)Positive polarity reads better at small sizes (Piepenbrock 2013; Dobres 2017), the dark ladder collapses outdoors (step 288% at 0 lux → 1.5% at 20,000 lux), halation for Hangul has no controlled study. Dark success vs dark primary today ΔE 5.5 → #75D1C6 12.0 / 13.9 / 16.3.
23Neutrals green-tintedKEEPcanvas C* 1.5, basement 3.2 (Radix sage 0.9–1.8, Wise tinted surface 2.8); cap C ≤ 4.5 for L ≥ 88**Tinted neutrals paired with the accent hue are the Radix/Material pattern; the brand's appearance shift on the basement is ΔE 0.55–0.76.
24"60-30-10", "10–18% / 3–8% / ≤ 3% saturated", "≥ 2 hue families", "brand share ≤ 70%"DROP as ruleskeep as descriptive peer medians in the benchmark; the normative rules are rows 6, 9, 10, 2560-30-10 has no traceable origin (interior-decorating folklore); Itten and Albers are art theory; the proportion targets are peer medians. Material 3 Expressive's research measured attention speed and preference for "colour, shape, size, motion, containment" and reports a strong minority preferring calmer designs; it is not evidence for a proportion.
25How many colours is cleanADD≤ 7 hue families per viewport, role-exclusive contexts (brand never a status; status never a category; optic never a surface); every colour code carries a glyph or label (WCAG 1.4.1)Healey 1996; ISO 9241-125's colour-count clause is paywalled and not cited.

2. The palette, final (light / dark) ​

RoleTokenLightDarkAllowed onKey numbers
Brand solid$primary#006D37#70D5A2 (block #015228)one primary action, one hero block, toggle on, selected datewhite on light Lc 86.6 (6.48:1); ink on dark primary Lc 70
Brand text$primary#006D37#70D5A2inline links (underlined) ≥ 15.8px/500on white Lc 81.5, basement 75.7
Ink$text#0D100E#DFE4DC (was #EFF3F0)body 14/500, titles, numerals, controlsLc 105 / 100
Secondary$textSecondary#4F5851unchangedmeta 13/500 and up7.38:1 / 6.78:1, Lc 85.6 / 79.8
Tertiary$textTertiary#626C64unchanged≥ 16px/500 or non-essential onlyLc 77 / 71
Canvas (lists)$background#FFFFFF#090C0Alists, feeds, details—
Basement (hubs)$basement (new)#F0F7F3#090C0A… card #1A201B홈, 기록, 프로필, club hubΔL* 3.4 light; 8.4 dark
Control track / fill$borderSubtle#DFE7E1#374039segmented track, empty-state disc, progress track, neutral chips1.26:1 card / 1.16:1 basement
Hairline$border#CAD4CCunchangeddividers, map-tile inset1.52:1
Success$success / $successText#00796A / #014B40#75D1C6 / —paid, confirmed, win: fill+icon / small textvs brand 13.4 / 14.7 / 17.2 (n/p/d)
Warning$warningText / $warningFill#6B4A00 / #F5B301 (ink text)#E1B75C / #E2B13Cdeadline, unpaid, cautionfill vs error 3.19:1 luminance
Error$error#B8332E#FA9B94cancelled, overdue, live-stopbrand vs error protan 6.5 (irreducible; glyph + label)
Info$info#4B0082#C7B5F2informational onlyon white Lc 97
Level accent$levelChipBg / $levelChipText#C6E84B / #0D100EsameNTRP/tier chip, today dot, block progress fillink on optic Lc 84; hairline edge on white
Court$courtHard / Clay / Grass#089AC3 / #8D3D00 / #83971F#67D3FC / #DA875A / #BBD06812–16px court dot on session and venue rowstrio min ΔE 20 under deutan
Tiertier slotsbronze #B2580B, silver #8E8E93, gold #D9A00F, master #9B59B6, grandmaster #9A0F3Abronze #DA8A11, grandmaster #D8516Atier dot/ring beside the printed tier name (never alone)min ΔE 18.1 deutan; all ≥ 3:1 except gold 2.34:1 (documented; name printed)
Series$series1–5#3566B8 #8D3D00 #83971F #883170 #089AC3#6C9EF1 #DA875A #BBD068 #D57CB8 #67D3FCcharts, calendar dotsmin ΔE 14.9 deutan; ≥ 3:1

Club identity hue stays a continuous picker; identity is never colour-only (monogram or photo always present).

3. The gate (check:color-contrast, extended) ​

Asserted on the theme files, failing on today's values where noted (canaries):

  1. Hub basement vs card: 3.0 ≤ ΔL* ≤ 5.5 and C*ab ≤ 4.5 — today fails (no basement; 1.7).
  2. Every fill-tier colour: ≥ 1.2:1 on the card and ≥ 1.15:1 on the basement — #E5EBE7 would fail (1.11).
  3. List canvas = #FFFFFF; hairline on white 1.4–1.8:1.
  4. Text: ≥ 4.5:1 on its real ground; text at ≤ 14px additionally needs weight ≥ 500 (or ≥ 6.5:1 at weight 400) — this is what APCA's size table actually asks of small text, and it is why meta is 13/500, block labels ≥ 14/700, badge text 600; APCA Lc reported, not gated (APCA is not yet in any standard — WCAG 3 draft 2026-04: algorithm "yet to be determined").
  5. Block: white on $primary ≥ 4.5:1 (6.48) and any alpha label ≥ 0.8; lime on the block ≥ 3:1 (4.64).
  6. CVD separation (Machado 2009 port with lane G's test vectors) at the FILL/ICON level — brand, $success, $warningFill, error, info, optic — pairwise ΔE2000 ≥ 11 under protan and deutan and ≥ 20 normal (final set: every pair ≥ 13.4 / 14.7 / 17.2; brand vs error 61.0 / 6.5 / 11.3, the protan 6.5 documented as irreducible for any red and carried by glyph + label) — today fails (success vs brand 6.7). Text-level colours ($successText, $warningText) are not colour codes on their own: they always sit on their fill or beside a label, and are held to ≥ 10 under deutan (warning text vs error 10.3).
  7. Court trio, tier set, series set: pairwise ΔE2000 ≥ 11 under deutan and tritan; court and series colours ≥ 3:1 on white and on the basement; tier dots are decorative beside the printed name, so gold #D9A00F (2.34:1) is the one listed exception — today fails (bronze vs grandmaster 3.4 deutan; clay vs grass 1.7 protan).
  8. Dark: card vs canvas 6 ≤ ΔL* ≤ 12; every status text on the dark card ≥ 4.5:1 — today fails (dark error Lc 40.5).
  9. Hue-family census of the theme: ≤ 7 families among the roles allowed on one viewport.

The six rules that replaced the proportion targets: (1) exactly one filled brand object > 2.5% of the frame per screen; (2) brand solid otherwise only on the primary action and toggles; (3) every control state has a non-colour second cue (weight, fill, glyph, chevron); (4) inline links underlined; (5) every colour code has a glyph or label (WCAG 1.4.1); (6) ≤ 7 hue families per viewport (Healey 1996).

4. Decisions for the owner ​

  • D1 Success turns teal. #00796A is the hue of the shipped "모집 중 / open" accent badge, so open and paid would share a colour. Recommendation: success = teal (paid, confirmed, win); 모집 중 moves to the brand-weak tint (mint fill, brand text) because it is an invitation tied to the primary action, not an outcome. Alternative: keep success green and make brand the only green by expressing success as a check glyph on a neutral chip — weaker for 총무 scanning of paid rows.
  • D2 Computed hexes need eyes. #6B4A00, #8D3D00, #83971F, #014B40, #9A0F3A are computed, not judged; the board renders them (round 4) and a device look is owed before tokens ship.
  • D3 Court colours change meaning. Clay becomes brown-orange and grass olive so that neither equals error red nor brand green; hard becomes cyan. The court dot is a category code, not a photo of the surface.
  • D4 Dark block #015228 is chroma-carried (1.8:1 vs the card); if it does not read on an OLED device, the dark hub drops the block and keeps the numeral.
  • D5 Tertiary text is restricted to ≥ 16px or non-essential strings; the many 13–14px tertiary sites (timestamps, counts) move to secondary — a sweep, not a token change.

5. What is folklore, overstated or unmeasured (do not cite) ​

60-30-10 · the 10–18% / 3–8% / ≤ 3% saturated-area and ≥ 2-hue-family targets (peer medians) · "ink controls are faster" (no study) · "one brand block helps" (salience supports one unique item; von Restorff is memory, and MeasuringU found no preference effect) · "neutral controls lose affordance" (NN/g's penalty is for ghost buttons and links styled as text, not filled tracks and pills) · "our chip fails WCAG 1.4.1" (it does not) · "full pills are better / Seed does it" (it does not) · "photo thumbnails speed scanning" for look-alike lists · "hairlines help money lists" (convention) · "Helmholtz–Kohlrausch makes lime pop" (Γ 0.93) · "large saturated areas fatigue" (no isolating study) · Material 3 Expressive as proportion evidence · any Hangul mobile minimum-size study (none exists; the Korean accessibility guidance text could not be retrieved).

6. Caveats ​

Frames and tokens were read from published sources; m3.material.io, Apple HIG swatches, Fluent 2 and Polaris pages are script-rendered, so Material tones were computed from material-color-utilities and Apple separator values come from a third-party table. APCA is used as a ranking gauge beside WCAG 2.x, never as the sole pass/fail. The Barten edge-visibility model in lane E is an order-of-magnitude estimate with parameters from memory. Nothing was viewed on a device; D2 and D4 are device items.

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