Skip to main content
Form usability issues frustrate users and lead to abandonment. Accessibility issues in forms make them completely unusable for screen reader users. Both categories have direct business impact.

missing-error-messages

Critical WCAG 3.3.1

What it is

When a form is submitted with invalid or missing data, no error message is shown anywhere in the DOM. The submission fails silently, leaving users with no indication of what went wrong or how to fix it.

Why it matters

Without error feedback, users cannot complete the form. This directly blocks task completion for all users — and for screen reader users, even a generic error message is critical since they cannot visually scan the form for visual error indicators.

How QAOS detects it

The agent submits forms with required fields left empty or with invalid data, then scans the DOM for any error message elements — including messages in separate <div> elements, data-validation-message attributes, or ARIA live regions.

Examples

<!-- Form with no error handling — fails silently -->
<form>
  <input type="email" required name="email">
  <button type="submit">Subscribe</button>
</form>
<!-- After invalid submit: page unchanged, no message shown -->

How to fix

Display at least one error message when form validation fails. The message should indicate which fields are invalid and what the user needs to do:
<!-- Generic summary error -->
<div role="alert" id="form-errors">
  Please correct the following errors before continuing.
</div>

<!-- Per-field inline error -->
<label for="email">Email</label>
<input type="email" id="email" aria-describedby="email-error">
<span id="email-error" role="alert">Please enter a valid email address</span>

error-not-visible

Critical WCAG 3.3.1

What it is

Error message elements exist in the DOM after a failed submission, but they are hidden via CSS (display: none, visibility: hidden, opacity: 0, or off-screen positioning) and never appear to the user.

Why it matters

This is a particularly confusing failure state: the application generates error information but suppresses it. Users see nothing, have no way to correct their input, and cannot complete the form.

How QAOS detects it

After submitting a form with invalid data, the agent checks whether error-related elements exist in the DOM but are hidden by CSS properties that prevent them from being visible.

How to fix

Ensure error message elements are visible when validation fails:
/* Instead of toggling display:none */
.error-message {
  display: block; /* Show on error */
}

/* Or use visibility */
.field-error {
  visibility: visible;
  height: auto;
}
Use JavaScript or framework reactivity to conditionally render error messages rather than hiding pre-rendered ones:
// React example
{errors.email && <span role="alert">{errors.email.message}</span>}

required-field-not-marked

Medium WCAG 3.3.2

What it is

Form fields that are required (via the required attribute or server-side validation) are not visually indicated as required — no asterisk, no label, no visual cue that helps users understand which fields must be completed before submission.

Why it matters

Users who don’t realize a field is required often skip it, experience validation errors, and feel frustrated. Clear required field indicators reduce form abandonment.

How QAOS detects it

The agent identifies <input required> or validated fields that lack any visual indicator (asterisk in label, “required” text, or similar convention).

Examples

<!-- Missing visual required indicator -->
<label for="phone">Phone</label>
<input type="tel" id="phone" required>

<!-- Correct: asterisk indicator -->
<label for="phone">Phone <span aria-hidden="true">*</span></label>
<input type="tel" id="phone" required aria-required="true">

<!-- Include legend explaining the indicator -->
<p>Fields marked with <span aria-hidden="true">*</span> are required.</p>

How to fix

Mark required fields visually with a consistent indicator (typically *). Include a legend near the top of the form explaining the convention. Use aria-required="true" on the input for screen reader users.

error-not-associated

Low WCAG 1.3.1

What it is

Error messages are displayed after a failed submission but are not programmatically linked to the field they describe. The message appears visually near the field but lacks aria-describedby or aria-errormessage linking them.

Why it matters

Screen readers announce input fields by reading their label and any programmatically associated descriptions. Without aria-describedby, a screen reader user focusing on a field will not hear the associated error message unless they navigate away to find it.

How QAOS detects it

After submitting invalid data, the agent checks whether fields with visible error messages have corresponding aria-describedby or aria-errormessage attributes pointing to the error element.

