| 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
- 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.
- Migrate at least one exemplar caller (proves the API; moves the ratchet).
- 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.
- Run
pnpm --filter canvas-workspace test, read the governance failures, and update baselines per the landmines below — in the SAME commit.
- Update
conventions/frontend.md's blessed list + do-NOT-hand-roll list.
The seven landmines
- 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.
- 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.
- 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.
- 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.
- 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.
- 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).
- 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.