mgifford/accessibility-skills/forms

Claude Code and agent skill by mgifford/accessibility-skills

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Load this skill whenever the project contains forms, inputs, selects, checkboxes, radio buttons, text areas, or any validation flow. Under no circumstances create a form without visible labels, error identification, and keyboard accessibility. Absolutely always associate every input with a label and provide clear, accessible error messages.

Install

npx vskill@latest install mgifford/accessibility-skills/forms

SKILL.md

Forms Accessibility Skill

Canonical source: examples/FORMS_ACCESSIBILITY_BEST_PRACTICES.md in mgifford/ACCESSIBILITY.md
This skill is derived from that file. When in doubt, the example is authoritative.

Apply these rules when implementing or reviewing any form, input, or validation flow. Only load this skill if the project contains forms.


Core Mandate

Forms must be understandable and operable with keyboards, touch, speech input, screen readers, screen magnification, browser autofill, password managers, and other assistive technologies. Requirements apply to initial entry, validation, recovery, review, submission, and authentication. Users must be able to complete time-limited forms when a time limit is not essential, and complete the form at 200%/400% zoom without losing content or functionality.

Prefer native HTML controls. Build a custom widget only when native controls cannot meet the documented requirement, and implement the applicable keyboard, focus, name, role, state, and value behaviour.


Severity Scale (this skill)

LevelMeaning
CriticalBlocks task completion entirely for one or more disability groups
SeriousSignificantly impairs access; workaround unreasonable to expect
ModerateCreates friction; workaround exists and is not too burdensome
MinorBest-practice gap; marginal impact on access

Do not assign severity from the violated rule alone — determine it from the actual form and task. Consider: whether the defect blocks submission or recovery; whether it affects authentication, payment, health, legal, safety, or essential public services; how many users/controls are affected; whether a reasonable workaround exists; whether it comes from a shared component; and whether data can be lost.


Critical: Labels

Every form control must have a programmatically associated label. Missing labels are Critical — screen reader users cannot identify the field.

  • Never use placeholder text as the sole label — it disappears on input, fails

contrast requirements, and is not reliably announced by all screen readers

  • Required fields must be identified in text, not by colour alone
  • A control nested inside its own <label> is also valid
  • Use aria-label/aria-labelledby only when a visible HTML label is not

practical, and ensure the accessible name includes the words users can see (speech-input users identify controls by their visible label)

  • Controls whose content already supplies a name (e.g., <button>Save</button>)

don't need a separate label

html
<!-- Bad: placeholder as only label — CRITICAL issue -->
<input type="email" placeholder="Email address">
<!-- Good: explicit label -->
<label for="email">
Email address <span class="visually-hidden">(required)</span>
</label>
<input type="email" id="email" autocomplete="email" required>

On `aria-required`: Do not add aria-required="true" to native inputs that already have the HTML required attribute — it is redundant and adds noise to the accessibility tree. Use aria-required only on custom widgets (e.g., role="combobox", role="listbox") that cannot use the native attribute.

Required/optional fields: explain how required fields are identified before the first field (e.g., "Fields marked 'required' must be completed"). Don't rely on an asterisk or colour alone. Don't mark every optional field when marking the smaller set of required fields would be clearer, or vice versa.


Critical: Error Identification

Errors must be programmatically associated with their field. Absent error identification is Critical — blind users receive no indication something is wrong.

  • Mark invalid fields with aria-invalid="true" after validation determines

they are invalid; remove the invalid state and error association after correction

  • Error messages must be specific and actionable — not just "invalid input"
  • Never use colour or an icon alone to indicate an error
  • Associate the error message with the field via aria-describedby (or a

tested aria-errormessage implementation); preserve any existing hint association

  • Keep the entered value unless security requires otherwise
html
<label for="email">Email address</label>
<p id="email-hint">We will send the receipt to this address.</p>
<input id="email" name="email" type="email" autocomplete="email"
aria-invalid="true" aria-describedby="email-hint email-error">
<p id="email-error">
<strong>Error:</strong> Enter an email address in the format name@example.com.
</p>

Do not put role="alert" on error text that is already present when the page loads — a live region only announces changes made after it is established. Avoid combining several competing announcement mechanisms without AT testing.


Critical: Error Summary

Provide an error summary when several fields can fail, the form is long, errors may be outside the viewport, or users would otherwise have difficulty locating them. No error summary on multi-error, long, or complex forms is Critical.

html
<div id="error-summary" tabindex="-1" aria-labelledby="error-heading">
<h2 id="error-heading">There are 2 errors</h2>
<ul>
<li><a href="#email">Email address: enter a valid email address</a></li>
<li><a href="#dob-day">Date of birth: enter a real date</a></li>
</ul>
</div>
  • Place the summary before or at the beginning of the form; insert it after a

failed submission and move focus to it

  • Use links that identify both the field and the problem; activating a link

must move focus to the relevant control

  • Keep the inline field message as well as the summary; update the heading

count when errors change

  • Avoid adding role="alert" when focus movement already causes a clear

announcement, unless testing shows an additional live announcement helps

  • Focus movement is a deliberate response to the submit action — don't move

