What this example teaches

  • How visible labels connect to inputs.
  • Why placeholder-only fields are risky.
  • How required text, errors, and grouped radio buttons need structure.

Proper label for input

Bad

<input id="email" name="email" type="email">

The field has no visible or programmatic label.

Better

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

The label is visible and connected to the input with for and id.

Placeholder-only field

Bad

<input name="name" placeholder="Full name">

The placeholder disappears when users type and may not work as a reliable label.

Better

<label for="name">Full name</label>
<input id="name" name="name" placeholder="Jane Smith">

The label remains visible, and the placeholder is only an example.

Required field text

Bad

<label for="phone">Phone</label>
<input id="phone" required>

Required may be announced by some technology, but the visible page does not explain it clearly.

Better

<label for="phone">Phone <span aria-hidden="true">*</span></label>
<p id="phone-hint">Required. Use 10 digits.</p>
<input id="phone" required aria-describedby="phone-hint">

The field has visible instructions and a programmatic description.

Error message association

Bad

<label for="postal">Postal code</label>
<input id="postal">
<p class="error">Enter a valid postal code.</p>

The error may be visible but not connected to the field.

Better

<label for="postal">Postal code</label>
<input id="postal" aria-invalid="true" aria-describedby="postal-error">
<p id="postal-error" class="error">Enter a valid Canadian postal code, such as K1A 0B1.</p>

The field exposes its error state and points to the error message.

Grouped radio buttons

Bad

<p>Contact preference</p>
<label><input type="radio" name="contact"> Email</label>
<label><input type="radio" name="contact"> Phone</label>

The group label is not programmatically tied to the radio choices.

Better

<fieldset>
  <legend>Contact preference</legend>
  <label><input type="radio" name="contact" value="email"> Email</label>
  <label><input type="radio" name="contact" value="phone"> Phone</label>
</fieldset>

The legend gives the group a clear accessible name.

Contact form example

Bad

<input placeholder="Name">
<input placeholder="Email">
<textarea placeholder="Message"></textarea>
<button>Submit</button>

The fields depend on placeholders and the submit button is generic.

Better

<label for="contact-name">Name</label>
<input id="contact-name" name="name">
<label for="contact-email">Email address</label>
<input id="contact-email" name="email" type="email">
<label for="message">Message</label>
<textarea id="message" name="message"></textarea>
<button type="submit">Send message</button>

Each control has a persistent label and the button describes the action.

Checkbox group

Bad

<p>Interests</p>
<label><input type="checkbox" name="interest" value="news"> News</label>
<label><input type="checkbox" name="interest" value="events"> Events</label>

The group heading is not programmatically tied to the checkboxes; a screen reader may not announce the relationship.

Better

<fieldset>
  <legend>Interests</legend>
  <label><input type="checkbox" name="interest" value="news"> News</label>
  <label><input type="checkbox" name="interest" value="events"> Events</label>
</fieldset>

Wrapping checkboxes in a fieldset with a legend gives the group a clear accessible name.

Submit button text

Bad

<button type="submit">Submit</button>
<button type="submit"><svg aria-hidden="true" viewBox="0 0 24 24"><path d="M12 5v14M5 12h14"></path></svg></button>

"Submit" is generic and does not describe the action. The icon button has no accessible name at all.

Better

<button type="submit">Send message</button>
<button type="submit" aria-label="Send message"><svg aria-hidden="true" viewBox="0 0 24 24"><path d="M12 5v14M5 12h14"></path></svg></button>

The visible button says what it does. The icon-only button has a clear aria-label.

Autocomplete on contact fields

Bad

<label for="name">Full name</label>
<input id="name" name="name">
<label for="email">Email</label>
<input id="email" name="email" type="email">

No autocomplete hints, so assistive technology and browser autofill cannot help users fill the form.

Better

<label for="name">Full name</label>
<input id="name" name="name" autocomplete="name">
<label for="email">Email</label>
<input id="email" name="email" type="email" autocomplete="email">

Autocomplete values help browsers and assistive tech suggest or fill common field values.

What still needs human review

  • Whether labels, instructions, and errors are understandable to real users.
  • Whether validation works with keyboard and assistive technology.
  • Whether custom controls expose the expected name, role, state, and focus order.
  • Whether CAPTCHA or spam protection is accessible.
  • Whether error messages are helpful and clear for all users.

Related tools

Related issue library pages

Related guides and resources

Recommended next steps