How to fix

Link error messages to their fields using aria-describedby:
<label for="email">Email</label>
<input
  type="email"
  id="email"
  aria-describedby="email-error"
  aria-invalid="true"
>
<span id="email-error" role="alert">
  Please enter a valid email address
</span>

placeholder-as-label

Medium WCAG 1.3.1

What it is

An input field relies solely on placeholder text to communicate its purpose. There is no visible <label> element, aria-label, or aria-labelledby attribute associated with the field.

Why it matters

Placeholder text disappears as soon as the user starts typing, leaving the field with no label at all. Users who navigate back to a partially filled form see unlabeled fields and cannot recall what they entered. Screen readers also handle placeholders inconsistently — some announce them, others do not.

How QAOS detects it

The agent identifies input fields that have a placeholder attribute but no programmatic label association.

Examples

<!-- Placeholder-only — label disappears on typing -->
<input type="email" placeholder="Email address">

<!-- Correct: visible label + placeholder for hint -->
<label for="email">Email address</label>
<input type="email" id="email" placeholder="you@example.com">

How to fix

Always provide a persistent visible label. Use placeholder text only as a supplementary hint, never as the primary label.

form-no-submit-button

Medium

What it is

A form element contains no visible submit button or equivalent (<button type="submit"> or <input type="submit">), making it impossible to submit via keyboard without relying on the Enter key in a text field.

Why it matters

Keyboard-only users and assistive technology users expect a clearly labeled action to submit a form. A form without a submit button is also non-standard and can confuse users who don’t know the Enter-key shortcut.

How QAOS detects it

The agent scans form elements for the absence of any submit button or equivalent interactive control that would submit the form.

How to fix

Every form should have an explicit submit button:
<form>
  <label for="email">Email</label>
  <input type="email" id="email" name="email">
  <button type="submit">Subscribe</button>
</form>

select-missing-label

Medium WCAG 1.3.1

What it is

A <select> element has no associated label — no <label for="...">, aria-label, or aria-labelledby — making its purpose opaque to screen readers.

Why it matters

Screen readers announce form controls by reading their label. A <select> without a label is announced only as “combo box” or “list box” with no indication of what the user is selecting.

How QAOS detects it

The agent scans the DOM for <select> elements without a programmatically associated label.

Examples

<!-- Unlabeled select -->
<select name="country">
  <option>United States</option>
</select>

<!-- Correct: associated label -->
<label for="country">Country</label>
<select id="country" name="country">
  <option>United States</option>
</select>

How to fix

Associate every <select> with a visible label using <label for="...">. If a visible label is not possible, use aria-label:
<select aria-label="Select your country" name="country">

copy-paste-blocked

Medium

What it is

An input field (commonly a password confirmation or OTP code field) blocks paste operations, forcing users to type content manually even when they have it copied to the clipboard.

Why it matters

Blocking paste discourages the use of password managers (which paste strong, unique passwords), forces users to retype complex credentials (increasing errors), and provides no real security benefit. NIST guidelines explicitly recommend allowing paste in password fields.

How QAOS detects it

The agent attempts to paste into input fields using keyboard and programmatic methods and checks whether the paste is suppressed.

How to fix

Remove any onpaste="return false" or paste event listeners that call preventDefault():
// Remove this
input.addEventListener('paste', (e) => e.preventDefault())

// Allow paste — it's the default behavior, no code needed

input-missing-type

Low

What it is

An <input> element has no type attribute. The browser defaults to type="text", which may not match the intended data type, missing semantic benefits like numeric keyboards on mobile and built-in validation.

How QAOS detects it

The agent scans the DOM for <input> elements with no type attribute.

Examples

<!-- Missing type — defaults to text -->
<input name="email">
<input name="age">

<!-- Correct: explicit types -->
<input type="email" name="email">
<input type="number" name="age" min="0" max="150">

How to fix

Always set an explicit type attribute on every input to get the correct keyboard on mobile, built-in browser validation, and clear semantic meaning.

