Build1 publisher3 min readPublished
Seven semantic checkpoints catch the UI defects a visual review approves
A dev.to walkthrough collects the review items a screenshot cannot show: labels not linked to their inputs, icon buttons with no accessible name, and Cancel buttons that submit because HTML defaults them to submit.
The Engineer · Build desk
What happened
- A dev.to walkthrough lists the UI details easy to miss in a visual review: a label not connected to its input, an icon button with no accessible name, and a secondary button that submits a form by accident.
- Inside a form, a button with no explicit type attribute defaults to type="submit", so controls labelled Cancel or Show password fire the submit path unless type="button" is set on each one.
- A hardcoded id survives review on a single-instance page and breaks when the component renders twice, because duplicate ids break label associations and assistive technology references.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- constraint Approving a pull request on rendered output cannot cover any of these items, because each one renders identically whether it is correct or not.
- decision Three of the seven checks are satisfied by an attribute, which makes them a line-level question for the reviewer reading the diff rather than a design conversation.
- exposure The user pays for the missing type="button": a control meant to cancel runs the submit path instead, and keyboard-only users absorb the div-as-button failures.
- contradiction The source declines to pin these defects on AI generation and offers no failure rate, so any argument that generated UI needs this list more than hand-written UI rests on volume, not on measurement.
Of the checkpoints in the dev.to walkthrough, the button type is the only one that changes what the program does rather than what a screen reader says. An ordinary `<button>` inside a `<form>` with no `type` attribute is a submit button, which makes "Cancel" and "Show password" live submit triggers [12]. The fix is `type="button"` on every control in the form that is not the submit control [12]. HTML's default is sensible exactly once per form.
That defect survives a visual review because the rendered button is identical either way. Same for the label. `<label>Email address</label>` sitting next to an `<input type="email" />` shows the text and associates nothing; `htmlFor` on the label with a matching `id` on the input creates the association, gives the input an accessible name, and makes clicking the label focus the field [4][5]. Wrapping the input inside the label does the same job, because the association is the point rather than the attribute [6]. A placeholder is a hint and never a replacement for a visible label [7].
Count the checkpoints in the piece and there are seven [1]. Three of them come down to attributes: the `htmlFor`/`id` pairing, `aria-label` on an icon-only button, and `type="button"` [2]. Attributes are visible in a diff and invisible in a screenshot, which is why this list belongs in code review rather than a design pass.
The id checkpoint is the one that fails late. A hardcoded `id="email"` works until the component renders twice on one screen, at which point duplicate ids break the label association and any assistive-technology reference pointing at them [8]. React's `useId` gives each instance its own value [9]. The scope rule in the piece is worth copying verbatim: `useId` is for accessibility relationships, and list keys come from your underlying data [9].
The clickable `<div>` is the same problem in a different place. It can look identical to a button while providing none of the native keyboard handling, focusability or form integration, and adding `role="button"` without Enter and Space handlers still leaves keyboard users stranded [14]. A native `<button>` for actions and an `<a href>` for navigation hand you focus management, accessibility traits and keyboard operability before you write any script [15].
On the icon button, `aria-label` supplies the programmatic name while `aria-hidden="true"` on the decorative SVG stops the redundant announcement, and a `title` or tooltip gives sighted mouse and keyboard users something on hover and focus [10]. A button that already carries visible text needs no `aria-label` [11].
The cheapest verification needs no tooling. Put the mouse down and drive the page with Tab, Shift+Tab, Enter, Space and Escape, then ask whether focus is visible, whether custom dropdowns and dialogs open and close, whether focus moves into a modal and stays there, and whether it returns to the trigger on close [16]. Style those states with `:focus-visible` rather than `:focus` [17].
One caution about the framing. The author is explicit that these are review checkpoints and not claims that every AI tool makes these mistakes, and that hand-written code fails the same way [3]. There is no defect rate here, generated or otherwise. For an AI-specific review gap to be a measured thing rather than an assumption, you would need these same seven checks run against generated and hand-written components with failures counted per component. What the evidence supports is narrower: each of the seven is checkable from the diff, which is where the review already sits.
What to watch
- Whether generation tools start emitting type="button" and htmlFor/id pairings by default, which moves these checks out of human review.
- Any published count of failures per component that compares generated markup with hand-written markup on the same checks.
- Whether teams encode the seven checkpoints as automated checks in CI instead of a list a reviewer is asked to remember.