feat(web): the mode landing is two ActionTiles, and the tiles are coloured - #307
Merged
Conversation
…oured The front door drew eight hand-written `<Link>`s: one accent colour for every glyph on the page, a neutral border that went accent on hover, and a `→` standing in for affordance. The owner: "I don't like what we built for QueuePilot. It's not as flashy. Very boring, not colorful." It is two `ActionTiles` sets now — the two starts at `size="lg"`, the four management links at `size="sm"`. `@charcuterie/ui@4.1.0` walks a ten-hue categorical palette, sits the icon beside the name and hovers in the tile's own hue. This is the fourth app to take the shape; mux-magic, gallery-downloader and points-market adopted it yesterday. Six CSS rule groups are deleted rather than trimmed — `.mode-primary-action`, `.mode-primary-icon`, `.mode-action-arrow`, `.mode-management-link`, `.mode-management-icon`, `.mode-management-arrow` — plus both `grid-template-columns` overrides in the 760px media query. A leftover rule repaints one app's copy of a shape four apps share. `.mode-primary-actions` survives as one property, `margin-top`, because the gap above the set is still this page's to say. Each destination gets a NAMED hue rather than the position each set would take. Queues is drawn twice — once as the second start, once as the first management link — and two sets each walk the palette from their own first index, so the one destination would have been two colours. The queue-type chooser has been an `ActionTiles` since 2026-09-01; its two tiles gain a glyph, so the new colour lands on a shape rather than only the left bar. `CategoricalIndex` is 1-BASED. The first draft wrote `index as CategoricalIndex` over the array position, handed the library a 0, and the landing rendered as a blank page — every lookup inside `ActionTiles` is a `Record<CategoricalIndex, …>`, so the 0 was `undefined` and the tile died reading `.ghost` off it. `as` is an assertion, so tsc believed it; lint has no opinion about arithmetic; the unit suite does not render the landing. A browser caught it. The value is read out of `CATEGORICAL_INDEXES` now, which needs no cast at all. `narrow-scroll-test` waited on `.mode-primary-action`. It waits on the set's named `role="group"` instead — named rather than bare, because the page has two. Gates: typecheck clean; lint 74 warnings, all pre-existing and identical to main; 695 unit tests pass; `narrow-scroll-test` and `routing-test` both pass, including "the task home offers two starts and four management destinations".
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
The front door at
/drew eight hand-written<Link>s under.mode-primary-actionand.mode-management-link: one accent colour forevery glyph on the page, a neutral border that went accent on hover, and a
→standing in for affordance.The owner, looking at four apps side by side:
It is two Charcuterie
ActionTilessets now. This is the fourth app totake the shape — mux-magic, gallery-downloader and points-market adopted
it yesterday.
The landing
The queue-type chooser
It has been an
ActionTilessince 2026-09-01. What changes is that@charcuterie/ui@4.1.0colours it, and its two tiles gain a glyph so thecolour lands on a shape rather than only the left bar.
Every frame is fixture data from
e2e/fixtures/landing.*.yaml, capturedby the new
e2e/shot-mode-landing.ts. That script assertsdata-schemebefore each shot, so a frame named-darkcannot be alight render.
Three decisions in the diff
Six CSS rule groups are deleted, not trimmed.
.mode-primary-action,.mode-primary-icon,.mode-action-arrow,.mode-management-link,.mode-management-icon,.mode-management-arrow, plus bothgrid-template-columnsoverrides inthe 760px media query. A leftover rule repaints one app's copy of a shape
four apps share.
.mode-primary-actionssurvives as one property,margin-top, because the gap above the set is still this page's to say.Each destination gets a named hue, not a per-set position. Queues is
drawn twice — once as the second start, once as the first management
link. Two
ActionTilessets each walk the palette from their own firstindex, so the one destination would have been two colours and the eye
would have read two places.
NAVIGATION_CATEGORICALis keyed byhref, because that is the identity a destination has here.The column count comes from the container. Both
grid-template-columnsdeclarations and both media-query overrides aregone. A 600px panel on a 2560px monitor is exactly as narrow as a phone,
and a
max-width: 760pxquery could never see that.One bug I wrote and caught
CategoricalIndexis 1-based —1..10, not0..9.The first draft built the hue map as
index as CategoricalIndexover thearray position, so the first destination got a
0. Every lookup insideActionTilesis a plainRecord<CategoricalIndex, …>, so the 0 wasundefinedand the tile died reading.ghostoff it. The landingrendered as a blank page.
Nothing in the toolchain reported it.
asis an assertion, so tscbelieved the claim instead of checking it; lint has no opinion about
arithmetic; the unit suite does not render the landing. A browser probe
after the build caught it. The value is read out of
CATEGORICAL_INDEXESnow, which needs no cast at all.
Gates
yarn typecheck— clean (web, server, e2e).yarn lint— 74 warnings, identical tomain, none in the changedfiles.
yarn test— 695 pass (web 324, server 371).e2e/narrow-scroll-test.ts— passes at 320px, 390px and both emulatedwidths. Its landing selector moved from
.mode-primary-actionto theset's named
role="group".e2e/routing-test.ts— passes, including "the task home offers twostarts and four management destinations".
🤖 Generated with Claude Code