cd /news/developer-tools/what-to-check-when-reviewing-ai-gene… · home topics developer-tools article
[ARTICLE · art-124205] src=dev.to ↗ pub= topic=developer-tools verified=true sentiment=· neutral

What to Check When Reviewing AI-Generated UI

A developer outlines key accessibility and usability checkpoints for reviewing AI-generated user interfaces, emphasizing proper label-input associations, unique IDs, accessible names for icon buttons, explicit button types, and semantic HTML elements. The guidance applies to hand-written code as well and includes React-specific examples using useId and htmlFor.

by read5 min views5 publishedSep 9, 2026

AI can help us build a form, a modal, or a dashboard quickly.

The result may look good. The inputs accept text. The buttons respond. The layout fits the screen.

But before merging the code, there is another question to ask:

Does the UI work well beyond the happy path?

Some details are easy to miss during a visual review. A label may not be connected to its input. An icon button may have no accessible name. A secondary button may submit a form by accident.

These are review checkpoints, not claims that every AI tool makes these mistakes. They apply to code we write ourselves too.

The examples below use React, but most of the ideas come from HTML and apply across frameworks. The snippets focus on individual details rather than a complete application.

This looks reasonable:

<label>Email address</label>
<input type="email" />

The text is visible, but the label and input are not connected. In JSX, use htmlFor with a matching input id:

<label htmlFor="email">Email address</label>
<input id="email" name="email" type="email" />

This gives the input an accessible name. Clicking the label also focuses the input. In plain HTML, the attribute is for; React uses htmlFor.

Wrapping an input inside its label is another valid approach. The point is to create the association, not to add htmlFor everywhere.

Also, remember that a placeholder is a hint. It should never replace a visible label.

A hardcoded id="email" can work for one field. But what happens when the same component appears twice on the same screen?

Duplicate IDs break label associations and assistive technology references. For reusable React components, React's useId hook ensures instance-level uniqueness:

import { useId } from "react";

function EmailField() {
  const id = useId();

  return (
    <div>
      <label htmlFor={id}>Email address</label>
      <input
        id={id}
        name="email"
        type="email"
        autoComplete="email"
      />
    </div>
  );
}

Each instance gets its own unique ID for the label connection.

Rule of thumb: useId is for accessibility relationships. List keys should come from your underlying data.

A magnifying glass icon may clearly mean “Search” to someone looking at the screen. Make sure the button also provides a programmatic name for screen readers, as well as a visual cue for sighted users who might find the icon ambiguous:

<button type="button" aria-label="Search" title="Search">
  <svg aria-hidden="true" viewBox="0 0 24 24">
    <circle cx="10" cy="10" r="6" fill="none" stroke="currentColor" />
    <path d="m15 15 6 6" stroke="currentColor" />
  </svg>
</button>

Here, aria-label supplies the accessible name, while aria-hidden="true" hides the decorative SVG to avoid redundant announcements. Adding title or a tooltip gives sighted mouse and keyboard users clarity on hover and focus.

If a button already contains visible text:

<button type="submit">Save changes</button>

It does not need an additional aria-label. Prefer visible text whenever the design allows.

Inside a <form>, an ordinary <button> without an explicit type defaults to type="submit".

That becomes an immediate issue when its purpose is “Cancel” or “Show password.” Make the intention explicit on every button:

<button type="button" onClick={onCancel}>
  Cancel
</button>

<button type="submit">
  Create account
</button>

Review every secondary button inside a form. A single missing type="button" attribute can trigger accidental form submissions.

A clickable <div> can look identical to a button:

<div onClick={openSettings}>Settings</div>

However, a <div> does not provide native button keyboard handling, focusability, or form integration. Adding role="button" without implementing Enter and Space key handlers still leaves keyboard users stranded.

Use native semantic elements:

{/* For an action */}
<button type="button" onClick={openSettings}>
  Settings
</button>

{/* For navigation */}
<a href="/settings">Settings</a>

Native HTML elements give you focus management, accessibility traits, and keyboard operability for free before you write a single line of extra script.

Set your mouse aside and test the page using only Tab, Shift + Tab, Enter, Space, and Escape:

When styling focus states in CSS, prefer :focus-visible over :focus:

/* Better: Shows strong outline only during keyboard navigation */
button:focus-visible {
  outline: 2px solid #2563eb;
  outline-offset: 2px;
}

Never set outline: none without providing a prominent, high-contrast :focus-visible replacement.

A red border alone does not explain the issue to users. An error message should identify what went wrong and be programmatically tied to the field:

import { useId } from "react";

function UsernameField({ error }) {
  const id = useId();
  const errorId = `${id}-error`;

  return (
    <div>
      <label htmlFor={id}>Username</label>
      <input
        id={id}
        name="username"
        aria-invalid={error ? true : undefined}
        aria-describedby={error ? errorId : undefined}
      />
      {error && (
        <p id={errorId} role="alert">
          {error}
        </p>
      )}
    </div>
  );
}

aria-invalid flags the field's state to assistive technology.aria-describedby associates the error text with the input so it reads aloud when the field receives focus.role="alert" or using aria-live="polite" ensures dynamically rendered client-side errors are announced immediately, even if the input is already focused. Review more than just the successful response.

Ask:

When indicating submission progress:

<button type="submit" disabled={isSubmitting} aria-busy={isSubmitting}>
  {isSubmitting ? "Saving…" : "Save changes"}
</button>

Note on disabled buttons: Adding disabled directly to a focused button can cause screen readers to immediately drop focus back to the top of the <body>. For critical flows, consider keeping the button focusable with aria-disabled="true" while ignoring pointer and click events in your handler.

AI prompts and sample mocks often feature convenient, short text. Real production data is messy:

alt text, while decorative illustrations use alt="" to avoid cluttering screen readers. Use this checklist during code review:

htmlFor / useId). aria-label) and visual tooltips. type="button" or type="submit".<button>, while navigation uses <a>.:focus-visible indicators. This checklist does not have to remain a manual reference. You can also include it in your coding agent’s review instructions.

Ask the agent to check generated UI for:

An agent can catch many code-level problems before a pull request reaches manual review. However, it cannot fully reproduce how the interface feels with a keyboard, screen reader, browser zoom, or real production content. Use agent review as an additional review layer, then verify the important interactions manually.

── more in #developer-tools 4 stories · sorted by recency
── more on @react 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/what-to-check-when-r…] indexed:0 read:5min 2026-09-09 ·