Contrast headroom of the candidate palette space #50

Closed
opened 2026-07-28 08:10:36 +00:00 by archvillainette · 1 comment
Owner

Part of #47

Question

Which candidate palette combinations can actually hit WCAG AA, and where is the headroom?

AA is non-negotiable on this map, and it is the constraint that kills palettes late if it is not checked early. Establish the numbers before any prototype is built.

Inputs — current tokens in DESIGN.md frontmatter and scss/westgate/_tokens.scss, plus the collected references in ../_media/theme-color-palettes/. Candidate space:

  • Base darks: Plum Noir #351E28 (confirmed to supersede Bruised Plum), plus near-blacks #141214, #1B1211, #110E0D, #0F0D12 (current), #09080B (current).
  • Oxblood and wine mids: #5B1E32, #3E1617, #340C0C, #22040D, #402533, #311B1D.
  • Gold: current Tarnished Gold #C2A35A, Old Gilt #A8893F, Gold Lit #E0C878, and the warmer Antique Gold #C28D39.
  • Second-metal candidates, purple excluded: neutral pewter around #7F8995 / #636D79 / #9EA7B3, and warm iron/brass alternatives.
  • Warm light tones as an actual surface, which the theme has never had: #EFE5D2 champagne, #B59785 butcher paper.
  • Text ramp: #E6E0D6, #B9B2A6, #9A9086, #D8C28A.

Report, as a table of measured ratios:

  • Which text tokens clear 4.5:1 on which base darks, and which clear 3:1 for large text only.
  • Whether gold text on the base darks passes, at which of the three golds — this determines whether gold can be a link colour or only a border.
  • Focus ring headroom: what clears 3:1 against every surface it can land on.
  • If a light surface enters, which dark text tokens work on it, and whether that forces a new token.
  • Which candidate combinations are simply impossible at AA, so no prototype wastes time on them.

Note where the current theme is already close to the floor — placeholders are documented as sitting at 74% alpha precisely because that is the 4.5:1 edge, which means there is no slack there.

Part of #47 ## Question Which candidate palette combinations can actually hit WCAG AA, and where is the headroom? AA is non-negotiable on this map, and it is the constraint that kills palettes late if it is not checked early. Establish the numbers before any prototype is built. Inputs — current tokens in `DESIGN.md` frontmatter and `scss/westgate/_tokens.scss`, plus the collected references in `../_media/theme-color-palettes/`. Candidate space: - Base darks: Plum Noir `#351E28` (confirmed to supersede Bruised Plum), plus near-blacks `#141214`, `#1B1211`, `#110E0D`, `#0F0D12` (current), `#09080B` (current). - Oxblood and wine mids: `#5B1E32`, `#3E1617`, `#340C0C`, `#22040D`, `#402533`, `#311B1D`. - Gold: current Tarnished Gold `#C2A35A`, Old Gilt `#A8893F`, Gold Lit `#E0C878`, and the warmer Antique Gold `#C28D39`. - Second-metal candidates, purple excluded: neutral pewter around `#7F8995` / `#636D79` / `#9EA7B3`, and warm iron/brass alternatives. - Warm light tones as an actual surface, which the theme has never had: `#EFE5D2` champagne, `#B59785` butcher paper. - Text ramp: `#E6E0D6`, `#B9B2A6`, `#9A9086`, `#D8C28A`. Report, as a table of measured ratios: - Which text tokens clear 4.5:1 on which base darks, and which clear 3:1 for large text only. - Whether gold text on the base darks passes, at which of the three golds — this determines whether gold can be a link colour or only a border. - Focus ring headroom: what clears 3:1 against every surface it can land on. - If a light surface enters, which dark text tokens work on it, and whether that forces a new token. - Which candidate combinations are simply impossible at AA, so no prototype wastes time on them. Note where the current theme is already close to the floor — placeholders are documented as sitting at 74% alpha precisely because that is the 4.5:1 edge, which means there is no slack there.
archvillainette added the wayfinder:research label 2026-07-28 08:10:36 +00:00
archvillainette added a new dependency 2026-07-28 08:10:53 +00:00
Author
Owner

Resolved

