You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(lcars): choose the glyph against the disc it sits on
LCARS fills an active part with --liebe-part-color and declared a fixed
dark glyph on it. For the two data-driven paths - a bulb's own RGB, a
person's identity colour - that disc is a value no theme has seen, so the
glyph was whatever the data made it: 2.44:1 for a pure-blue bulb against
the 3:1 floor, 3.90:1 for the worst identity hue against the 4.5:1 an
avatar's initials answer to as text.
The task carried a question whose answer decides where it lives, and it
is the first branch: --liebe-part-color is published, already carries the
live value, and is already what this theme reads for the disc, so a theme
can DERIVE the foreground even though no token holds one. My earlier read
that all three published tokens hold author-chosen colours was right
about the tokens and wrong about the conclusion - the question was
whether one carries enough to compute from. 0036 is not reopened.
contrast-color() returns black or white, whichever contrasts more, so it
can only equal or beat the fixed black. Measured: bulb 2.44 -> 5.25,
worst identity hue 3.90 -> 4.75, all 126 live-hue composites clear. The
ten triplet glyphs are byte-identical (worst 5.05) because the function
picks the same black for every colour the theme calibrated.
Beneath it, a relative-colour rung for browsers with that syntax but not
the function. Swept at five thresholds; the best still leaves one
identity hue at 4.42:1, because no single Oklch lightness cut reproduces
contrast-color() for chromatic colours - L does not track WCAG luminance
across hues. It takes those browsers from six failing composites to one:
a narrowing, recorded as such rather than presented as a closure.
0035 stays draft. PR 6 is ticked and a new PR 7 records what this could
not settle - the icon-only tile composite, where a reviewer's ~2.33:1 and
a synthetic harness's 6.81-10.80:1 are BOTH unverified against how
GridCard delivers hueStyle to that tile, and the rung's residual.
Copy file name to clipboardExpand all lines: docs/changes/0035-light-appearance-contrast.md
+11-1Lines changed: 11 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -79,12 +79,22 @@ PR 6 is PR 4's own residue and depends on nothing: it was measured in the same r
79
79
-[x]**PR 3 — Residual accessible names**: name `InputSelectCard`'s select trigger and the switch `InputBooleanCard` renders in its own tile at `controlStyle: switch` (**not** the detail-controls switch, which is already named); re-run the `button-name` audit across every story in both appearances and record the resulting count; drop #191 from the outstanding list
80
80
-[x]**PR 4 — The live hue on its own tint**: decide and implement what the glyph takes when a data-driven hue overrides the triplet — the light card's bulb colour under `useLightColor` and the person avatar's identity colour, one mechanism in `hueStyle` (`src/components/anatomy/anatomyPart.ts`) rather than two card defects; measure both against their floors (the avatar's initials are text at 4.5:1, not a glyph at 3:1) and move [options/light](../specs/entity-cards/options/light.md) and [options/person](../specs/entity-cards/options/person.md) with whatever is decided
81
81
- [x] **PR 5 — The dark-appearance glyph on `media`**: `indigo-9` on its own 20% tint over the dark card measures **2.81:1**, under the 3:1 glyph floor. **It was not the only one, and that is the correction this PR carries**: the figure was Default's, and re-measured across every built-in theme in dark, Liquid Glass misses on three — `media` 2.17:1, `alert` 2.47:1, `ok` 2.89:1 — while Default misses on `media` alone (2.80:1 re-measured, next worst `alert` 3.63:1). LCARS clears everywhere, worst 5.05:1, because it owns its icon treatment. Found while implementing [0033](./0033-icon-only-cards.md)'s icon-only tile tint, which reproduces this pattern's exact composite at tile scale and so inherits the figure rather than introducing it. It contradicts this change's Out-of-Scope claim that "all four defects pass in dark", which was made about the light-appearance repin and never measured per hue in dark; resolving this task MUST correct that line as well. Resolved by extending PR 2's own answer rather than inventing a second one: the glyph takes the `-text` token in **both** appearances, which retires the per-appearance switch and the `--part-<name>-glyph` indirection with it, so `anatomy.css` now keys nothing off the appearance at all. All 50 hue×theme×appearance composites clear, worst 3.68:1 and that one in light; light and LCARS are unchanged to the decoded pixel.
82
-
- [ ] **PR 6 — LCARS puts a fixed dark glyph on a live hue**: the theme declares `color` on the active circle itself and reads no role token, so PR 4's split does not reach it; its treatment is a solid disc of the hue rather than a 20% tint, and the fixed dark glyph on it measures **2.44:1** for a pure-blue bulb against the 3:1 glyph floor and **3.90:1** for the worst of the eight identity hues against the 4.5:1 the avatar's initials answer to (violet 3.90, iris 3.91, purple 4.06, plum 4.42; the other four clear). Found while measuring PR 4, on the same rig and in the same run, and unchanged by it — before and after are byte-identical for every LCARS cell. It is here rather than in [0036](./0036-theming-contract-gaps.md) because it is a contrast floor missed, not a theming hook missing; what it may need from 0036 is a hook, which is the decision this task carries. Note the shape of the problem before reaching for a repin: the failing ground is **data**, so there is no value a theme can pin that answers for every bulb colour a user's lamp can report. LCARS is dark-only, so this is a dark-appearance figure throughout.
82
+
- [x] **PR 6 — LCARS puts a fixed dark glyph on a live hue**: the theme declares `color` on the active circle itself and reads no role token, so PR 4's split does not reach it; its treatment is a solid disc of the hue rather than a 20% tint, and the fixed dark glyph on it measures **2.44:1** for a pure-blue bulb against the 3:1 glyph floor and **3.90:1** for the worst of the eight identity hues against the 4.5:1 the avatar's initials answer to (violet 3.90, iris 3.91, purple 4.06, plum 4.42; the other four clear). Found while measuring PR 4, on the same rig and in the same run, and unchanged by it — before and after are byte-identical for every LCARS cell. It is here rather than in [0036](./0036-theming-contract-gaps.md) because it is a contrast floor missed, not a theming hook missing; what it may need from 0036 is a hook, which is the decision this task carries. Note the shape of the problem before reaching for a repin: the failing ground is **data**, so there is no value a theme can pin that answers for every bulb colour a user's lamp can report. LCARS is dark-only, so this is a dark-appearance figure throughout.
83
83
84
84
**PR 5's sweep narrows this usefully: LCARS's ten _triplet_ glyphs all clear in dark, worst 5.05:1** (`vacuum`; next `alert` 5.48:1), measured on the same rig in the same run. So the theme's solid-disc treatment is sound for every colour it was calibrated against, and the defect is confined to the **live-hue** path — a hue the theme never saw. That is evidence for the reading that no repin can fix it, since the failing input is data rather than a palette entry, and it means PR 6 need not revisit the triplet treatment at all.
85
85
86
86
**Start from [0036](./0036-theming-contract-gaps.md) PR 3's published tokens, not from the assumption that a hook is missing.** That PR landed `--liebe-part-color`, `--liebe-part-label` and `--liebe-part-label-active` precisely so a theme filling a part with a saturated hue can name the foreground on it — which is this theme, this composite, and the reason LCARS's own restatement of the domain-colour mapping was retired. The likely fix is therefore LCARS **consuming** finished work rather than 0036 owing more, and this task belongs here because it is a measured floor missed and 0035 owns whether a composite clears its floor. Read those three tokens before concluding otherwise. **The condition under which this moves:** if no published token can express "a solid disc of a **live** hue needs a foreground chosen against _that hue_" — the value being data, not a colour a theme can pick in advance — then the gap is in the contract rather than in LCARS, and that finding reopens [0036](./0036-theming-contract-gaps.md) and takes this task with it. Record which of the two it turned out to be either way.
87
87
88
+
**It turned out to be the first: the contract needed nothing added.** `--liebe-part-color` is published, already carries the live value, and is already what this theme reads for the disc — so a theme can _derive_ the foreground from it even though no token _holds_ one. `contrast-color(var(--liebe-part-color))` returns black or white, whichever contrasts more, so it can only equal or beat the fixed black it replaced. My earlier read — that all three published tokens hold colours a theme author chooses, none a function of a runtime value — was **right about the tokens and wrong about the conclusion**: the question was never whether a token _holds_ the answer, but whether one carries enough for a theme to compute it. **0036 is not reopened.** Measured: a pure-blue bulb 2.44:1 → **5.25:1**, the worst identity hue 3.90:1 → **4.75:1**, all 126 live-hue composites clear, and LCARS's ten triplet glyphs byte-identical (worst 5.05:1) because the function picks the same black for every colour the theme calibrated. Behind `@supports`, so a browser without the function keeps today's treatment.
89
+
90
+
-[ ]**PR 7 — The LCARS icon-only tile, and the fallback rung's residual**: two things PR 6 could not settle, both recorded as uncertainty rather than as findings.
91
+
92
+
**The tile composite is unverified in both directions.** `codex` review of PR 6 read `.liebe-card[data-icon-tile]:not([data-domain='person']) .liebe-icon[data-active] { color: var(--liebe-part-color) }` as leaving a live-hue glyph at ~**2.33:1** on its own tint. A synthetic harness built for that composite measured **6.81–10.80:1** instead — but for a reason that invalidates the harness rather than the claim: the glyph painted `#edb378`, because `--liebe-part-color` **resolved to LCARS's own colour rather than the hue set inline**. This theme re-declares that token _on the part_, which beats a value inherited from the card — and the card is where `GridCard` puts `hueStyle` for an icon-only tile. So the harness may have measured a composite that does not occur, and **neither figure is trustworthy**. This is the artifact-identity trap one level up: the rig was faithful to the stylesheets and not to how the DOM is actually assembled. Settling it needs a **real-story or e2e** measurement of a live-hue icon-only tile under LCARS, not another synthetic pass — do not rebuild the harness and trust it.
93
+
94
+
**The fallback rung does not fully close the floor.** PR 6's middle rung (relative-colour syntax, for browsers with it but without `contrast-color()`) was swept at five thresholds; the best leaves one of the eight identity hues at **4.42:1** against the 4.5:1 text floor, because no single Oklch lightness cut reproduces `contrast-color()` for chromatic colours — L does not track WCAG relative luminance across hues. It takes that browser set from six failing composites to one, which is a narrowing and not a closure. Options for closing it: a per-hue exception, a computed foreground supplied by the card rather than the theme, or accepting the residual and recording it.
95
+
96
+
**`docs/PRD.md` states no browser floor** — "Modern browsers only (Chrome, Firefox, Safari, Edge)", with no version, no `browserslist` and no Vite target — so how much either rung matters cannot be evaluated. Whoever takes this should settle that first; a progressive-enhancement decision has no meaning against an unstated baseline.
97
+
88
98
## Out of Scope
89
99
90
100
-**Slider thumb names** — already fixed ([#192](https://github.com/fx/liebe/issues/192), closed).
0 commit comments