Skip to main content
    Back to blog
    DevelopmentPublished on October 12, 2026

    Accessible Forms: Labels, Hints, Errors and Autofill

    How to build forms people can complete with a screen reader, a keyboard and minimal effort: labels, autocomplete, fieldsets and error messages

    Accessible Forms: Labels, Hints, Errors and Autofill

    Part 3 of 8 in the "Accessible Web in Practice" series. Previous part: Semantic HTML and Alt Text: The Foundation of an Accessible Website.

    A form is where users are closest to their goal, and where they most often get stuck: half of popular websites have unlabeled fields. Here is how to build forms that are easy to complete with a screen reader, a keyboard or a password manager.

    Forms

    Every field gets a visible label

    <!-- ❌ Placeholder instead of a label -->
    <input type="email" placeholder="Email">
    
    <!-- ✅ -->
    <label for="email">Email address</label>
    <input type="email" id="email" name="email" autocomplete="email">
    

    Why a placeholder can't replace a label:

    • it disappears as soon as you type — people forget what they're entering, especially with memory or attention difficulties;
    • it's usually too faint and fails contrast;
    • support for it as an accessible name is inconsistent.

    Connecting for and id gives you three things at once: the screen reader reads the label when the field gets focus, voice control finds the field by its label, and clicking the label focuses the field — a bigger click target, especially for checkboxes.

    Hints and formats

    <label for="phone">Phone</label>
    <p id="phone-hint" class="hint">For example, +1 503 555 0100</p>
    <input type="tel" id="phone" name="phone"
           autocomplete="tel"
           aria-describedby="phone-hint">
    

    aria-describedby reads the hint after the field's name. The hint is visible to everyone, not tucked away in a tooltip.

    The autocomplete attribute

    autocomplete with the right tokens (name, email, tel, street-address, postal-code, cc-number, one-time-code and others) lets the browser fill in the form for the user. For people with motor and cognitive disabilities that's a huge difference, and WCAG gives it its own criterion, 1.3.5 Identify Input Purpose. The full token list is in the HTML spec and on MDN.

    Grouping fields

    Radio buttons and checkboxes that answer one question are grouped with <fieldset> and <legend>:

    <fieldset>
      <legend>Preferred way to contact you</legend>
    
      <input type="radio" id="contact-phone" name="contact" value="phone">
      <label for="contact-phone">Phone</label>
    
      <input type="radio" id="contact-email" name="contact" value="email">
      <label for="contact-email">Email</label>
    </fieldset>
    

    Without the legend, a screen reader user hears "Phone, radio button" and has no idea what the question is.

    Required fields

    <label for="name">Name <span aria-hidden="true">*</span></label>
    <input type="text" id="name" name="name" required autocomplete="name">
    

    The required attribute tells the screen reader "required." The asterisk is a visual cue, hidden from the screen reader so it doesn't read "star." At the top of the form, explain: "Fields marked with an asterisk are required."

    Errors

    A good error message is:

    1. visible — next to the field, not just a red border;
    2. connected to the field programmatically;
    3. explains how to fix it, not just "Invalid format";
    4. announced on submit, with focus moved to the first error.
    <label for="email">Email address</label>
    <input type="email" id="email" name="email"
           autocomplete="email"
           aria-invalid="true"
           aria-describedby="email-error">
    <p id="email-error" class="error">
      <svg aria-hidden="true" focusable="false">...</svg>
      The address needs an @ sign, for example name@example.com
    </p>
    
    // On submit: validate, show errors and move focus to the first one
    form.addEventListener('submit', (event) => {
      const invalid = [...form.elements].filter((el) => el.willValidate && !el.checkValidity());
      if (invalid.length === 0) return;
    
      event.preventDefault();
      invalid.forEach((field) => showError(field)); // sets aria-invalid and the error text
      invalid[0].focus();
    });
    

    For long forms, an error summary at the top with links to each field works well — the GOV.UK Design System uses this pattern (GOV.UK Error summary).

    Note the icon: an error must not be conveyed by color alone (more in Contrast, Motion and Button Size: Visual Accessibility).

    Don't break paste or password managers

    WCAG 2.2 criterion 3.3.8 says it plainly: logging in must not depend on memorizing or retyping. In practice:

    <!-- ✅ Let the password manager do its job -->
    <input type="email" id="login" autocomplete="username">
    <input type="password" id="password" autocomplete="current-password">
    
    <!-- For an SMS code -->
    <input type="text" inputmode="numeric" autocomplete="one-time-code">
    

    And don't block paste (onpaste="return false") in password or code fields — it protects nothing and gets in everyone's way.

    The "Accessible Web in Practice" series

    1. Web Accessibility: Who It Really Serves and Where to Start
    2. Semantic HTML and Alt Text: The Foundation of an Accessible Website
    3. Accessible Forms: Labels, Hints, Errors and Autofill — you are here
    4. Keyboard and Focus: Building a Website You Can Use Without a Mouse
    5. Contrast, Motion and Button Size: Visual Accessibility
    6. ARIA and Dynamic Content: When Attributes Help and When They Hurt
    7. Accessibility in React: IDs, Focus on Route Change and Announcements
    8. How to Test Accessibility: Automation, Manual Checks and a Checklist

    Next part: Keyboard and Focus: Building a Website You Can Use Without a Mouse

    Agama Labs builds websites and web applications. Want to check how accessible your site is? Write to us: hello@agamalabs.com

    Contact

    Let’s talk about your project

    Leave your details and we’ll get back to you within 2 business hours for a free consultation. Tell us what you’re building - we’ll help you choose the right approach.

    Business hours

    Mon–Fri: 08:00 AM – 5:00 PM (PST)
    Sat–Sun: Closed

    Request a call

    Fill out the form and we’ll reach out shortly

    By clicking the button, you agree to our Privacy Policy

    We value your privacy

    We use analytics cookies, including third-party ones, to see how the site is used and improve it