Skip to content

feat(tokens): snap near-miss colours onto their token only when the hue holds - #1193

Merged
oddharsh merged 12 commits into
mainfrom
claude/near-miss-snap
Oct 6, 2026
Merged

oddharsh merged 12 commits into
mainfrom
claude/near-miss-snap

Conversation

@oddharsh

@oddharsh oddharsh commented Oct 6, 2026

Copy link
Copy Markdown
Owner

Decision C from the palette proposal. Stacked on #1190.

1,054 literals sat 0.004 to 0.02 from a token. The proposal's prototype snapped all of them, and /whoareyou's content pane turned cream. This version snaps the ones whose colour holds and leaves the rest.

What turned the pane cream

Two causes, both measured:

  • 357 of the 1,054 were white. White is four tokens' value, so the contract never calls it a copy, and the prototype read #fff as a near-miss of the warm --paper (dE 0.011). White and black are never a near-miss of one token now.
  • Hue moves change temperature below the JND. A cool white onto warm --paper, or a neutral grey picking up --row-alt's blue tint, reads warmer or cooler even when the total distance is small.

The rule

Each near-miss is split into its lightness, chroma and hue parts. It snaps only when the hue part is under 0.003 and the chroma change under 0.004.

rule snapped held
distance only (the prototype, minus white) 705 0
hue part < 0.003 507 198
hue < 0.003 and chroma change < 0.004 (this PR) 238 400

What snaps: greys onto the ink ramp, blue-greys onto --surface-desktop and --rule, a lighter --row-alt. What holds: anything that would change temperature, including the green status variants (29) whose chroma differs from --go.

Changes

  • New --paper-cool, oklch(98% 0.004 calc(var(--hue-luna) - 13)): the cool twin of the warm --paper. The site already wrote it as a hand-mixed near-white in 18 files. 22 copies swept onto it.
  • 252 literals in 55 source files now name a token, garage and lwe spec CSS included (pageCss/demoCss lines only, never demoJs). Every generated page was regenerated.
  • 400 near-misses stay literal. colors:drift keeps listing them.

Rendered

Against #1190, on a local build of the same nine pages as the proposal:

page pixels changed changed by > 6 levels largest move
/whoareyou 2.09% 0% 5
/writing 1.14% 0% 5
/garage/vigenere, /serendipity, /updates 0.06 to 0.22% 0% 3 to 5
/garage/horizon, /garage/tooltips, / 0% 0% 0
/access 15.94% 0% 6

Nothing anywhere moves by more than 6 of 255 levels. The all-in prototype moved up to 60% of a page past that.

Checks

check:fast (1,234 pass, lint clean), build (invariants), derive:check, pages:check, routes:check (337 routes). The colour contract passes with --paper-cool in the token set.

luna.css changes, so its /a/ URL re-mints.

typography.css has carried a pt scale and three family stacks since the
design system was written. On 2026-10-06 the scale had 23 consumers against
1,072 hand-written sizes in 70 distinct values, and 55 family stacks were
spelled out by hand (31 of them "Courier New",monospace, the mono token with
its fallbacks dropped).

- The scale grows to what the site sets: --text-lg 13pt, --text-lede 11pt,
  --text-2xs 8pt, --text-3xs 7.5pt, --text-4xs 7pt. 8pt alone had 98 uses.
- 989 declarations in the sources rewritten to tokens; generated garage and
  lwe pages regenerated from their specs and generators.
- 121 off-grid declarations snapped to the nearest step, ties down. Most move
  0.2pt or less (8.6 and 8.4 -> 8.5, 8.8 -> 9, 8.2 and 7.8 -> 8). Six move
  0.4-0.5pt: 12.5 -> 12 and 11.5 -> 11 (two each), 6.5 and 6.6 -> 7.
