Skip to content

fix(theme): tokenise the accents, so light stops painting dark-tuned colour (#402) - #607

Merged
cevheri merged 5 commits into
mainfrom
feat/tokenise-accent-colours
Sep 6, 2026
Merged

fix(theme): tokenise the accents, so light stops painting dark-tuned colour (#402)#607
cevheri merged 5 commits into
mainfrom
feat/tokenise-accent-colours

Conversation

@cevheri

@cevheri cevheri commented Sep 6, 2026

Copy link
Copy Markdown
Member

Closes #402.

What the issue claimed, and what actually measured

The defect is real and the direction was right. Two figures in the table were not.

claim issue measured
text-blue-300 on bg-blue-500/15 over #fafafa 1.46 : 1 1.457 : 1 confirmed
the same pairing on #0a0a0a "comfortable" 9.48 : 1 confirmed
text-blue-400 on its ground 2.26 / 2.38 2.527 : 1
text-amber-300 on #fafafa ~2.0 1.386 : 1
text-amber-500 on #fafafa 2.05 2.055 : 1 confirmed

The two wrong ones look like a mislabelled row and a pre-v4 palette value; neither changes the conclusion. One premise did not survive: "one deliberate literal remains in src" — there are ~32 *-white sites, though those are #384's neutral scope, not this one.

Every line reference in the issue had drifted (AgentRail.tsx:1028:1989, BottomPanel.tsx:204:256, and so on), and the surface is larger than the table: 955 colour literals across 68 files.

The load-bearing correction: the obvious light steps do not work. blue-600 measures 4.23 and amber-700 4.06 on their own /15 tint — both fail. The tint is ~1.19× harder than the plain ground across the whole ramp, and it flips the verdict for exactly the two steps a reviewer reaches for first.

And the two acceptance criteria are not in tension. Dark blue-300 sits at 9.48 on its own tint. Nothing in dark needs to move.

What this does

Two families, because colour here answers two different questions.

Statebrand / warning / success / danger, each with -bright (emphasis), -tint (the wash it sits on), -solid / -solid-hover (filled grounds). Used where colour tracks a changing condition.

Identity--studio-hue-<h> for the hues the app uses, some with -alt (a second step) and -tint. Used where colour is a fixed label: which engine, which panel, added vs removed, primary key vs foreign key, number vs boolean. Folding these into four state roles would repaint nineteen engines in four colours. Selected per mode the way lib/charts/palette.ts selects, rather than flipping a ramp — the two modes run out of room in different places.

It is brand, not accent. shadcn owns --accent (its neutral hover ground, #f5f5f5) and globals.css maps --color-accent to it after importing the studio layer, so a token of that name loses the cascade in silence: .text-accent{color:var(--accent)} — near-white text on a white page. Only the compiled stylesheet shows this; no unit test can see it. Caught in the browser.

Acceptance

  • Accent text clears 4.5:1 on its own ground in both themes, by measurement. Light floor 4.79:1, dark floor 4.97:1 across every token, on all five studio grounds and on washes of its own hue at every alpha the code actually paints.
  • Accents resolve through the token layer. dist/styles.css carries all 135 declarations; build:lib + attw green.
  • Dark does not visibly change. A pair-by-pair audit of the diff resolves every removed literal and every added token to a hex: 93 differences, all of them the one sanctioned class below, zero others.

The one deliberate dark change

A -500 (or -200) text literal was drift — the codebase used three steps of one hue for one meaning. Those collapse onto the base token and move one step in dark. 93 sites, all text, no backgrounds or borders. Everything else is byte-for-byte identical, resolved against the installed Tailwind palette rather than transcribed, so a Tailwind upgrade that restyles blue-400 fails the suite instead of shipping quietly.

Verified in a real browser

Chrome, production build, live PostgreSQL, both themes, resolving computed colour through a canvas because Tailwind emits color-mix(in oklab, …) for every opacity modifier.

The issue's own element — the active agent-mode pill, bg-blue-500/15 — composites to #dbe8fb, exactly the ground the issue predicted:

before after
light #8ec5ff on #dbe8fb1.46 : 1 #193cb8 on #dbe8fb7.11 : 1
dark #8ec5ff on #0e1c2f — 9.48 : 1 #8ec5ff on #0e1c2f9.48 : 1, unchanged

Sweeping every accent-coloured node on the studio screen: zero below AA in light, worst 4.51:1.

The measurement is now a gate

Every contrast figure this repo has ever recorded is prose — charts/palette.ts's header, AgentRail.tsx:2583, the login showcase comment. Prose does not fail when the value it describes moves, which is exactly how a dark-tuned token survived into a light theme for four months.

tests/helpers/contrast.ts does the arithmetic the browser does (oklch → Oklab → clipped sRGB, compositing in the gamma space, luminance in the linear one) and reads the Tailwind palette out of node_modules rather than transcribing it. theme-accent-contrast.test.ts re-measures every token on every ground each run; theme-token-usage.test.ts fails on a colour literal in src and on a token nothing reaches.

Both were mutation-probed: reverting the light accent to blue-300, moving a dark value one step, collapsing teal-alt onto emerald-alt, colliding amber with orange, lightening a solid below AA, and dropping a token from the @theme map each fail; the unmutated tree is clean.

What an independent review found, and what it changed

A five-lens adversarial review raised 49 findings; 17 survived refutation. Three mattered:

The Oklab matrix in my own helper was wrong. The a-row had -0.2429228246 where Ottosson has -2.4285922050, with the sign on the s term flipped. Contrast never touches Oklab, so every ratio stayed correct — but every separation figure was measured in a space that is not Oklab, including the ones that chose the identity palette. Corrected, verified against Ottosson's published values, and given a round-trip check. In the right space the natural assignment wins: hue-amber returns to amber-800 (the step warning already uses) and yellow's alt to -900; orange moves to -900 instead, because Tailwind's amber ramp rotates into orange as it darkens.

Three error pages hovered the wrong way. They used bg-blue-600 hover:bg-blue-700 — hover darkens — where the rest of the app lightens. Flattening them onto -solid-hover inverted the direction and took white-on-hover from 6.8:1 to 3.8:1. They get -solid-active, and a new test measures what a filled control's label survives; the AA sweep had excluded solids as grounds, so nothing did.

Nine monitoring grounds repainted in dark. bg-green-500bg-success-tint is emerald, not green; bg-yellow-500bg-warning-tint is amber. A hue change in dark, outside the stated exception. They now take the identity hues that reproduce them exactly.

Also fixed: two assertions that compared an object to itself and could never fail, a GROUNDS.dark that was never read, a tint ceiling asserted rather than derived (four live sites use /25, one of them the pill this issue is about — the ladder is now scanned out of src), and a word boundary that let bg-brand-tint satisfy --studio-brand.

Deliberately not done

  • Filled warning / success / teal buttons carry a white label at 3.2–3.7:1. Under AA, in both themes, and under AA before this migration. Darkening them is a visible change to a control Accent colours are not tokenised, so they keep dark-tuned values on light grounds #402 never claimed. Pinned by name in the suite so the deferral is a fact rather than a sentence; when it is fixed, that test fails and says so.
  • Gradients, blur orbs and glow shadows stay literal. Never read as text. The one place a gradient is the letterform (the login hero's bg-clip-text headline) sits inside a subtree that re-declares the dark palette, so the light ramp never reaches it.
  • Colour that no CSS token can reach: a Recharts/SVG presentation attribute, Monaco's theme object, and the per-connection colour a user picks (with its hardcoded hex fallback).
  • Hue drift between vocabularies. The monitoring surfaces say green/yellow where the studio says emerald/amber, and SchemaDiff likewise. Preserving dark meant preserving the drift. Worth a follow-up; not worth changing dark pixels here.

Verification

format · lint (134 warnings, vs 135 on main) · typecheck · knip · chart:check · channels:showcase:check · readme:check · security:check · test (10771 pass, 0 fail) · build · build:lib + attw · coverage 45983/45983 lines (100.00%).

cc @omerfarukbolat — you filed this; the calibration in it is what made it tractable. Two numbers in the table did not reproduce (noted above), and the tint effect turned out to be the thing that decides which step works.

Every contrast figure in the repository is prose: measured once by hand and
written into a comment. Prose does not fail when the value it describes moves,
which is how a token tuned against a near-black ground survived into a light
theme at 1.46:1 (#402).

tests/helpers/contrast.ts does the arithmetic the browser does — oklch through
Oklab to clipped sRGB, alpha compositing in the gamma-encoded space, WCAG
luminance in the linear one — and reads the Tailwind palette out of node_modules
rather than transcribing it, so a Tailwind upgrade that restyles blue-400
reports itself as the dark-mode change it is.

theme.css gains the accent layer #384 left out of scope: four state roles with
two text steps, a wash and a filled ground each, plus a fifteen-hue identity
palette selected per mode the way charts/palette.ts selects. Light floor 4.79:1,
dark floor 4.97:1, and the light identity set is no tighter anywhere than the
dark set it is modelled on.
…colour (#402)

697 colour literals across 68 files move onto the token layer #384 built. Two
families, because colour here answers two different questions: brand/warning/
success/danger where it tracks a condition, and a fifteen-hue identity palette
where it labels a thing — which engine, which panel, added vs removed, primary
key vs foreign key.

The state family is brand, not accent. shadcn owns --accent (its neutral hover
ground, #f5f5f5) and globals.css maps --color-accent to it AFTER importing the
studio layer, so a token of that name loses the cascade in silence and paints
near-white text on a white page. The compiled stylesheet is the only place that
shows; no unit test can see it.

Dark is a byte-for-byte no-op except where a -500 text literal was drift — the
codebase used three steps of one hue for one meaning, and those collapse onto
the base token. Every such site is listed in the PR.

Also: the tests that asserted a hue name now assert the token, and are renamed
where the name said 'returns green'.
…nverted hover, and nine repainted dark grounds

Three defects an independent review found, none of which any gate could see.

The Oklab a-row in the contrast helper was transcribed wrong: -2.4285922050
became -0.2429228246 and the sign on the s term flipped. Contrast never touches
Oklab so every ratio stayed correct, but every separation figure was measured in
a space that is not Oklab — including the ones that chose the palette. Corrected
against Ottosson's published values, with a round-trip check so the next slip
fails instead of measuring quietly. In the right space the natural assignment
wins: hue-amber returns to amber-800 (the step warning already uses) and
yellow's alt to -900; orange moves to -900 instead, because Tailwind's amber
ramp rotates into orange as it darkens.

Three error pages hovered DOWN the ramp where the rest of the app hovers up.
Flattening them onto -solid-hover inverted the direction and took white-on-hover
from 6.8:1 to 3.8:1. They get -solid-active, and a test now measures what a
filled control's label survives — the AA sweep had excluded solids as grounds,
so nothing did.

Nine monitoring grounds and borders were routed through success/warning, which
are emerald and amber, while the literals were green and yellow: a hue change in
dark, outside the stated exception. They take the identity hues that reproduce
them exactly. A pair-by-pair audit of the whole diff now reports 93 differences,
all of them the sanctioned -500/-200 text drift, and zero others.

Also: two assertions that compared an object to itself, a GROUNDS.dark that was
never read, a tint ceiling asserted rather than derived (four live sites use /25,
one of them the pill this issue is about), and a word boundary that let
bg-brand-tint satisfy --studio-brand.
@cevheri cevheri added the enhancement New feature or request label Sep 6, 2026
@codecov

codecov Bot commented Sep 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Foreground opacity and a fixed black preview surface still create untested light-mode contrast failures.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Migrates accent colours to theme-aware state and identity tokens, improving light-mode contrast while preserving dark-mode styling and package consumers.

Changes:

  • Adds and documents shared accent tokens.
  • Migrates UI colour literals to semantic utilities.
  • Adds contrast, token-usage, and regression tests.
File summaries
File Description
docs/ui/theming.md Documents accent tokens and validation.
src/app/admin/error.tsx Tokenises error action colour.
src/app/error.tsx Tokenises error action colour.
src/app/login/login-form.tsx Tokenises login accents.
src/app/not-found.tsx Tokenises navigation action colour.
src/components/CodeGenerator.tsx Tokenises generator accents.
src/components/CommandPalette.tsx Tokenises command icons and states.
src/components/ConnectionModal.tsx Tokenises connection UI accents.
src/components/CreateTableModal.tsx Tokenises form and preview accents.
src/components/DataCharts.tsx Tokenises chart controls.
src/components/DataImportModal.tsx Tokenises import workflow states.
src/components/DataProfiler.tsx Tokenises profiler states and identities.
src/components/DatabaseDocs.tsx Tokenises documentation panel colours.
src/components/MaskingSettings.tsx Tokenises masking UI accents.
src/components/MobileNav.tsx Tokenises active navigation state.
src/components/PivotTable.tsx Tokenises pivot controls and values.
src/components/QueryEditor.tsx Tokenises editor actions.
src/components/QueryHistory.tsx Tokenises history states and controls.
src/components/QuerySafetyDialog.tsx Tokenises risk-level styling.
src/components/ResultsGrid.tsx Tokenises grid interactions and values.
src/components/SaveQueryModal.tsx Tokenises save-dialog controls.
src/components/SavedQueries.tsx Tokenises saved-query actions.
src/components/SchemaDiagram.tsx Tokenises diagram controls and warnings.
src/components/SchemaDiff.tsx Tokenises schema-difference identities.
src/components/SnapshotTimeline.tsx Tokenises selection and deletion states.
src/components/Studio.tsx Tokenises handles and warning dialog.
src/components/TestDataGenerator.tsx Tokenises generator colours.
src/components/VisualExplain.tsx Tokenises plan and AI-analysis colours.
src/components/admin/AdminDashboard.tsx Tokenises admin navigation.
src/components/admin/tabs/AuditTab.tsx Tokenises audit states.
src/components/admin/tabs/OperationsTab.tsx Tokenises operation accents.
src/components/admin/tabs/OverviewTab.tsx Tokenises overview accents.
src/components/admin/tabs/SecurityTab.tsx Tokenises security controls.
src/components/agent/AgentRail.tsx Tokenises agent states and controls.
src/components/agent/AnswerCard.tsx Tokenises answer states and identities.
src/components/agent/ConsentCard.tsx Tokenises consent states.
src/components/agent/SafetyStrip.tsx Tokenises posture tones.
src/components/community-section.tsx Tokenises focus rings.
src/components/copy-button.tsx Tokenises copy-failure state.
src/components/login/connection-signature.tsx Tokenises signature accent.
src/components/monitoring/MonitoringDashboard.tsx Tokenises refresh indicator.
src/components/monitoring/tabs/OverviewTab.tsx Tokenises monitoring summary icons.
src/components/monitoring/tabs/PerformanceTab.tsx Tokenises health states.
src/components/monitoring/tabs/PoolTab.tsx Tokenises pool metrics.
src/components/monitoring/tabs/QueriesTab.tsx Tokenises slow-query warning.
src/components/monitoring/tabs/SessionsTab.tsx Tokenises session states.
src/components/monitoring/tabs/StorageTab.tsx Tokenises storage identities.
src/components/monitoring/tabs/TablesTab.tsx Tokenises vacuum states.
src/components/results-grid/ResultCard.tsx Tokenises result-card accents.
src/components/results-grid/RowDetailSheet.tsx Tokenises copy and masking states.
src/components/results-grid/StatsBar.tsx Tokenises result statistics and actions.
src/components/results-grid/renderers/binary.ts Tokenises binary values.
src/components/results-grid/renderers/json.ts Tokenises JSON values.
src/components/results-grid/renderers/scalar.ts Tokenises scalar values.
src/components/schema-diagram/TableNode.tsx Tokenises schema-node identities.
src/components/schema-explorer/ColumnList.tsx Tokenises key and index icons.
src/components/schema-explorer/SchemaExplorer.tsx Tokenises explorer controls.
src/components/schema-explorer/TableItem.tsx Tokenises table actions.
src/components/sidebar/ConnectionItem.tsx Tokenises connection states.
src/components/sidebar/Sidebar.tsx Tokenises sidebar indicators.
src/components/studio/BottomPanel.tsx Tokenises panel identities and notices.
src/components/studio/QueryToolbar.tsx Tokenises query states and actions.
src/components/studio/StudioDesktopHeader.tsx Tokenises desktop header states.
src/components/studio/StudioMobileHeader.tsx Tokenises mobile header states.
src/components/studio/StudioTabBar.tsx Tokenises active tab styling.
src/lib/db-ui-config.ts Assigns tokenised engine identities.
src/lib/monitoring-thresholds.ts Returns tokenised threshold borders.
src/styles/theme.css Defines light and dark accent palettes.
src/workspace/StudioWorkspace.tsx Tokenises embedded workspace accents.
tests/components/CommandPalette.test.tsx Updates palette mocks.
tests/components/ConnectionModal.mobile.test.tsx Updates mobile colour mocks.
tests/components/ConnectionModal.test.tsx Updates connection colour mocks.
tests/components/DataProfiler.test.tsx Verifies profiler tokens.
tests/components/MaskingSettings.test.tsx Verifies danger styling.
tests/components/QueryHistory.test.tsx Verifies history tokens.
tests/components/QuerySafetyDialog.test.tsx Verifies risk tokens.
tests/components/ResultsGrid.test.tsx Updates edit-border selector.
tests/components/SchemaDiagram.test.tsx Verifies diagram highlights.
tests/components/SchemaDiff.test.tsx Verifies identity tints.
tests/components/SnapshotTimeline.test.tsx Verifies selection tokens.
tests/components/VisualExplain.test.tsx Verifies explanation tokens.
tests/components/admin/OperationsTab.test.tsx Updates engine-colour mock.
tests/components/admin/OverviewTab.test.tsx Updates engine-colour mocks.
tests/components/agent/AgentRail.test.tsx Updates token terminology.
tests/components/monitoring/PerformanceTab.test.tsx Verifies monitoring tokens.
tests/components/results-grid/RowDetailSheet.test.tsx Verifies copy-state tokens.
tests/components/schema-explorer/ColumnList.test.tsx Verifies key token.
tests/components/sidebar/ConnectionItem.test.tsx Verifies active token.
tests/components/sidebar/ConnectionsList.test.tsx Verifies list token.
tests/components/studio/BottomPanel.test.tsx Verifies panel identity.
tests/components/studio/QueryToolbar.test.tsx Verifies toolbar states.
tests/components/studio/StudioDesktopHeader.test.tsx Verifies header states.
tests/components/studio/StudioTabBar.test.tsx Verifies active tab token.
tests/helpers/contrast.ts Adds colour and contrast arithmetic.
tests/unit/components/column-list.test.tsx Updates key-colour assertions.
tests/unit/components/results-grid-renderers.test.ts Updates renderer expectations.
tests/unit/components/results-grid-utils.test.ts Updates value-colour expectations.
tests/unit/components/studio-mobile-header.test.tsx Updates mobile-header assertions.
tests/unit/lib/monitoring-thresholds.test.ts Verifies threshold token output.
tests/unit/theme-accent-contrast.test.ts Adds accent contrast gates.
tests/unit/theme-token-usage.test.ts Prevents literals and unused tokens.
Review details
  • Files reviewed: 101/101 changed files
  • Comments generated: 12
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/components/CreateTableModal.tsx Outdated
@@ -243,12 +243,12 @@ export function CreateTableModal({ isOpen, onClose, onTableCreated }: CreateTabl
<div className="p-4 rounded-lg bg-black border border-hairline font-mono">
col.faker.generator === "autoIncrement"
? "bg-overlay text-fg-subtle line-through"
: "bg-amber-500/10 text-amber-400/80",
: "bg-hue-amber-tint/10 text-hue-amber/80",
{node["Filter"] && (
<div className="flex items-start gap-2 py-1 text-xs">
<span className="text-amber-500/70 font-medium shrink-0">Filter:</span>
<span className="text-hue-amber/70 font-medium shrink-0">Filter:</span>
<div className="flex items-center gap-2 py-1 text-xs">
<span className="text-emerald-500/70 font-medium">Index:</span>
<span className="text-emerald-400 font-mono">{node["Index Name"]}</span>
<span className="text-hue-emerald/70 font-medium">Index:</span>
data-testid="agent-handover-declined"
className="mt-0.5 pl-3.5 text-xs text-amber-400/80"
>
<p key={declined.id} data-testid="agent-handover-declined" className="mt-0.5 pl-3.5 text-xs text-warning/80">
Comment on lines +145 to +146
function worstGround(palettes: Map<string, string>, token: string, ownTint: string): { ratio: number; where: string } {
const foreground = rgb(palettes, token);
Comment thread docs/ui/theming.md Outdated
Comment on lines +58 to +59
| Accent, state | `brand` · `warning` · `success` · `danger`, each with `-bright`, `-tint`, `-solid`, `-solid-hover` |
| Accent, identity | `hue-<name>`, one per hue the app uses, some with `-alt` (a second step) and `-tint` (its wash) |
Comment thread docs/ui/theming.md Outdated
Comment thread docs/ui/theming.md Outdated
Comment on lines +379 to +380
- **4.5:1** on the five studio grounds, on a wash of its own hue up to `/20`, and on the brand
tile — WCAG AA for text, in both palettes.
Comment thread src/styles/theme.css Outdated
…ht on a black ground

Two external reviews on #607. One found a real regression, one found a real hole
in the gate; the smaller items are fixed here too.

CreateTableModal's SQL preview is a terminal: a hard `bg-black` in both themes.
Routing its text through a mode-aware token made light resolve blue-700 on black
— 5.27:1 on main, 2.30:1 here. It is now scoped to the dark palette the way the
login hero scopes its own pinned-dark column, and measures 5.27:1 again.

The contrast gate only ever measured an OPAQUE foreground, so it could not see
`text-warning/80` and its 45 siblings: an opacity modifier composites before
anyone reads it, and a token that clears AA at full strength can fall under it
faded. The suite now measures those, and holds them to the rule the rest of the
issue works to — light no worse than dark — with the sites that still fail pinned
by name rather than described in a sentence. Measured against main, this
migration improved every one of those 38 sites in light (worst 1.35:1 → 6.80:1)
and regressed none, which is why they are a second failure class rather than this
one.

Also: the Explain button's hover had collapsed onto its own base, because the
sanctioned -500 text drift moved the base to what the hover used to be;
getThresholdColor drew two of three rings from the identity family and one from
the state family, at identical values, so the mix was invisible and is now
asserted against; the mode-independence check enumerated suffixes by hand and so
skipped brand-solid-active entirely; and both the token comment and the theming
doc said nineteen engines where DatabaseType has seventeen.
@cevheri

cevheri commented Sep 6, 2026

Copy link
Copy Markdown
Member Author

Both reviews checked against the code and re-measured rather than taken on trust. Between them they found one real regression and one real hole in the gate; both are fixed in 0856ac9, along with five smaller confirmed items.

Confirmed and fixed

The regression — @copilot-pull-request-reviewer on CreateTableModal.tsx:243. Correct, and the only regression in the PR. That preview is a terminal: a hard bg-black in both themes, so a mode-aware token resolves blue-700 on black in light. Measured 5.27:1 on main → 2.30:1 here. Now scoped to the dark palette the way the login hero scopes its own pinned-dark column, and re-measured in Chrome at 5.27:1, exactly main's value. The other bg-black/60 hits are modal scrims and carry no text.

The gate hole — foreground opacity. Also correct: worstGround measured the token opaque, so text-warning/80 and its 45 siblings were invisible to it. The suite now composites foreground alpha.

What the measurement says once it exists, though, refutes the framing that these "reintroduce the contrast defect the migration is intended to remove". Against main, every one of the 38 faded sites improved in light and none regressed:

site light before → after dark before → after
AgentRail:719 amber-400/80warning/80 1.50 → 4.45 7.55 → 7.53
VisualExplain:310 amber-500/70hue-amber/70 1.68 → 3.59 4.91 → 5.94
VisualExplain:317 emerald-500/70hue-emerald/70 1.87 → 3.68 4.35 → 5.34
ConsentCard:248 amber-400/70warning/70 1.44 → 3.59 5.96 → 5.94
TestDataGenerator:293 amber-400/80hue-amber/80 1.50 → 4.45 7.55 → 7.53
TableNode:72 red-500/60hue-red/60 2.39 → 3.63 2.45 → 3.06

21 are still under AA, and 9 of those fail in dark too, at values byte-identical to what shipped before this PR. By the issue's own calibration — "accent-on-accent-tint is the one failure class that light has and dark does not" — faded text is a second, symmetric class, and clearing it needs a per-mode alpha (a faded token whose dark value carries the alpha and whose light value is opaque), which is its own design. So: measured, held to light no worse than dark, and the survivors pinned by name so the list can only shrink and the day one is fixed the suite says so. Filing the follow-up.

Docs — all three correct. /20 was stale (the implementation scans to /25); the token summary omitted brand-solid-active and the teal/purple -solid pairs; and "nineteen engines" was wrong in two places — DatabaseType has 17, as you said.

@deepseek — all three correct.

  • QueryEditor:608: the hover really had collapsed onto its base, and the cause is worth recording — the sanctioned -500 text drift moved the base to the exact value the hover used to be, so a disclosed change silently consumed an undisclosed one. Now hover:text-warning-bright. I swept for others with a backreference and this is the only one; my first sweep reported ten, which was my own regex matching hover:bg-x-solid inside hover:bg-x-solid-hover.
  • getThresholdColor: two rings from the identity family, one from state, at identical values — invisible, which is exactly why it would have survived. All three are hue-* now, plus an assertion that the three come from one vocabulary.
  • -solid-active missing from the mode-independence loop: right, and the hand-written suffix list was the defect. It now derives the ground set from the declarations, so a token added later cannot be skipped again.

One correction

@deepseek: No bugs blocking merge — all findings are polish items.

The bg-black preview was a genuine light-mode regression (5.27 → 2.30), which a review that verified "the dark-mode no-op invariant holds" would not surface: dark was fine. Worth noting because the two reviews were complementary — the invariant check confirmed what this PR promised, and the call-site read found what it broke.

Verification

All three new contracts were mutation-probed: flipping -solid-active to lighten, tuning a ground in one palette only, and making a faded site worse each fail; the unmutated tree is clean. Gates green — format · lint · typecheck · knip · drift guards · test · build · coverage 45983/45983 (100.00%).

…lesystem order

CI failed on a list with identical membership and a different order. The scans
that read `src` walked it with `readdirSync`, which returns filesystem order, so
every list built on them was machine-ordered: the pinned set happened to match
the order of the machine it was written on and matched nothing else. Locally it
was green, which is the worst version of this bug — the suite was reporting the
developer's inode order as a fact about the codebase.

Sorted at the walk rather than only at the assertion, in both files that do it,
so nothing else built on those scans inherits it. Verified by reversing the walk
and re-running: still green, which is the control that a sort matching one
machine would not have passed.
@cevheri cevheri self-assigned this Sep 6, 2026
@sonarqubecloud

sonarqubecloud Bot commented Sep 6, 2026

Copy link
Copy Markdown

@cevheri
cevheri merged commit a337c83 into main Sep 6, 2026
27 checks passed
@cevheri
cevheri deleted the feat/tokenise-accent-colours branch September 6, 2026 21:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Accent colours are not tokenised, so they keep dark-tuned values on light grounds

2 participants