Palette candidates on a dense page #53
Notifications
Due Date
No due date set.
Blocks
Depends on
#54 The ornament and frame component set
ShadowsOverWestgate/sow-nodebb-theme
#55 Role-based token contract
ShadowsOverWestgate/sow-nodebb-theme
#57 The home board
ShadowsOverWestgate/sow-nodebb-theme
#49 North star and named rules for the noir re-tune
ShadowsOverWestgate/sow-nodebb-theme
#50 Contrast headroom of the candidate palette space
ShadowsOverWestgate/sow-nodebb-theme
Reference: ShadowsOverWestgate/sow-nodebb-theme#53
Reference in New Issue
Block a user
Part of #47
Question
Which palette candidate actually survives contact with a real page?
Build two or three complete palette candidates as token sets and look at them in the live dev stack. Each candidate is a full role assignment — base, panel tones, borders, text ramp, accent, state colours — not a mood board.
The candidates must be judged on a dense forum topic page and a category list first, and only then on a card grid. A palette that only works on a spacious marketing page is not a palette for this site; the forum is where most pixels actually are.
Judge:
#351E28.Every candidate is contrast-checked against the headroom table before it is built; no candidate ships past AA failures. Prototypes are throwaway, on a
prototype/*branch, linked from this ticket, never merged.Resolution names the winning candidate and its full role assignment.
Prototype built — three candidates on the live stack
Branch
prototype/53-palette-candidates(pushed, not for merge). Judged in abrowser on a real 13-post topic and the real category list first, card grid last,
per the ticket. Screenshots in
prototype/shots/, replay steps inprototype/README.md.How it was built. The theme's colours were mostly literal — 233
rgba(194, 163, 90, …)uses alone — so a candidate could not be swapped without a rebuild each time. The branch
mechanically tokenises them (
var(--wg-gold-rgb),var(--wg-gold), …), which makes eachcandidate a
:rootoverride injected at runtime. A computed-style probe (prototype/probe.js)confirms the swap actually reaches the pixels: body bg
rgb(15,13,18)→rgb(21,14,19),metadata
rgb(154,144,134)→ pewterrgb(158,167,179)under B.Contrast first, as required.
prototype/contrast.py(same method as #50, self-checkedagainst WCAG's published values) clears every candidate surface before it was built. The four
new surface hexes carry the whole text ramp at AA: bg
#150E13(Dust 6.08), bg-deep#0C0810(6.34), panel
#21161C(5.61), panel-2#180F16(6.00), with Plum Noir#351E28as panel-3(Dust 4.90, the known ceiling). Pewter
#9EA7B3is AA on all of them (6.31–8.16).plum-soft #4A2A38is accent-only — Dust 3.99, Old Gilt 3.76 — which is exactly #50's rule 2 holding.The candidates
keeps every job it has today.
#9EA7B3taking borders, rulings and metadata; gilt keepslinks, ledger ink, buttons, focus ring, stamps.
#EFE5D2inset on quoted material,ink
#351E28, no metal on it.What the pages showed
#150E13reads brown-plum and warm; nothing in the crops reads lavender.
metadata are the same warm ash as the body copy, so the row is one material and separation
leans on panel tone alone. In B the description and the
topics/postslabels go cool andthe row splits into value (gold/bone) and structure (pewter) at a glance.
compete for the same role. #50's warning that they are luminance twins (1.006:1) is real and
is why this only works by hue and placement, never as a contrast step.
shots/quote-c.png: the champagneblock is the brightest thing on the page, so quoted material — secondary by definition —
out-shouts the reply that contains it. Hierarchy inverts. A light surface may still earn a
showcase plaque; it does not earn an app-surface component.
the warm ash on warm dark stops the faint blue cast the baseline has.
One thing the prototype exposed
scss/overrides.scssholds a second, hidden palette layer — Sass variables ($wg-ink,$gray-*,$black,$dropdown-bg) that must be real colours, notvar(), so Sass can passthem to
rgba()/Bootstrap. They do not follow a token swap: in the candidate shots the topbarkeeps the old cold ground while the page below it moves, a visible seam. Any re-tune has to
change both layers, or the shell and the page disagree. That is a constraint on #55's token
contract, not a bug in the candidates.
Recommendation
B — Two Metals, with the light surface dropped from the app surfaces and left as an open
question for showcase ornament only.
Full role assignment for B:
#150E13/#0C0810#21161C/#180F16/#351E28(Plum Noir)#351E28/#4A2A38(accent surface, large text and non-text only)#E6E0D6/#B9B2A6/#9EA7B3(pewter)#C2A35A/ Gold Lit#E0C878/ Old Gilt#A8893F#D8C28A/ pewter#9EA7B3/#A84A4E#8E3438, Sparing Red unchanged#C2A35Awithoutline-offset: 2pxkept (#50 finding b)This ticket is HITL and stays open until the human has looked. The judgement above is the
prototype's read, not the resolution.
Resolution
Candidate B — Two Metals wins, with the light surface dropped from every app surface.
Confirmed in a browser on the dense topic and the category list before the card grid.
Evidence: branch
prototype/53-palette-candidates(throwaway, never merges) — candidates asruntime
:rootswaps,prototype/contrast.pyfor the AA check,prototype/shots/for theimages,
prototype/README.mdto replay it.The winning role assignment
--wg-bg/--wg-bg-deep#150E13/#0C0810--wg-panel/-2/-3#21161C/#180F16/#351E28(Plum Noir)--wg-plum/-soft#351E28/#4A2A38— accent surface, large text and non-text only--wg-text/-soft/-muted#E6E0D6/#B9B2A6/#9EA7B3(pewter)#C2A35A/ Gold Lit#E0C878/ Old Gilt#A8893F--wg-border/-soft/--wg-ledger-ruling--wg-ledger-ink/-ink-muted/-stamp#D8C28A/ pewter#9EA7B3/#A84A4E--wg-red#8E3438, Sparing Red unchanged#C2A35A,outline-offset: 2pxkept — an a11y mechanism, not spacing (#50 finding b)The five questions the ticket asked
candidate a category row is a single material and separation leans on panel tone alone. In B
the row splits into value (gold, bone) and structure (pewter) at a glance.
for the same role — this is Two Metals from #49 doing real work. #50's warning holds: at
1.006:1 they are luminance twins, so the split is carried by hue and placement only, never as
a contrast step.
champagne block becomes the brightest thing on the page, so secondary content out-shouts the
reply containing it. Hierarchy inverts. It stays open only as possible showcase ornament —
handed to #54, not carried here.
#150E13over Plum Noir#351E28reads brown-plumand warm; nothing in the crops reads lavender.
faint blue cast the baseline has over a minute of reading.
Contrast
Every surface was cleared before it was built, same method as #50, self-checked against WCAG's
published values. Full text ramp at AA on all four new surfaces (worst pair Dust on
#21161C,5.61); Plum Noir at its known ceiling (Dust 4.90); pewter
#9EA7B3AA on all of them(6.31–8.16);
#4A2A38accent-only (Dust 3.99, Old Gilt 3.76), which is #50's rule 2 holding.What this exposed for later tickets
scss/overrides.scssis a second, hidden palette layer. Its Sass variables ($wg-ink,$gray-*,$black,$dropdown-bg) must be real colours, notvar(), because Sass passesthem to
rgba()and Bootstrap. They do not follow a token swap: in the candidate shots thetopbar keeps the old cold ground while the page below it moves. A re-tune has to change both
layers or the shell and the page disagree. Constraint on #55.
rgba(194, 163, 90, …)uses alone. Shippingany palette change means tokenising first. The prototype branch has a working mechanical pass
to crib from.
The remaining question is which of
velvet-panel/-deep/-raisedstill earn a slot, andthat is #55's to answer, not fog.