- contract-type-comes-from-the-tokens refuses a pt size up to 14pt or a family
  stack outside var(--font-*). px (pixel-exact chrome, SVG text), relative
  units and display sizes over 14pt stay legal; three rules say @type demo
  (horizon's console caret, the polaroid's handwriting, lwe's CJK avatar).
- The homepage loads luna.css without blocking, so it inlines the five size
  tokens it reads; the contract holds that copy to typography.css.
- cssSources() now reads lwe specs' demoCss, which neither contract saw.
- colors.css's --text-body: var(--ink) is gone. typography.css's --text-body
  (10.5pt) loads after it in luna.css, so the colour alias never applied.

Checked in a browser: no element on 8 sampled pages computes to the 16px
default; /garage/useragent's size histogram moves exactly by the snaps.
…s they hid

The token contract read oklch() only, and the site writes most of its colour
in hex: 2,248 hex and 82 rgb() literals on 2026-10-06. Measured against the
tokens by OKLab distance, 87 were a token retyped in sRGB (#f3f6fa for
--row-alt 51 times over, #888 for --ink-faint, #e8eef7 for --surface-desktop).

- colorLiteralsIn() reads oklch(), hex and rgb()/rgba() with one set of
  guards: SVG presentation attributes, and for the sRGB spellings anything
  outside a declaration value, so #fab in a selector or a hex in a comment
  (access's note on why its greys moved) is never a colour.
- tokenCopiesIn() and colors:drift both use it. The report now covers 3,884
  literals and lists 156 colours hand-written in 3+ files with no token.
- 84 copies swept to var(): 55 in pages, stylesheets and Workers, 29 inside
  garage and lwe spec CSS (pageCss/demoCss lines only, never demoJs), with
  every generated page regenerated. Two whole gradients were --grad-raised
  stop for stop (luna's button face, serendipity's) and now say so.

Invisible: every swap is under dE 0.004 of its token.
…omments name

colors.css said its values "resolve to period-correct Luna" and never
measured it. They came in with a generated design system on 2026-06-05 and
sat up to dE 0.040 from their hex (--darkshadow vs #716F64) and 0.042 for the
title blues (--blue-95 vs #3D95FF): every chrome colour darker and greyer
than XP, with round-number lightness that read like an estimate.

- Chrome: --hue-chrome 96.85, --chroma-chrome 0.0228, exact lightness and
  chroma per token. One knob still drives all four; each lands within
  dE 0.0008 of ButtonFace, ButtonLight, ButtonShadow and the 3D dark shadow.
- Title blues: --chroma-luna 0.2274 and a per-token hue offset (the ramp
  already used them) put --blue-65, --blue-95, --blue-inactive and --blue-50
  within dE 0.0001 of ActiveTitle, GradientActiveTitle, InactiveTitle and
  Highlight. Every title bar and the selection colour move with this.
- The contract now reads a hex in a token's comment as a claim and holds it
  within dE 0.002. Control: the old values fail it on seven tokens.
- With the tokens on XP, 12 hand-typed XP hex values became copies and are
  swept (#316ac5, #ece9d8, #aca899, #0054e3).
- A var() fallback is now legal for a colour literal, as it already was for
  a font stack. luna's client edge says var(--face,#ece9d8): build.ts mirrors
  it into the homepage, which paints before luna.css defines --face, and a
  bare var() would drop the 6px border the mirror exists to keep. build.ts's
  extractor and its homepage check accept that form.

Rendered (local build, 9 pages): the chrome retune moves 0 to 0.4% of
pixels on 8 pages by at most 5/255; the title blues move about 2% (the title
bars) by up to 28/255.
…ue holds

Decision C from the palette proposal. 1,054 literals sat 0.004 to 0.02 from
a token, and the prototype that snapped all of them turned /whoareyou's
content pane cream. Two causes, both measured:

- 357 of them were white. White is four tokens' value, so the contract never
  calls it a copy, and the prototype read #fff as a near-miss of the warm
  --paper. White and black are never near-misses of one token now.
- A move that changes hue, or puts a tint on a neutral, changes temperature
  even under the just-noticeable difference. Each near-miss is split into its
  lightness, chroma and hue parts, and it snaps only when the hue part is
  under 0.003 and the chroma change under 0.004: greys onto the ink ramp,
  blue-greys onto --surface-desktop and --rule, a lighter --row-alt.

- New --paper-cool, oklch(98% 0.004) at --hue-luna - 13: the cool twin of
  the warm --paper, which the site already wrote as a hand-mixed near-white
  in 18 files. 22 copies of it swept.
- 252 literals in 55 source files now name a token, garage and lwe spec CSS
  included (pageCss/demoCss lines only); every generated page regenerated.
- 400 near-misses stay literal because the move would shift their hue or
  tint. colors:drift keeps listing them.

Rendered against #1190 (local build, 9 pages): 0 to 2.1% of pixels move on
eight pages and 15.9% on /access, and nothing anywhere moves by more than 6
of 255 levels. The all-in prototype moved up to 60% of a page past that.
@oddharsh oddharsh added the hashed-asset Remints an /a/ URL, so every page and page dictionary rebuilds label Oct 6, 2026
@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Wire-size diff vs merge base

5cc1cc6d8d56e16500339ed46ee447da2aadd95a → 6e33ba8a96e37b0d03be7df724d7af669445183f

Brotli q11, the quality the edge serves. Every number here is deterministic:
an unchanged file produces no row. Advisory — this check fails on nothing.

Client assets

Total Brotli: 194.28 KiB → 194.27 KiB (-0.01 KiB, -0.0%) across 37 files, 8 changed.

file base head Δ
infotip.css 0.68 KiB 0.66 KiB -0.02 KiB (-2.7%)
nav-tray.css 0.98 KiB 0.97 KiB -0.01 KiB (-1.3%)
nav.js 6.11 KiB 6.12 KiB +0.01 KiB (+0.2%)
quiz.js 2.37 KiB 2.38 KiB +0.01 KiB (+0.2%)
luna.css 7.86 KiB 7.86 KiB +0.00 KiB (+0.1%)
quiz.css 0.78 KiB 0.78 KiB -0.00 KiB (-0.4%)
lwe-base.css 1.20 KiB 1.20 KiB +0.00 KiB (+0.2%)
nav-run.css 1.65 KiB 1.65 KiB +0.00 KiB (+0.1%)

Pages

Total Brotli: 594.31 KiB → 594.06 KiB (-0.25 KiB, -0.0%) across 70 files, 64 changed.

No single file moved by 0.13 KiB or more.

Plus 64 files under the 0.13 KiB floor, net -0.25 KiB — usually content-hash churn in /a/ references, counted in the total above.

Worker bundle

268.89 KiB → 268.78 KiB gzip (-0.11 KiB, -0.0%)

Largest module deltas (raw bytes in bundle):

file base head Δ
.build/src/worker/lens.ts 133.24 KiB 132.99 KiB -0.25 KiB (-0.2%)
.build/src/worker/writing.ts 11.00 KiB 10.94 KiB -0.06 KiB (-0.6%)
.build/src/worker/census.ts 14.60 KiB 14.56 KiB -0.04 KiB (-0.3%)
.build/src/worker/around.ts 14.87 KiB 14.86 KiB -0.02 KiB (-0.1%)
.build/src/worker/reading.ts 7.38 KiB 7.36 KiB -0.02 KiB (-0.2%)
.build/src/worker/updates.ts 12.80 KiB 12.82 KiB +0.02 KiB (+0.1%)
.build/serendipity/serendipity.ts 89.11 KiB 89.09 KiB -0.01 KiB (-0.0%)
.build/src/worker/ledger.ts 13.45 KiB 13.43 KiB -0.01 KiB (-0.1%)
.build/src/worker/dyno.ts 9.03 KiB 9.04 KiB +0.01 KiB (+0.1%)
.build/src/worker/inbox.ts 6.80 KiB 6.80 KiB +0.01 KiB (+0.1%)
… 6 more

Dictionary transport

Family dictionary acquisition: no change, 14.41 KiB q11.

pd/: 344 → 344 deltas, 628.56 KiB → 638.07 KiB (+9.51 KiB).

…omments name

colors.css said its values "resolve to period-correct Luna" and never
measured it. They came in with a generated design system on 2026-06-05 and
sat up to dE 0.040 from their hex (--darkshadow vs #716F64) and 0.042 for the
title blues (--blue-95 vs #3D95FF): every chrome colour darker and greyer
than XP, with round-number lightness that read like an estimate.

- Chrome: --hue-chrome 96.85, --chroma-chrome 0.0228, exact lightness and
  chroma per token. One knob still drives all four; each lands within
  dE 0.0008 of ButtonFace, ButtonLight, ButtonShadow and the 3D dark shadow.
- Title blues: --chroma-luna 0.2274 and a per-token hue offset (the ramp
  already used them) put --blue-65, --blue-95, --blue-inactive and --blue-50
  within dE 0.0001 of ActiveTitle, GradientActiveTitle, InactiveTitle and
  Highlight. Every title bar and the selection colour move with this.
- The contract now reads a hex in a token's comment as a claim and holds it
  within dE 0.002. Control: the old values fail it on seven tokens.
- With the tokens on XP, 12 hand-typed XP hex values became copies and are
  swept (#316ac5, #ece9d8, #aca899, #0054e3).
- A var() fallback is now legal for a colour literal, as it already was for
  a font stack. luna's client edge says var(--face,#ece9d8): build.ts mirrors
  it into the homepage, which paints before luna.css defines --face, and a
  bare var() would drop the 6px border the mirror exists to keep. build.ts's
  extractor and its homepage check accept that form.

Rendered (local build, 9 pages): the chrome retune moves 0 to 0.4% of
pixels on 8 pages by at most 5/255; the title blues move about 2% (the title
bars) by up to 28/255.
@oddharsh
oddharsh added this pull request to stack #1194 October 6, 2026 19:29
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
aadhar-sh 371e107 Commit Preview URL

Branch Preview URL
Oct 06 2026, 07:56 PM

…aseline

parseOklch and parseSrgb return null for input they cannot read, and the
test used both results without a check: two TS2531 errors that validate's
typecheck refused against config/ts-baseline.json. Assert both parsed, then
use them.
parseOklch and parseSrgb return null on a miss, so typecheck refused the
hex-claim test against the baseline. Assert both parsed, then measure.
…omments name

colors.css said its values "resolve to period-correct Luna" and never
measured it. They came in with a generated design system on 2026-06-05 and
sat up to dE 0.040 from their hex (--darkshadow vs #716F64) and 0.042 for the
title blues (--blue-95 vs #3D95FF): every chrome colour darker and greyer
than XP, with round-number lightness that read like an estimate.

- Chrome: --hue-chrome 96.85, --chroma-chrome 0.0228, exact lightness and
  chroma per token. One knob still drives all four; each lands within
  dE 0.0008 of ButtonFace, ButtonLight, ButtonShadow and the 3D dark shadow.
- Title blues: --chroma-luna 0.2274 and a per-token hue offset (the ramp
  already used them) put --blue-65, --blue-95, --blue-inactive and --blue-50
  within dE 0.0001 of ActiveTitle, GradientActiveTitle, InactiveTitle and
  Highlight. Every title bar and the selection colour move with this.
- The contract now reads a hex in a token's comment as a claim and holds it
  within dE 0.002. Control: the old values fail it on seven tokens.
- With the tokens on XP, 12 hand-typed XP hex values became copies and are
  swept (#316ac5, #ece9d8, #aca899, #0054e3).
- A var() fallback is now legal for a colour literal, as it already was for
  a font stack. luna's client edge says var(--face,#ece9d8): build.ts mirrors
  it into the homepage, which paints before luna.css defines --face, and a
  bare var() would drop the 6px border the mirror exists to keep. build.ts's
  extractor and its homepage check accept that form.

Rendered (local build, 9 pages): the chrome retune moves 0 to 0.4% of
pixels on 8 pages by at most 5/255; the title blues move about 2% (the title
bars) by up to 28/255.
parseOklch and parseSrgb return null on a miss, so typecheck refused the
hex-claim test against the baseline. Assert both parsed, then measure.
…ss-snap

#1190 was rebased onto main after #1181 and #1183 were squash-merged, so this
branch and its new base carried the same sweeps under different commits: a
plain merge conflicted in 61 files. Decision C is fully scripted, so it was
re-run on the new base instead (copies onto --paper-cool, then the hue-aware
snap, then spec CSS, then every generated page regenerated). The re-run
touches the same 72 files with the same counts (22 copies, 238 snaps, 44
spec literals), and this merge records that tree.
… claude/near-miss-snap

GitHub rebased #1190 again when #1191 and #1192 landed on main; #1192 edits
the pqc page this branch also touches, so the previous merge went stale.
Decision C was re-run on the new base by the same scripts, with the same
result (72 files; 22 copies, 238 snaps, 44 spec literals), and this merge
records that tree.
Base automatically changed from claude/xp-retune to main October 6, 2026 19:42
#1190 landed as a squash, so this branch carried its pre-squash commits and
conflicted with main. Decision C was re-run on main by the same scripts with
the same result (72 files; 22 copies, 238 snaps, 44 spec literals), and this
merge records that tree.
@oddharsh
oddharsh merged commit bf98224 into main Oct 6, 2026
7 checks passed
@oddharsh
oddharsh deleted the claude/near-miss-snap branch October 6, 2026 20:16
oddharsh added a commit that referenced this pull request Oct 6, 2026
#1193 squash-merged with a tree identical to this branch's base, so this
branch's own work (the 64 palette tokens, the grey and --red-46 merges, and
the email fix #1197 also landed) is replayed onto main as a three-way merge
with that commit as the merge base: no conflicts, nothing re-swept.
oddharsh added a commit that referenced this pull request Oct 6, 2026
…iles (#1196)

* feat(type): every pt size and font family names a typography token

typography.css has carried a pt scale and three family stacks since the
design system was written. On 2026-10-06 the scale had 23 consumers against
1,072 hand-written sizes in 70 distinct values, and 55 family stacks were
spelled out by hand (31 of them "Courier New",monospace, the mono token with
its fallbacks dropped).

- The scale grows to what the site sets: --text-lg 13pt, --text-lede 11pt,
  --text-2xs 8pt, --text-3xs 7.5pt, --text-4xs 7pt. 8pt alone had 98 uses.
- 989 declarations in the sources rewritten to tokens; generated garage and
  lwe pages regenerated from their specs and generators.
- 121 off-grid declarations snapped to the nearest step, ties down. Most move
  0.2pt or less (8.6 and 8.4 -> 8.5, 8.8 -> 9, 8.2 and 7.8 -> 8). Six move
  0.4-0.5pt: 12.5 -> 12 and 11.5 -> 11 (two each), 6.5 and 6.6 -> 7.
- contract-type-comes-from-the-tokens refuses a pt size up to 14pt or a family
  stack outside var(--font-*). px (pixel-exact chrome, SVG text), relative
  units and display sizes over 14pt stay legal; three rules say @type demo
  (horizon's console caret, the polaroid's handwriting, lwe's CJK avatar).
- The homepage loads luna.css without blocking, so it inlines the five size
  tokens it reads; the contract holds that copy to typography.css.
- cssSources() now reads lwe specs' demoCss, which neither contract saw.
- colors.css's --text-body: var(--ink) is gone. typography.css's --text-body
  (10.5pt) loads after it in luna.css, so the colour alias never applied.

Checked in a browser: no element on 8 sampled pages computes to the 16px
default; /garage/useragent's size histogram moves exactly by the snaps.

* fix(tokens): read hex and rgb() as colours, and sweep the token copies they hid

The token contract read oklch() only, and the site writes most of its colour
in hex: 2,248 hex and 82 rgb() literals on 2026-10-06. Measured against the
tokens by OKLab distance, 87 were a token retyped in sRGB (#f3f6fa for
--row-alt 51 times over, #888 for --ink-faint, #e8eef7 for --surface-desktop).

- colorLiteralsIn() reads oklch(), hex and rgb()/rgba() with one set of
  guards: SVG presentation attributes, and for the sRGB spellings anything
  outside a declaration value, so #fab in a selector or a hex in a comment
  (access's note on why its greys moved) is never a colour.
- tokenCopiesIn() and colors:drift both use it. The report now covers 3,884
  literals and lists 156 colours hand-written in 3+ files with no token.
- 84 copies swept to var(): 55 in pages, stylesheets and Workers, 29 inside
  garage and lwe spec CSS (pageCss/demoCss lines only, never demoJs), with
  every generated page regenerated. Two whole gradients were --grad-raised
  stop for stop (luna's button face, serendipity's) and now say so.

Invisible: every swap is under dE 0.004 of its token.

* fix(tokens): the chrome and title blues resolve to the XP hex their comments name

colors.css said its values "resolve to period-correct Luna" and never
measured it. They came in with a generated design system on 2026-06-05 and
sat up to dE 0.040 from their hex (--darkshadow vs #716F64) and 0.042 for the
title blues (--blue-95 vs #3D95FF): every chrome colour darker and greyer
than XP, with round-number lightness that read like an estimate.

- Chrome: --hue-chrome 96.85, --chroma-chrome 0.0228, exact lightness and
  chroma per token. One knob still drives all four; each lands within
  dE 0.0008 of ButtonFace, ButtonLight, ButtonShadow and the 3D dark shadow.
- Title blues: --chroma-luna 0.2274 and a per-token hue offset (the ramp
  already used them) put --blue-65, --blue-95, --blue-inactive and --blue-50
  within dE 0.0001 of ActiveTitle, GradientActiveTitle, InactiveTitle and
  Highlight. Every title bar and the selection colour move with this.
- The contract now reads a hex in a token's comment as a claim and holds it
  within dE 0.002. Control: the old values fail it on seven tokens.
- With the tokens on XP, 12 hand-typed XP hex values became copies and are
  swept (#316ac5, #ece9d8, #aca899, #0054e3).
- A var() fallback is now legal for a colour literal, as it already was for
  a font stack. luna's client edge says var(--face,#ece9d8): build.ts mirrors
  it into the homepage, which paints before luna.css defines --face, and a
  bare var() would drop the 6px border the mirror exists to keep. build.ts's
  extractor and its homepage check accept that form.

Rendered (local build, 9 pages): the chrome retune moves 0 to 0.4% of
pixels on 8 pages by at most 5/255; the title blues move about 2% (the title
bars) by up to 28/255.

* feat(tokens): snap near-miss colours onto their token only when the hue holds

Decision C from the palette proposal. 1,054 literals sat 0.004 to 0.02 from
a token, and the prototype that snapped all of them turned /whoareyou's
content pane cream. Two causes, both measured:

- 357 of them were white. White is four tokens' value, so the contract never
  calls it a copy, and the prototype read #fff as a near-miss of the warm
  --paper. White and black are never near-misses of one token now.
- A move that changes hue, or puts a tint on a neutral, changes temperature
  even under the just-noticeable difference. Each near-miss is split into its
  lightness, chroma and hue parts, and it snaps only when the hue part is
  under 0.003 and the chroma change under 0.004: greys onto the ink ramp,
  blue-greys onto --surface-desktop and --rule, a lighter --row-alt.

- New --paper-cool, oklch(98% 0.004) at --hue-luna - 13: the cool twin of
  the warm --paper, which the site already wrote as a hand-mixed near-white
  in 18 files. 22 copies of it swept.
- 252 literals in 55 source files now name a token, garage and lwe spec CSS
  included (pageCss/demoCss lines only); every generated page regenerated.
- 400 near-misses stay literal because the move would shift their hue or
  tint. colors:drift keeps listing them.

Rendered against #1190 (local build, 9 pages): 0 to 2.1% of pixels move on
eight pages and 15.9% on /access, and nothing anywhere moves by more than 6
of 255 levels. The all-in prototype moved up to 60% of a page past that.

* fix(tokens): the chrome and title blues resolve to the XP hex their comments name

colors.css said its values "resolve to period-correct Luna" and never
measured it. They came in with a generated design system on 2026-06-05 and
sat up to dE 0.040 from their hex (--darkshadow vs #716F64) and 0.042 for the
title blues (--blue-95 vs #3D95FF): every chrome colour darker and greyer
than XP, with round-number lightness that read like an estimate.

- Chrome: --hue-chrome 96.85, --chroma-chrome 0.0228, exact lightness and
  chroma per token. One knob still drives all four; each lands within
  dE 0.0008 of ButtonFace, ButtonLight, ButtonShadow and the 3D dark shadow.
- Title blues: --chroma-luna 0.2274 and a per-token hue offset (the ramp
  already used them) put --blue-65, --blue-95, --blue-inactive and --blue-50
  within dE 0.0001 of ActiveTitle, GradientActiveTitle, InactiveTitle and
  Highlight. Every title bar and the selection colour move with this.
- The contract now reads a hex in a token's comment as a claim and holds it
  within dE 0.002. Control: the old values fail it on seven tokens.
- With the tokens on XP, 12 hand-typed XP hex values became copies and are
  swept (#316ac5, #ece9d8, #aca899, #0054e3).
- A var() fallback is now legal for a colour literal, as it already was for
  a font stack. luna's client edge says var(--face,#ece9d8): build.ts mirrors
  it into the homepage, which paints before luna.css defines --face, and a
  bare var() would drop the 6px border the mirror exists to keep. build.ts's
  extractor and its homepage check accept that form.

Rendered (local build, 9 pages): the chrome retune moves 0 to 0.4% of
pixels on 8 pages by at most 5/255; the title blues move about 2% (the title
bars) by up to 28/255.

* fix(test): guard the hex-claim test's parses so typecheck holds its baseline

parseOklch and parseSrgb return null for input they cannot read, and the
test used both results without a check: two TS2531 errors that validate's
typecheck refused against config/ts-baseline.json. Assert both parsed, then
use them.

* fix(tokens): narrow the parsed colours before measuring them

parseOklch and parseSrgb return null on a miss, so typecheck refused the
hex-claim test against the baseline. Assert both parsed, then measure.

* fix(tokens): the chrome and title blues resolve to the XP hex their comments name

colors.css said its values "resolve to period-correct Luna" and never
measured it. They came in with a generated design system on 2026-06-05 and
sat up to dE 0.040 from their hex (--darkshadow vs #716F64) and 0.042 for the
title blues (--blue-95 vs #3D95FF): every chrome colour darker and greyer
than XP, with round-number lightness that read like an estimate.

- Chrome: --hue-chrome 96.85, --chroma-chrome 0.0228, exact lightness and
  chroma per token. One knob still drives all four; each lands within
  dE 0.0008 of ButtonFace, ButtonLight, ButtonShadow and the 3D dark shadow.
- Title blues: --chroma-luna 0.2274 and a per-token hue offset (the ramp
  already used them) put --blue-65, --blue-95, --blue-inactive and --blue-50
  within dE 0.0001 of ActiveTitle, GradientActiveTitle, InactiveTitle and
  Highlight. Every title bar and the selection colour move with this.
- The contract now reads a hex in a token's comment as a claim and holds it
  within dE 0.002. Control: the old values fail it on seven tokens.
- With the tokens on XP, 12 hand-typed XP hex values became copies and are
  swept (#316ac5, #ece9d8, #aca899, #0054e3).
- A var() fallback is now legal for a colour literal, as it already was for
  a font stack. luna's client edge says var(--face,#ece9d8): build.ts mirrors
  it into the homepage, which paints before luna.css defines --face, and a
  bare var() would drop the 6px border the mirror exists to keep. build.ts's
  extractor and its homepage check accept that form.

Rendered (local build, 9 pages): the chrome retune moves 0 to 0.4% of
pixels on 8 pages by at most 5/255; the title blues move about 2% (the title
bars) by up to 28/255.

* fix(tokens): narrow the parsed colours before measuring them

parseOklch and parseSrgb return null on a miss, so typecheck refused the
hex-claim test against the baseline. Assert both parsed, then measure.

* feat(tokens): name the 64 colours the site already shared across 5+ files

The palette proposal's alternative to hue ramps: name what the site already
uses rather than snapping it onto a scale. After #1193, 73 colours were still
hand-written in 5+ files. Each is a cluster of near-duplicates within
dE 0.01, and the token's value is the cluster's most-used colour, so those
uses move by nothing (worst dE 0.0001).

- 64 tokens in a SHARED PALETTE block in colors.css (and luna.css's copy),
  named family + OKLCH lightness: --grey-*, --mist-*, --slate-*, --luna-*
  (the window-frame blues), --violet-96, --red-*, --amber-*, --green-*.
  --slate-84 is a slate at L 84%; two same-lightness pairs take a -b suffix.
  Hues hang off the existing knobs, so a knob turn retunes these too.
- Nine clusters within dE 0.01 of an existing token are not named: a second
  name for nearly the same colour would make the choice ambiguous.
- 687 copies swept to var(), 80 near-misses snapped under #1193's hue-aware
  rule, 103 literals in garage and lwe spec CSS, two in the garage generator's
  scaffold, and lwe's encoding concept's accentSoft now names --mist-96.
  Every generated page regenerated.

Rendered against #1193 (local build, 9 pages): 0.5 to 4.8% of pixels move,
none by more than 5 of 255 levels.

* feat(tokens): merge the near-twin greys, and --red-46 into --danger-deep

The owner's call on the merges #1196 listed:

- --grey-30 into --grey-28, --grey-33 into --grey-35, --grey-72 into
  --grey-70. Each pair sat 0.02 apart, the just-noticeable difference; the
  more-used member of each pair stays, so the fewest uses move.
- --red-46 into --danger-deep. --red-46 was exactly #AA0000, the red the
  site has written by hand since the initial import; --danger-deep arrived
  with the generated design system as #AA0018, a bluer approximation of it.
  The name stays and takes the period value: --danger-deep's 2 uses move
  dE 0.014, --red-46's 13 do not move, and its comment's #AA0000 is now held
  by the hex-claim contract.

18 files repointed, specs included; every generated page regenerated.

* fix(email): keep mail colours literal; a token in an email paints nothing

#1183 swept eight colours in two email templates into var(): four #888s in
the coffee-booking mail (cal/src/email.ts) and the webmention moderation
mail's #888s and #555 (src/worker/webmention.ts) became var(--ink-faint) and
var(--ink-quiet). No mail client loads luna.css and many ignore var()
outright, so since #1183 deployed those borders, notes and dates have
rendered in the client's default colour.

The cause was the shared walk: cssSources() read every Worker source as page
CSS that links luna.css. Email HTML does not.

- Both files are back to their pre-#1183 text (it was their only change).
- EMAIL_TEMPLATES lists them, and cssSources() skips them, so neither the
  colour nor the type contract asks them to name a token again.
- A contract test holds both directions: no var() in an email template, and
  every file that calls resendSend is listed. Control: main's current
  email.ts fails it.

---------

Co-authored-by: Aadharsh Pannirselvam <19518661+oddharsh@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

hashed-asset Remints an /a/ URL, so every page and page dictionary rebuilds

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant