extend-blessed-ui · git:20260711.2153867 · 2026-07-11 · sha256 c16861d08826a24d

extend-blessed-ui git:20260711.2153867A

Immutable. This exact content is served forever at /api/v1/blob/c16861d08826a24d.

---
name: extend-blessed-ui
description: Use when adding a component/hook to the blessed components/ui/ set or migrating feature code onto it. Encodes the ratchet-interaction landmines learned across four expansion rounds. Deliberately contains ZERO baseline numbers — live values live in the governance test's own comments.
---

# Extend the Blessed UI Set

Thin by design: the procedure and seven landmines only. The current counters, baselines, and their histories live in `src/main/__tests__/ui-reuse-governance.test.ts`'s comments (that file is the SSOT — it churns often, so numbers written here would rot); the blessed-set inventory lives in `harness/knowledge/conventions/frontend.md`.

## Procedure

1. Build the piece in `components/ui/<Name>/` matching the set's style: `ui-*` class prefix, JSDoc stating exactly which element `className` lands on, tokens for radius/colors/shadows (all three are gated), canonical hooks only (no hand-rolled ESC/click-outside/keydown), barrel export in `ui/index.ts`.
2. Migrate at least one exemplar caller (proves the API; moves the ratchet).
3. Write behavioral tests in `ui/__tests__/` (happy-dom pragma + createRoot/act pattern — copy a sibling). Every prior piece has them; don't be the exception.
4. Run `pnpm --filter canvas-workspace test`, read the governance failures, and update baselines per the landmines below — in the SAME commit.
5. Update `conventions/frontend.md`'s blessed list + do-NOT-hand-roll list.

## The seven landmines

1. **The ratchet fails in BOTH directions.** Improving a counter without lowering its baseline fails CI just like growth does — wins must be locked in the same PR. Read the failure message; it tells you which way to move.
2. **Your new ui/ piece counts against the counter it drains.** A ui/Button contains a `<button>`; a ui/TextField contains an `<input>`; a portal-using shell adds a `createPortal` file. Expect +1 from the blessed piece itself and plan an exemplar migration to offset it — or record the reason if net-positive is genuinely right.
3. **Shells can flatten their own cost.** When a new shell would raise a counter (its own portal/element), check whether routing it through an existing blessed primitive keeps the count flat — ui/Modal and ui/Drawer render through ui/Portal instead of calling createPortal themselves precisely for this.
4. **Tag counters are comment-stripped and test-files are excluded.** Doc prose mentioning `<button>` doesn't count; test harnesses may use natural markup. Don't contort code to dodge a counter — if a count seems wrong, check these two filters before touching baselines.
5. **Canonical primitives are exempt from the keydown counter by an explicit allowlist** (in the test). If you add a new blessed KEYBOARD hook, add its file to that allowlist — otherwise the blessed implementation itself inflates the number it exists to reduce.
6. **A narrow opt-out prop only disables the ONE effect it's wired to.** A shared hook can own several independent behaviors behind one config surface — e.g. `useMenuKeyboardNav`'s `autoFocus` gates only its initial-mount-focus effect; its arrow/Home/End DOM-focus-nav effect is unconditional and fires regardless. Adding an opt-out for the symptom a misfit note names doesn't verify the primitive's OTHER effects won't still conflict with the caller's own external keyboard/focus ownership. Before promising an unlock, trace the primitive's FULL effect list against the caller's FULL keyboard contract, not just the named symptom (API-extension batch, `ui-reuse-burndown.md`: this caught a Home/End focus-steal in two menus that `autoFocus={false}` alone did not fix).
7. **CSS literal counters are NOT comment-stripped (unlike the tag counters).** `borderRadiusLiterals`/`hardcodedColorLiterals`/`shadowLiterals`/`zIndexHighRaw`/`sectionFieldCssClusters` scan raw CSS file content — a doc comment that spells out a literal color/radius/shadow value near a deletion can silently add back the same count you just removed, netting to zero. Describe such values by token name in CSS comments, never by literal (caught landing the iframe-bar-btn migration, `ui-reuse-burndown.md`).

## Done when

Piece + tests + exemplar in; every moved counter's baseline updated same-commit with a one-line cause in the test's comment trail; conventions list updated; `typecheck` + full renderer suite + governance test green.