utils/unified-design-system/DOCS-phase3-web-ui.md
Krzysztof kuhy Rudnicki 7096c102cd
Some checks are pending
crdt_sync_dart tests / test (push) Waiting to run
crdt_sync_flutter tests / test (push) Waiting to run
crdt-sync pre-commit / pre-commit (push) Waiting to run
crdt-sync tests / test (push) Waiting to run
dependency freshness / dependencies on newest stable (push) Waiting to run
design_system tests / test (push) Waiting to run
file length / file-length (push) Waiting to run
freedays pre-commit / pre-commit (push) Waiting to run
freedays tests / test (push) Waiting to run
gatelock pre-commit / pre-commit (push) Waiting to run
gatelock tests / test (push) Waiting to run
github_device_auth tests / test (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
music_theory tests / test (push) Waiting to run
palette drift / palette (push) Waiting to run
shared gate tests / gate unit tests (push) Waiting to run
sync_settings_ui tests / test (push) Waiting to run
web_ui tests / test (push) Waiting to run
Re-point paths for the ~ reorganisation (home-tidy)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AwtowrowDXx6ccmWVPkCaF
2026-09-11 18:45:55 +02:00

4.4 KiB

Implement Phase 3 of the shared component work: create ~/src/utils/web_ui/ (TypeScript) and migrate two proof consumers onto it. Treat this as override on the spec gate — the spec is below, it is complete, and I do not want a round of questions before you start.

Goal. Seven web repos define seven different vocabularies for the same palette: --surface-1 vs --panel vs --card; --text vs --ink vs --fg vs --bone; spacing as --space-N vs --sp-N vs --sp-xs. Two of them additionally carry the same two components, drifted. Give the web stack the token + component layer that Dart already has.

Build these three:

  1. tokens.css — one :root block using the canonical variable names from ~/src/utils/unified-design-system/README.md and tokens.md. The palette is frozen: ink #211D1B, ink-raised-1 #2B2624, ink-raised-2 #38312E, line-dark #463E3A, text-on-dark #ECEAE9, muted-on-dark #AAA09A, paper #F6F4F3, paper-raised #FCFBFB, line-light #E0DAD7, muted-on-light #70625B, accent #B8862E, success #8A9A3C, warning #E0A63C, danger #E2585F, on-fill #211D1B. Spacing 4/8/16/24/32/48, radius 8/12/16, type 12/14/16/20/24/32. Do not invent new values; these are the same numbers ~/src/utils/design_system ships for Dart.
  2. RangeSlider — canonical is dufs-cloud's, whose pure fractionFromPointer(rect, clientX) is testable without layout (jsdom has none). awesome-mcp-explorer's copy has drifted 196 lines. Keep the pure function pure; that is the whole reason this one is the donor.
  3. FilterBar + filter-sort.ts — reconcile dufs-cloud (174 lines) vs awesome-mcp-explorer (225).

Consumption mechanism — already decided, do not re-litigate. A git dep "@kuhyx/web-ui": "github:kuhyx/utils#web_ui-v0.1.0", matching the Dart/Python tag convention rather than introducing npm workspaces. Ship a prebuilt dist/ (tsc → ESM + .d.ts) committed in the tag. Raw .tsx in node_modules would need per-repo bundler config, because Vite does not transpile dependencies by default; a committed dist/ keeps consumers at zero config. This is the only stack with no existing precedent — verified: no workspaces key and no file:/link:/github:kuhyx dep anywhere — so the mechanism is the risky part, not the components.

Proof consumers: dufs-cloud and awesome-mcp-explorer — the only two with confirmed shared components. Migrate both; do not stop at one.

Extraction criterion. Build a component only where two or more repos already contain a structurally similar implementation. Nielsen's ten elements are the audit grid, not a build list — shipping a widget nobody imports is the failure mode to avoid. Two traps from Phase 1: a name collision is not a duplicate (judge structurally — the three FilterSheets share a name but one uses no chips at all), and when you extract something, retire the donor copies in the same pass, or you have shipped a duplicate rather than removed one.

An open question you will hit. The design system has one accent and therefore no answer for "N mutually distinguishable hues" — a categorical ramp for charts, tags and category dots. billsplit's four category colours were deliberately left unmapped for this reason. If FilterBar or anything else needs categorical colour, do not invent a ramp inline; raise it with me as its own decision.

Verification.

  • npm test in both proof consumers.
  • Verify at 1366x768 and 1024x600 with no horizontal scroll, and confirm :focus-visible is styled — an unstyled focus ring is the accessibility regression this layer is supposed to prevent.
  • Confirm the migrated repos actually render from tokens.css: grep that their old bespoke custom properties are gone, not merely shadowed.
  • pre-commit run --files <changed> in every touched repo; respect the 250-line file cap.

Done means: ~/src/utils/web_ui exists with a committed dist/, tagged web_ui-v0.1.0; both proof consumers import it, with their local RangeSlider/FilterBar/token blocks deleted; tests pass in both; and everything is committed and pushed.

Context, if you want it: ~/src/utils/unified-design-system/nielsen-audit.md has the full grid, the deferred clusters, and the Phase 1 record. Phase 1 (~/src/utils/design_system, Dart) is done — 7 exports, 100% coverage, six consumers — and is the pattern to imitate: publish_to: none, own lint config, own tests, own coverage gate enforced in CI, tag-pinned dep.