refactor(ui): the 25 hand-rolled field errors become one primitive (ROADMAP L1150) - #385
Open
itsdestin wants to merge 1 commit into
Open
refactor(ui): the 25 hand-rolled field errors become one primitive (ROADMAP L1150)#385itsdestin wants to merge 1 commit into
itsdestin wants to merge 1 commit into
Conversation
ROADMAP "Adopt the `FieldError` primitive". 21 sites across 11 files now render <FieldError> instead of writing its markup by hand. Two things the roadmap entry called out as "not a blind swap", both real: * SIZE. The app was split — 19 copies at text-3xs, 6 at text-2xs — while the primitive hardcoded 3xs. Rather than shrink six lines Destin has never seen, the primitive gains `size`. It has to be a prop and not a className pass-through: FieldError concatenates className onto its base, and Tailwind resolves two competing utilities by CSS SOURCE ORDER, so `className="text-2xs"` would silently keep rendering at 3xs (same trap as Button's pills). * HOST ELEMENT. The primitive renders a <span>; 21 of the sites were block <p> carrying `mt-1`/`mt-2`/`pb-2`/`px-1`. Vertical margin and padding do not lay out on an inline element, so a bare swap would have quietly dropped the gap under every one of those fields wherever the parent is not flex/grid. Hence `as`. Rendered text, size, spacing and element are unchanged at every site. Seven remaining matches on the class pair are NOT field errors and stay put, each with its reason in tests/field-error-adoption.test.ts: the four copies of the static skip-permissions caption (always-on warning copy — role="alert" would make a screen reader interrupt on every toggle), SettingsPanel's confirm-dialog prose, GitReviewView's destructive text button, and UpdateButton's deliberate role="status". Guards: tests/field-error-adoption.test.ts fails on a new hand-rolled copy and on an exemption that stops being true; two new cases in ui-primitives pin the size/as behaviour. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0127SETij5i44MFf1wYBnDxE
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.
Autonomous overnight batch, part 2 of 2 — left open for Destin, not merged.
Replaces #383, which GitHub auto-closed when #382 merged and its base branch was deleted. Same single commit, now rebased onto master.
What changed
ROADMAP "Adopt the
FieldErrorprimitive". 22 sites across 11 files now render<FieldError>instead of writing its markup by hand.The roadmap entry flagged two things as "not a blind swap". Both were real:
Size. The app was split — 19 copies at
text-3xs, 6 attext-2xs— while the primitive hardcoded 3xs. Rather than shrink six lines you have never seen, the primitive gains asizeprop. It has to be a prop and not aclassNamepass-through:FieldErrorconcatenates className onto its base, and Tailwind resolves competing utilities by CSS source order, soclassName="text-2xs"would have silently kept rendering at 3xs — the same trap that made the Button pills render as rectangles.Host element. The primitive renders a
<span>; 21 of the sites were block<p>carryingmt-1/mt-2/pb-2/px-1. Vertical margin and padding do not lay out on an inline element, so a bare swap would have quietly dropped the gap under every one of those fields wherever the parent is not flex/grid. Henceas.Why there is no review deck
Nothing to look at — and I would rather say so than hand you 22 identical Before/After pairs. The sweep is provably pixel-identical, so I proved it mechanically instead: for each site, the old tag + class string (read out of the parent commit) against the new one (computed from the FieldError props actually passed), compared as class-token sets so ordering is not mistaken for a diff.
If you would rather have the deck anyway, say so and I will build it.
What was deliberately left alone
Seven remaining matches on the class pair are not field errors. Each is exempt with its reason in
tests/field-error-adoption.test.ts, and a second test fails if an exemption stops being true:App.tsx,SessionStrip.tsx,ResumeBrowser.tsx,ResumeOptionsPopover.tsxFieldErrorcarriesrole="alert", which would make a screen reader interrupt every time the toggle flips. Their real problem is that there are four of them; that wants a shared warning component, not this primitive.SettingsPanel.tsx/80. A paragraph, not a line under a field.GitReviewView.tsxmarketplace/UpdateButton.tsxrole="status"(polite) — a failed update check must not interrupt.The one thing that DOES change behaviour
All 22 sites gain
role="alert". None of them had it before — verified per file againstthe parent commit. Nothing changes visually; what changes is that a screen reader now
announces these errors when they appear, where previously it announced nothing at all. That
is the primitive's stated purpose (the same reasoning is written at its first call site in
SpecialistsSection), and it is why the four static skip-permissions captions are exempt —they are always-on text, and
role="alert"there would interrupt on every toggle.Saying it plainly because it is the only difference the per-site proof below does not cover.
Risk
Low, and bounded by the proof above. The one thing worth an eyeball if you want it: the six
text-2xslines (Providers list error, Local Models "Couldn't reach Hugging Face", remote-server enable error, Connected Accounts, the drawer's rename error, and the update failure) — those are the sites that would have visibly shrunk had the primitive not gainedsize.Verification
bash scripts/verify.shgreen. Two new cases inui-primitives.test.tsxpin the size/as behaviour;field-error-adoption.test.tsfails on a new hand-rolled copy and on a stale exemption.🤖 Generated with Claude Code
https://claude.ai/code/session_0127SETij5i44MFf1wYBnDxE