focus on every inline validation change


Critical: Do Not Block Paste

Never block paste, copy, or standard keyboard actions on any input. Blocking paste is Critical — users with motor disabilities and those who use password managers cannot re-type credentials they cannot physically type.


Serious: Accessible Authentication (WCAG 3.3.8)

Authentication must not require users to remember, manipulate, or transcribe information unless a conforming alternative or assistance mechanism exists. At minimum:

  • Allow password managers to identify and fill username/password fields
  • Allow copy and paste into username, password, and verification-code fields

— do not split a one-time code into controls that prevent pasting the whole code

  • Do not ask for selected characters from a password
  • Use appropriate autocomplete tokens (username, current-password, new-password,

one-time-code)

  • Provide a multi-factor path that doesn't require unsupported transcription

or a cognitive puzzle; account recovery must meet the same requirements as login

  • If a CAPTCHA or cognitive test is used, evaluate it against WCAG 3.3.8 and

provide an applicable alternative

html
<label for="username">Email address</label>
<input id="username" name="username" type="email" autocomplete="username">
<label for="password">Password</label>
<input id="password" name="password" type="password" autocomplete="current-password">
<button type="button" aria-controls="password" aria-pressed="false">
Show password
</button>

A show-password button must update its visible label/pressed state consistently, preserve focus, and not clear the value.


Serious: Grouping Related Controls

Use <fieldset> + <legend> for related controls. Missing grouping is Serious — radio and checkbox groups become unintelligible without a group label.

html
<fieldset>
<legend>Notification preferences</legend>
<label><input type="checkbox" name="email-notify"> Email</label>
<label><input type="checkbox" name="sms-notify"> SMS</label>
</fieldset>

Keep legends concise; don't use <fieldset> solely for visual styling. For a checkbox set that allows multiple selections, state that users may select more than one.


Serious: Input Types

Choose type, autocomplete, and inputmode independently according to what the value means. This surfaces the correct virtual keyboard on mobile and provides semantic meaning to AT.

InformationRecommended control
Email addresstype="email" autocomplete="email"
Telephone numbertype="tel" autocomplete="tel"
Website addresstype="url" autocomplete="url"
Search querytype="search"
Current / new passwordtype="password" autocomplete="current-password" / "new-password"
One-time codeOne text input, autocomplete="one-time-code" inputmode="numeric"
Numeric quantity with increment/decrement semanticstype="number"
Numeric-looking identifier, postal code, card numbertype="text" with matching inputmode/autocomplete
Do not treat all digits as numbers

Use type="number" only for values on which mathematical operations make sense and min/max/step are useful — never for identifiers or values that may contain leading zeros:

html
<label for="quantity">Quantity</label>
<input id="quantity" name="quantity" type="number" min="1" max="20" step="1">
<label for="postal-code">Postal code</label>
<input id="postal-code" name="postal-code" type="text" autocomplete="postal-code">

inputmode requests a virtual keyboard; it does not validate the value. Add pattern only when the resulting constraint and error message are correct for every accepted value — do not assume all users enter ASCII digits.

Date inputs

Use a native <input type="date"> when it meets the product's locale, browser, and AT requirements — test the actual implementation. For dates like date of birth, separate labelled text inputs are often easier to understand and validate than a native date picker or custom calendar widget:

html
<fieldset>
<legend>Date of birth</legend>
<p id="dob-hint">For example, 23 4 1980.</p>
<label for="dob-day">Day</label>
<input id="dob-day" name="dob-day" inputmode="numeric"
autocomplete="bday-day" aria-describedby="dob-hint">
<label for="dob-month">Month</label>
<input id="dob-month" name="dob-month" inputmode="numeric" autocomplete="bday-month">
<label for="dob-year">Year</label>
<input id="dob-year" name="dob-year" inputmode="numeric" autocomplete="bday-year">
</fieldset>

Do not build a custom calendar dialog unless users need calendar-based selection; if one is required, implement and test the complete dialog and grid keyboard interaction (see the APG Date Picker pattern).

<select> — single

Native <select> is well-supported by all AT. Always include a blank/placeholder option as the first <option> when no default is appropriate.

Multiple selection

<select multiple> has poor discoverability (Shift/Ctrl-click) and is keyboard-unfriendly. Prefer checkboxes inside a `<fieldset>` for a short, known list. <select multiple> may be appropriate for expert interfaces but must be usability-tested with the intended audience; if used, add visible instructions (Hold Ctrl (or Cmd on Mac) to select multiple options.) referenced via aria-describedby.

<datalist>

Provides autocomplete suggestions for a text input; natively keyboard accessible but announcement quality varies — test with your AT matrix. Not a replacement for a fully accessible combobox when the suggestion list is the only valid set of values — use role="combobox" with the APG Combobox pattern instead.


Serious: Autocomplete for Personal Data

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 90 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

7-day trend score
0

Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/mgifford/accessibility-skills/forms/badge)](https://verified-skill.com/skills/mgifford/accessibility-skills/forms)

Something wrong with this skill or its page? Tell us.