missing-autocomplete

Low WCAG 1.3.5

What it is

Personal data fields — name, email, phone number, address — lack the autocomplete attribute, preventing browsers and password managers from filling them automatically.

Why it matters

Autocomplete saves significant time for users, especially those with motor impairments who find typing difficult. It also improves accuracy by reducing manual entry errors.

How QAOS detects it

The agent scans form fields for personal data inputs (by name, type, or label text) that lack the autocomplete attribute.

How to fix

Add the appropriate autocomplete token to personal data fields:
<input type="text" name="name" autocomplete="name">
<input type="email" name="email" autocomplete="email">
<input type="tel" name="phone" autocomplete="tel">
<input type="text" name="address" autocomplete="street-address">

password-no-visibility-toggle

Low

What it is

A password input field has no show/hide toggle, making it impossible for users to verify what they’ve typed without external tools.

Why it matters

Password masking causes typing errors. Without a visibility toggle, users cannot verify their password, leading to failed logins or mismatched password confirmation fields. This is a common usability issue that discourages use of strong passwords.

How QAOS detects it

The agent scans the DOM for <input type="password"> elements and checks whether a toggle button is associated with them.

How to fix

Add a toggle button that switches the input between type="password" and type="text":
<div class="password-field">
  <input type="password" id="password" name="password">
  <button type="button" aria-label="Show password" onclick="togglePassword()">
    Show
  </button>
</div>
function togglePassword() {
  const input = document.getElementById('password')
  input.type = input.type === 'password' ? 'text' : 'password'
}

character-count-missing

Low

What it is

A text input or textarea with a character limit (maxlength) shows no visible character count or remaining-characters indicator, leaving users unaware of the limit until they hit it.

How QAOS detects it

The agent identifies <input> or <textarea> elements with a maxlength attribute and checks whether a character counter is visible near the field.

How to fix

Display a live character count that updates as the user types:
<textarea id="bio" maxlength="280"></textarea>
<span id="bio-count" aria-live="polite">280 characters remaining</span>
document.getElementById('bio').addEventListener('input', function () {
  const remaining = 280 - this.value.length
  document.getElementById('bio-count').textContent = `${remaining} characters remaining`
})

ambiguous-date-format

Low

What it is

Dates are displayed or expected in an ambiguous format — such as 01/02/2024 — that could be interpreted differently depending on locale (MM/DD vs DD/MM).

Why it matters

A date like 04/05/24 means April 5th in the US and May 4th in Europe. Ambiguous dates lead to booking errors, missed deadlines, and user confusion in international contexts.

How QAOS detects it

The agent scans date display text and date input placeholders for numeric date formats without an unambiguous structure.

How to fix

Use unambiguous date formats that don’t rely on locale conventions:
Ambiguous: 01/02/2024
Clear:     Jan 2, 2024  |  2 January 2024  |  2024-01-02 (ISO 8601)
For date inputs, use <input type="date"> (the browser renders a locale-appropriate picker) or include an explicit format label:
<label for="dob">Date of birth (DD/MM/YYYY)</label>
<input type="text" id="dob" placeholder="15/06/1990">

empty-state-missing

Low

What it is

A dynamic content list — such as a comments section, inbox, notifications panel, or search results — is empty and shows only blank space with no message explaining the empty state.

Why it matters

Blank space where content is expected looks like a loading failure or rendering bug. Users don’t know whether the list is genuinely empty, still loading, or broken. An explicit “No results” or “Your inbox is empty” message removes ambiguity.

How QAOS detects it

The agent checks list containers (<ul>, <ol>, or labeled content regions) that have no child items and no adjacent explanatory text.

How to fix

Render a clear, helpful empty state message when a dynamic list is empty:
{comments.length === 0 ? (
  <p>No comments yet. Be the first to leave one!</p>
) : (
  <ul>{comments.map(c => <CommentItem key={c.id} {...c} />)}</ul>
)}