Full findings: docs/research/palette-contrast-headroom.md on branch research/palette-contrast-headroom (not pushed, not PR'd — commit 91cfd1a).

Every number below is computed WCAG 2.x relative luminance, (L1+0.05)/(L2+0.05), with alpha colours flattened over their real backdrop first (source-over, sRGB). The script self-checked against WCAG's own published values (#000/#fff = 21.0, #767676 on white = 4.54, #949494 on white = 3.03, #959595 < 3.0) before producing any palette number.

Headline

  1. Every base dark works. All of them, including the lightest (oxblood #5B1E32), clear 4.5:1 with Bone, Ash and Ledger Ink. Dark surfaces are not the constraint.
  2. Plum Noir #351E28 is safe and it is the ceiling. L=0.0184. The Dust-Text body-copy ceiling is L=0.0245. There is almost no room above Plum Noir.
  3. Gold can be a link colour — at Tarnished Gold, Gold Lit and Antique Gold. Not at Old Gilt.
  4. A light surface is a second palette, not a token. The whole text ramp fails on both (best 2.51:1).
  5. Gold and butcher paper #B59785 cannot coexist at all — all four golds sit at 1.08–1.22:1. Hardest kill in the space.
  6. The DESIGN.md 74% placeholder claim is wrong — the 4.5:1 edge is 66%, and 74% measures 5.38:1. There is slack there, not zero.

1. Text ramp on the base darks — no failures

fg \ bg Plum Noir #351E28 Bruised Plum #2A1222 #141214 #1B1211 #110E0D Crypt #0F0D12 Grave #09080B panel #18141D panel-2 #110F15 panel-3 #201924
Bone #E6E0D6 11.70 13.24 14.20 14.02 14.64 14.72 15.22 13.83 14.50 13.05
Ash #B9B2A6 7.30 8.26 8.86 8.75 9.14 9.19 9.50 8.63 9.05 8.14
Dust #9A9086 4.90 5.55 5.96 5.88 6.14 6.17 6.38 5.80 6.08 5.47
Ledger Ink #D8C28A 8.78 9.94 10.66 10.52 10.99 11.05 11.42 10.38 10.88 9.79
Ink Muted #A99D8F 5.78 6.54 7.02 6.93 7.24 7.28 7.52 6.84 7.17 6.45

All ≥4.5:1. Plum Noir's weakest pair (Dust, 4.90) has +0.40 of slack. Plum Noir superseding Bruised Plum costs 0.6–1.5 points across the ramp and breaks nothing.

2. On the oxblood/wine/plum mids — Dust breaks

fg \ bg oxblood #5B1E32 #3E1617 #340C0C #22040D plum-mid #402533 #311B1D plum-bloom #3A1830
Bone 9.54 12.01 13.29 14.66 10.45 12.26 11.82
Ash 5.95 7.49 8.29 9.15 6.52 7.65 7.38
Dust 4.00 large only 5.04 5.57 6.15 4.38 large only 5.14 4.96
Ledger Ink 7.16 9.01 9.97 11.00 7.84 9.20 8.87
Ink Muted 4.72 5.94 6.57 7.25 5.17 6.06 5.84

Rule that falls out: oxblood and plum-mid are accent surfaces, not body-copy surfaces. Put a timestamp on them and you are out of AA.

3. Gold as body/link text — the three-gold verdict

Gold Plum Noir Bruised Plum Crypt Grave panel panel-3 oxblood plum-mid
Tarnished #C2A35A 6.35 7.19 7.99 8.26 7.50 7.08 5.18 5.67
Old Gilt #A8893F 4.62 5.23 5.81 6.01 5.46 5.15 3.77 large only 4.13 large only
Gold Lit #E0C878 9.29 10.51 11.69 12.08 10.98 10.36 7.58 8.30
Antique #C28D39 5.24 5.93 6.59 6.81 6.19 5.84 4.27 large only 4.68
  • Tarnished Gold — yes, a link colour. Worst case 5.18 on oxblood. Comfortable. Keep it.
  • Gold Lit — AAA everywhere. Safe as hover; the only gold that would survive a lighter surface.
  • Antique Gold — admissible but tighter. Costs ~1.1 points of ratio vs #C2A35A everywhere; +0.74 slack on Plum Noir; fails on oxblood (4.27). Not a free swap for the warmth.
  • Old Gilt — border and pressed-state only, never a link or body copy. 4.62 on Plum Noir is +0.12, a rounding error, not a margin.

4. Second metal, neutral pewter only (no lavender/purple-cast)

Pewter Plum Noir Crypt Grave panel-3 oxblood plum-mid Verdict
#9EA7B3 6.31 7.94 8.21 7.04 5.15 5.64 body text anywhere on the darks — this is the one
#7F8995 4.32 large 5.44 5.63 4.82 3.53 large 3.86 large small text on the near-blacks only
#636D79 2.92 FAIL 3.68 3.80 2.38 FAIL 2.38 FAIL 2.61 FAIL fails even 3:1 on Plum Noir — cut it

Catch: the pewters are luminance twins of the golds. #9EA7B3 L=0.3816 vs Tarnished Gold L=0.3840 — 1.006:1 between them. A two-metal hierarchy has to work by hue and placement; contrast will not help it.

5. Focus ring, SC 1.4.11 (3:1)

Current ring is outline: 2px solid #c2a35a with outline-offset: 2px (_controls.scss:672). The offset means the ring's adjacent colour is the surface behind the control, not its fill.

Ring worst dark surface ratio champagne #EFE5D2 butcher #B59785 Verdict
Tarnished #C2A35A oxblood 5.18 1.94 1.12 OK on all darks; fails on light
Gold Lit #E0C878 oxblood 7.58 1.32 1.64 OK on darks; fails on light
Antique #C28D39 oxblood 4.27 2.35 1.08 OK on darks; fails on light
Old Gilt #A8893F oxblood 3.77 2.66 1.22 thin on darks; fails on light
pewter #9EA7B3 oxblood 5.15 1.95 1.12 OK on darks; fails on light
pewter #636D79 Plum Noir 2.92 4.21 1.94 disqualified
Grave Black #09080B 15.99 7.35 the light-surface ring

Composited surfaces change nothing: ring vs velvet-panel rgba(24,20,29,.97)/bg = 7.50, vs ledger-panel = 7.70, vs input well rgba(18,15,22,.96)/panel = 7.85.

Three findings: (a) gold has real headroom on the darks, 1.7× the requirement at worst — the ring does not need to change. (b) outline-offset: 2px is load-bearing — at offset 0 or inset, gold measures 3.57 on the primary plate #5A4A1A and 2.90 on the hover plate #6A5720, a fail. It is an a11y mechanism, not spacing taste. (c) no single ring colour spans dark and light; a light surface forces a surface-aware ring token.

6. If a light surface enters

The whole existing ramp is unusable: best on champagne is Dust at 2.51, best on butcher paper is Bone at 2.07. A new dark ink token is forced — structural, not tunable.

Ink champagne #EFE5D2 butcher #B59785
Grave Black #09080B 15.99 7.35
Crypt Black #0F0D12 15.47 7.11
Plum Noir #351E28 12.29 5.65
oxblood #5B1E32 10.03 4.61 (+0.11)
dried-blood #8E3438 6.23 2.86 FAIL
stamp #A84A4E 4.48 FAIL by 0.02 2.06 FAIL
any pewter 4.21 / 2.84 / — 1.31–1.94 FAIL
any gold 1.94–2.66 FAIL 1.08–1.22 FAIL

Cheap answer: Plum Noir doubles as the light-surface ink. One new role (ink-on-light: #351E28), no new hex — 12.29 on champagne, 5.65 on butcher paper.

Two more light-surface facts: champagne vs butcher paper is 2.17:1, so they cannot sit adjacent without a border. And butcher paper is a plain-paper inset or nothing — no metal, no accent, no state colour clears anything on it.

7. The 74% placeholder claim — it does not hold

$input-placeholder-color: rgba($wg-text-soft, 0.74) (overrides.scss:75), composited over the real input well (rgba(18,15,22,.96) over #18141D, flattened #171319):

α composited ratio
0.60 #76716D 3.93 large only
0.6589 4.500 the actual edge
0.70 #87817B 4.93 AA
0.74 shipped #8E8881 5.38 +0.88 slack
1.00 #B9B2A6 9.01 AAA

Edge alpha on every other plausible backdrop: bg-deep 0.653, bg 0.656, input well 0.659, panel 0.668, panel-3 0.683. Worst case 0.683, so even α=0.70 passes everywhere.

Verdict: the claim is false and should be corrected in DESIGN.md:205. Leaving it is worse than a wrong colour — it makes the re-tune treat a comfortable value as untouchable and go hunting for slack somewhere that genuinely has none. (Same for DESIGN.md:222 — "Ash on Crypt Black is the floor" measures 9.19:1; it is the floor by intent, with 4.69 of slack.)

8. Where the theme really is at the floor

Real use (composited) ratio needs slack
hljs-deletion #C55A5F on code well, light end 4.51 4.5 +0.01 tightest text pair in the theme; a raw hex (_posts.scss:215), so a re-tune will not carry it
unread/nav count badge #8E3438 fill vs surface 2.34 3.0 −0.66 live SC 1.4.11 failure (_controls.scss:620) — its text is fine at 5.99, the badge shape is not
ledger stamp #A84A4E ornament on ledger-panel 3.34 3.0 +0.34 stamp cannot darken, panel cannot lighten
status-dnd #C55A5F as text on bg 4.60 4.5 +0.10
Old Gilt on panel-3 5.15 4.5 +0.65
Dust on panel-3 5.47 4.5 +0.97

Everything else has ≥1.3 of slack (badge text 5.99, primary-button text 7.00, ghost text 11.86, ::selection 8.97, Ledger Ink 10.66, statuses 8.25–9.73).

The alpha borders are all below 3:1 and that is fine--wg-border .14 → 1.26, --wg-border-soft .08 → 1.13, ledger/input border .18 → 1.34–1.35, code border .22 → 1.46, ruling .055 → 1.08, --wg-focus glow .28 → 1.67. SC 1.4.11 covers boundaries needed to identify a control; these are decorative and the control is identified by its fill, label and the solid ring. But it caps ambition: solved exactly, gold needs α ≈ 0.57 to reach 3:1 (0.529 input well, 0.533 panel, 0.541 panel-3, 0.565 Plum Noir). Anything in the 5.5–28% range is atmosphere only, permanently.

9. Impossible at AA — do not prototype these

# Combination Measured
1 Any gold as ink/border/ring/ornament on butcher paper #B59785 1.08–1.22 not fixable by picking another gold
2 Any gold on champagne #EFE5D2 1.94–2.66 Old Gilt is closest and still under 3:1
3 Any pewter on either light surface 1.31–4.21
4 Existing text ramp on any light surface ≤2.51 structural
5 pewter #636D79 on Plum Noir as ink/icon/border/ring 2.92 below even 3:1 on the confirmed base dark
6 pewter #636D79 on oxblood/plum-mid/plum-bloom/#3E1617 2.38–3.00
7 Dust #9A9086 as body copy on oxblood or plum-mid 4.00 / 4.38 mids cannot host metadata rows
8 Old Gilt as body text or a link on oxblood or plum-mid 3.77 / 4.13
9 Antique Gold as small text on oxblood 4.27 fine on the base darks, not on the accent
10 One focus-ring colour spanning dark and light every candidate fails ≥1 surface
11 Gold ring drawn on a gold plate (offset 0/inset) 3.57 on #5A4A1A, 2.90 on #6A5720
12 champagne adjacent to butcher paper, no border 2.17
13 Two metals at matched luminance (gold #C2A35A L=.3840 vs pewter #9EA7B3 L=.3816) 1.006

10. Candidate rules for #49 / #55

  1. Plum Noir #351E28 is safe and is also the ceiling — do not lighten any surface that carries small text.
  2. Two surface classes: base darks (L ≤ 0.0245) host body copy; oxblood/plum-mid accents (L 0.026–0.034) host large text, headings and non-text UI only.
  3. Gold is a link colour at Tarnished Gold and Gold Lit; Old Gilt is not.
  4. Antique Gold is admissible but ~1.1 points tighter everywhere; not a free swap.
  5. If a pewter lands, it is #9EA7B3. #7F8995 conditional, #636D79 out.
  6. A light surface costs one new ink role (ink-on-light: #351E28) plus a second focus-ring token. Budget both or drop it.
  7. Butcher paper is a plain-paper inset or nothing.
  8. Fix three inherited gaps while the palette is open: the unread badge at 2.34:1 (real 1.4.11 failure), hljs-deletion at 4.51:1, and the wrong 74% claim at DESIGN.md:205.
  9. Keep outline-offset on the focus ring — it is an accessibility mechanism.

Caveat worth carrying forward

WCAG 2.x relative luminance is a crude perceptual model, known to be harsh on light-on-dark pairs. These are the conformance numbers, which is what "AA is non-negotiable" means and what an audit will use — not a promise that a 4.6:1 pair looks comfortable. Anything measuring under ~5:1 in these tables should still be looked at in a browser before it ships. Gradients were measured at their end stops only; anti-aliased thin gilt rules render below their nominal alpha, so the §8 border numbers are optimistic if anything.

## Resolved Full findings: `docs/research/palette-contrast-headroom.md` on branch `research/palette-contrast-headroom` (not pushed, not PR'd — commit `91cfd1a`). Every number below is computed WCAG 2.x relative luminance, `(L1+0.05)/(L2+0.05)`, with alpha colours **flattened over their real backdrop first** (source-over, sRGB). The script self-checked against WCAG's own published values (`#000`/`#fff` = 21.0, `#767676` on white = 4.54, `#949494` on white = 3.03, `#959595` < 3.0) before producing any palette number. ### Headline 1. **Every base dark works.** All of them, including the lightest (oxblood `#5B1E32`), clear 4.5:1 with Bone, Ash and Ledger Ink. Dark surfaces are not the constraint. 2. **Plum Noir `#351E28` is safe *and* it is the ceiling.** L=0.0184. The Dust-Text body-copy ceiling is L=0.0245. There is almost no room above Plum Noir. 3. **Gold can be a link colour** — at Tarnished Gold, Gold Lit and Antique Gold. Not at Old Gilt. 4. **A light surface is a second palette, not a token.** The whole text ramp fails on both (best 2.51:1). 5. **Gold and butcher paper `#B59785` cannot coexist at all** — all four golds sit at 1.08–1.22:1. Hardest kill in the space. 6. **The DESIGN.md 74% placeholder claim is wrong** — the 4.5:1 edge is 66%, and 74% measures 5.38:1. There is slack there, not zero. ### 1. Text ramp on the base darks — no failures | fg \ bg | Plum Noir `#351E28` | Bruised Plum `#2A1222` | `#141214` | `#1B1211` | `#110E0D` | Crypt `#0F0D12` | Grave `#09080B` | panel `#18141D` | panel-2 `#110F15` | panel-3 `#201924` | |---|---|---|---|---|---|---|---|---|---|---| | Bone `#E6E0D6` | 11.70 | 13.24 | 14.20 | 14.02 | 14.64 | 14.72 | 15.22 | 13.83 | 14.50 | 13.05 | | Ash `#B9B2A6` | 7.30 | 8.26 | 8.86 | 8.75 | 9.14 | 9.19 | 9.50 | 8.63 | 9.05 | 8.14 | | Dust `#9A9086` | **4.90** | 5.55 | 5.96 | 5.88 | 6.14 | 6.17 | 6.38 | 5.80 | 6.08 | 5.47 | | Ledger Ink `#D8C28A` | 8.78 | 9.94 | 10.66 | 10.52 | 10.99 | 11.05 | 11.42 | 10.38 | 10.88 | 9.79 | | Ink Muted `#A99D8F` | 5.78 | 6.54 | 7.02 | 6.93 | 7.24 | 7.28 | 7.52 | 6.84 | 7.17 | 6.45 | All ≥4.5:1. Plum Noir's weakest pair (Dust, 4.90) has +0.40 of slack. Plum Noir superseding Bruised Plum costs 0.6–1.5 points across the ramp and breaks nothing. ### 2. On the oxblood/wine/plum mids — Dust breaks | fg \ bg | oxblood `#5B1E32` | `#3E1617` | `#340C0C` | `#22040D` | plum-mid `#402533` | `#311B1D` | plum-bloom `#3A1830` | |---|---|---|---|---|---|---|---| | Bone | 9.54 | 12.01 | 13.29 | 14.66 | 10.45 | 12.26 | 11.82 | | Ash | 5.95 | 7.49 | 8.29 | 9.15 | 6.52 | 7.65 | 7.38 | | Dust | **4.00 large only** | 5.04 | 5.57 | 6.15 | **4.38 large only** | 5.14 | 4.96 | | Ledger Ink | 7.16 | 9.01 | 9.97 | 11.00 | 7.84 | 9.20 | 8.87 | | Ink Muted | 4.72 | 5.94 | 6.57 | 7.25 | 5.17 | 6.06 | 5.84 | **Rule that falls out: oxblood and plum-mid are accent surfaces, not body-copy surfaces.** Put a timestamp on them and you are out of AA. ### 3. Gold as body/link text — the three-gold verdict | Gold | Plum Noir | Bruised Plum | Crypt | Grave | panel | panel-3 | oxblood | plum-mid | |---|---|---|---|---|---|---|---|---| | Tarnished `#C2A35A` | 6.35 | 7.19 | 7.99 | 8.26 | 7.50 | 7.08 | 5.18 | 5.67 | | Old Gilt `#A8893F` | **4.62** | 5.23 | 5.81 | 6.01 | 5.46 | 5.15 | **3.77 large only** | **4.13 large only** | | Gold Lit `#E0C878` | 9.29 | 10.51 | 11.69 | 12.08 | 10.98 | 10.36 | 7.58 | 8.30 | | Antique `#C28D39` | 5.24 | 5.93 | 6.59 | 6.81 | 6.19 | 5.84 | **4.27 large only** | 4.68 | - **Tarnished Gold — yes, a link colour.** Worst case 5.18 on oxblood. Comfortable. Keep it. - **Gold Lit — AAA everywhere.** Safe as hover; the only gold that would survive a lighter surface. - **Antique Gold — admissible but tighter.** Costs ~1.1 points of ratio vs `#C2A35A` everywhere; +0.74 slack on Plum Noir; **fails on oxblood (4.27)**. Not a free swap for the warmth. - **Old Gilt — border and pressed-state only, never a link or body copy.** 4.62 on Plum Noir is +0.12, a rounding error, not a margin. ### 4. Second metal, neutral pewter only (no lavender/purple-cast) | Pewter | Plum Noir | Crypt | Grave | panel-3 | oxblood | plum-mid | Verdict | |---|---|---|---|---|---|---|---| | `#9EA7B3` | 6.31 | 7.94 | 8.21 | 7.04 | 5.15 | 5.64 | **body text anywhere on the darks — this is the one** | | `#7F8995` | **4.32 large** | 5.44 | 5.63 | 4.82 | **3.53 large** | **3.86 large** | small text on the near-blacks only | | `#636D79` | **2.92 FAIL** | 3.68 | 3.80 | **2.38 FAIL** | **2.38 FAIL** | **2.61 FAIL** | **fails even 3:1 on Plum Noir — cut it** | Catch: the pewters are luminance twins of the golds. `#9EA7B3` L=0.3816 vs Tarnished Gold L=0.3840 — **1.006:1 between them.** A two-metal hierarchy has to work by hue and placement; contrast will not help it. ### 5. Focus ring, SC 1.4.11 (3:1) Current ring is `outline: 2px solid #c2a35a` with `outline-offset: 2px` (`_controls.scss:672`). The offset means the ring's adjacent colour is the surface *behind* the control, not its fill. | Ring | worst dark surface | ratio | champagne `#EFE5D2` | butcher `#B59785` | Verdict | |---|---|---|---|---|---| | Tarnished `#C2A35A` | oxblood | **5.18** | 1.94 | **1.12** | OK on all darks; fails on light | | Gold Lit `#E0C878` | oxblood | 7.58 | **1.32** | 1.64 | OK on darks; fails on light | | Antique `#C28D39` | oxblood | **4.27** | 2.35 | **1.08** | OK on darks; fails on light | | Old Gilt `#A8893F` | oxblood | **3.77** | 2.66 | **1.22** | thin on darks; fails on light | | pewter `#9EA7B3` | oxblood | 5.15 | 1.95 | **1.12** | OK on darks; fails on light | | pewter `#636D79` | **Plum Noir** | **2.92** | 4.21 | **1.94** | **disqualified** | | Grave Black `#09080B` | — | — | **15.99** | **7.35** | **the light-surface ring** | Composited surfaces change nothing: ring vs velvet-panel `rgba(24,20,29,.97)`/bg = 7.50, vs ledger-panel = 7.70, vs input well `rgba(18,15,22,.96)`/panel = 7.85. Three findings: **(a)** gold has real headroom on the darks, 1.7× the requirement at worst — the ring does not need to change. **(b)** `outline-offset: 2px` is load-bearing — at offset 0 or inset, gold measures **3.57 on the primary plate `#5A4A1A` and 2.90 on the hover plate `#6A5720`**, a fail. It is an a11y mechanism, not spacing taste. **(c)** no single ring colour spans dark and light; a light surface forces a surface-aware ring token. ### 6. If a light surface enters The whole existing ramp is unusable: best on champagne is Dust at **2.51**, best on butcher paper is Bone at **2.07**. **A new dark ink token is forced** — structural, not tunable. | Ink | champagne `#EFE5D2` | butcher `#B59785` | |---|---|---| | Grave Black `#09080B` | 15.99 | 7.35 | | Crypt Black `#0F0D12` | 15.47 | 7.11 | | **Plum Noir `#351E28`** | **12.29** | **5.65** | | oxblood `#5B1E32` | 10.03 | **4.61 (+0.11)** | | dried-blood `#8E3438` | 6.23 | **2.86 FAIL** | | stamp `#A84A4E` | **4.48 FAIL by 0.02** | **2.06 FAIL** | | any pewter | 4.21 / 2.84 / — | **1.31–1.94 FAIL** | | any gold | **1.94–2.66 FAIL** | **1.08–1.22 FAIL** | **Cheap answer: Plum Noir doubles as the light-surface ink.** One new *role* (`ink-on-light: #351E28`), no new hex — 12.29 on champagne, 5.65 on butcher paper. Two more light-surface facts: champagne vs butcher paper is **2.17:1**, so they cannot sit adjacent without a border. And **butcher paper is a plain-paper inset or nothing** — no metal, no accent, no state colour clears anything on it. ### 7. The 74% placeholder claim — it does not hold `$input-placeholder-color: rgba($wg-text-soft, 0.74)` (`overrides.scss:75`), composited over the real input well (`rgba(18,15,22,.96)` over `#18141D`, flattened `#171319`): | α | composited | ratio | | |---|---|---|---| | 0.60 | `#76716D` | 3.93 | large only | | **0.6589** | — | **4.500** | **the actual edge** | | 0.70 | `#87817B` | 4.93 | AA | | **0.74 shipped** | **`#8E8881`** | **5.38** | **+0.88 slack** | | 1.00 | `#B9B2A6` | 9.01 | AAA | Edge alpha on every other plausible backdrop: bg-deep 0.653, bg 0.656, input well 0.659, panel 0.668, panel-3 0.683. Worst case 0.683, so **even α=0.70 passes everywhere.** **Verdict: the claim is false and should be corrected in DESIGN.md:205.** Leaving it is worse than a wrong colour — it makes the re-tune treat a comfortable value as untouchable and go hunting for slack somewhere that genuinely has none. (Same for DESIGN.md:222 — "Ash on Crypt Black is the floor" measures 9.19:1; it is the floor by intent, with 4.69 of slack.) ### 8. Where the theme really is at the floor | Real use (composited) | ratio | needs | slack | | |---|---|---|---|---| | **`hljs-deletion` `#C55A5F` on code well, light end** | **4.51** | 4.5 | **+0.01** | tightest text pair in the theme; a raw hex (`_posts.scss:215`), so a re-tune will not carry it | | **unread/nav count badge `#8E3438` fill vs surface** | **2.34** | 3.0 | **−0.66** | **live SC 1.4.11 failure** (`_controls.scss:620`) — its *text* is fine at 5.99, the badge *shape* is not | | **ledger stamp `#A84A4E` ornament on ledger-panel** | **3.34** | 3.0 | **+0.34** | stamp cannot darken, panel cannot lighten | | status-dnd `#C55A5F` as text on bg | 4.60 | 4.5 | +0.10 | | | Old Gilt on panel-3 | 5.15 | 4.5 | +0.65 | | | Dust on panel-3 | 5.47 | 4.5 | +0.97 | | Everything else has ≥1.3 of slack (badge text 5.99, primary-button text 7.00, ghost text 11.86, `::selection` 8.97, Ledger Ink 10.66, statuses 8.25–9.73). **The alpha borders are all below 3:1 and that is fine** — `--wg-border` .14 → 1.26, `--wg-border-soft` .08 → 1.13, ledger/input border .18 → 1.34–1.35, code border .22 → 1.46, ruling .055 → 1.08, `--wg-focus` glow .28 → 1.67. SC 1.4.11 covers boundaries *needed to identify a control*; these are decorative and the control is identified by its fill, label and the solid ring. **But it caps ambition:** solved exactly, gold needs **α ≈ 0.57** to reach 3:1 (0.529 input well, 0.533 panel, 0.541 panel-3, 0.565 Plum Noir). Anything in the 5.5–28% range is atmosphere only, permanently. ### 9. Impossible at AA — do not prototype these | # | Combination | Measured | | |---|---|---|---| | 1 | Any gold as ink/border/ring/ornament on **butcher paper `#B59785`** | 1.08–1.22 | not fixable by picking another gold | | 2 | Any gold on **champagne `#EFE5D2`** | 1.94–2.66 | Old Gilt is closest and still under 3:1 | | 3 | Any pewter on either light surface | 1.31–4.21 | | | 4 | **Existing text ramp on any light surface** | ≤2.51 | structural | | 5 | **pewter `#636D79` on Plum Noir** as ink/icon/border/ring | **2.92** | below even 3:1 on the confirmed base dark | | 6 | pewter `#636D79` on oxblood/plum-mid/plum-bloom/`#3E1617` | 2.38–3.00 | | | 7 | **Dust `#9A9086` as body copy on oxblood or plum-mid** | 4.00 / 4.38 | mids cannot host metadata rows | | 8 | **Old Gilt as body text or a link on oxblood or plum-mid** | 3.77 / 4.13 | | | 9 | Antique Gold as small text on oxblood | 4.27 | fine on the base darks, not on the accent | | 10 | **One focus-ring colour spanning dark and light** | every candidate fails ≥1 surface | | | 11 | **Gold ring drawn *on* a gold plate** (offset 0/inset) | 3.57 on `#5A4A1A`, **2.90 on `#6A5720`** | | | 12 | **champagne adjacent to butcher paper**, no border | 2.17 | | | 13 | Two metals at matched luminance (gold `#C2A35A` L=.3840 vs pewter `#9EA7B3` L=.3816) | **1.006** | | ### 10. Candidate rules for #49 / #55 1. Plum Noir `#351E28` is safe and is also the ceiling — do not lighten any surface that carries small text. 2. **Two surface classes:** base darks (L ≤ 0.0245) host body copy; oxblood/plum-mid accents (L 0.026–0.034) host large text, headings and non-text UI only. 3. Gold is a link colour at Tarnished Gold and Gold Lit; **Old Gilt is not**. 4. Antique Gold is admissible but ~1.1 points tighter everywhere; not a free swap. 5. If a pewter lands, it is **`#9EA7B3`**. `#7F8995` conditional, `#636D79` out. 6. A light surface costs **one new ink role** (`ink-on-light: #351E28`) **plus a second focus-ring token.** Budget both or drop it. 7. Butcher paper is a plain-paper inset or nothing. 8. **Fix three inherited gaps while the palette is open:** the unread badge at 2.34:1 (real 1.4.11 failure), `hljs-deletion` at 4.51:1, and the wrong 74% claim at DESIGN.md:205. 9. Keep `outline-offset` on the focus ring — it is an accessibility mechanism. ### Caveat worth carrying forward WCAG 2.x relative luminance is a crude perceptual model, known to be harsh on light-on-dark pairs. These are the *conformance* numbers, which is what "AA is non-negotiable" means and what an audit will use — not a promise that a 4.6:1 pair looks comfortable. **Anything measuring under ~5:1 in these tables should still be looked at in a browser before it ships.** Gradients were measured at their end stops only; anti-aliased thin gilt rules render below their nominal alpha, so the §8 border numbers are optimistic if anything.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Blocks
#53 Palette candidates on a dense page
ShadowsOverWestgate/sow-nodebb-theme
Reference: ShadowsOverWestgate/sow-nodebb-theme#50