From harness-claude
Builds accessible forms with proper labeling, grouped controls, inline validation (aria-describedby/aria-errormessage), and error summaries linked to inputs.
How this skill is triggered — by the user, by Claude, or both
Slash command
/harness-claude:a11y-form-patternsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> Build accessible forms with proper labeling, grouped controls, inline validation, and clear error communication
Build accessible forms with proper labeling, grouped controls, inline validation, and clear error communication
<label>. Use htmlFor (React) or for (HTML) to link the label to the input. Clicking the label should focus the input.<label htmlFor="email">Email address</label>
<input id="email" type="email" name="email" />
Never use placeholder text as a substitute for a label — it disappears when the user starts typing and has poor contrast.
<fieldset> and <legend> to group related controls. Screen readers announce the legend as context for each control within the group.<fieldset>
<legend>Shipping Address</legend>
<label for="street">Street</label>
<input id="street" name="street" />
<label for="city">City</label>
<input id="city" name="city" />
</fieldset>
<fieldset>
<legend>Payment method</legend>
<label><input type="radio" name="payment" value="card" /> Credit card</label>
<label><input type="radio" name="payment" value="paypal" /> PayPal</label>
</fieldset>
required attribute for native validation and aria-required="true" for custom validation. Include a visible indicator (asterisk with explanation).<label htmlFor="name">
Full name <span aria-hidden="true">*</span>
</label>
<input id="name" required aria-required="true" />
<p className="form-note">Fields marked with * are required.</p>
aria-invalid to mark the field and aria-describedby or aria-errormessage to point to the error text.<label htmlFor="password">Password</label>
<input
id="password"
type="password"
aria-invalid={!!errors.password}
aria-describedby={errors.password ? 'password-error' : 'password-hint'}
/>
<p id="password-hint">Must be at least 8 characters.</p>
{errors.password && (
<p id="password-error" role="alert" className="error">
{errors.password}
</p>
)}
Use aria-live or role="alert" for dynamic validation messages. When errors appear after form submission or as the user types, screen readers must be notified.
Provide an error summary at the top of the form on submission failure. List all errors with links to the corresponding fields. Move focus to the summary.
{
errors.length > 0 && (
<div role="alert" tabIndex={-1} ref={errorSummaryRef}>
<h2>There are {errors.length} errors in this form</h2>
<ul>
{errors.map((err) => (
<li key={err.field}>
<a href={`#${err.field}`}>{err.message}</a>
</li>
))}
</ul>
</div>
);
}
autocomplete attributes for common fields. This enables browser autofill and password managers, which benefit users with motor and cognitive disabilities.<input name="name" autocomplete="name" />
<input name="email" autocomplete="email" />
<input name="tel" autocomplete="tel" />
<input name="address" autocomplete="street-address" />
<input name="cc-number" autocomplete="cc-number" />
Do not disable the submit button while the form is incomplete. Disabled buttons are not focusable and provide no feedback about what is wrong. Instead, allow submission and show validation errors.
Support keyboard submission. Forms should submit when the user presses Enter in a text input. Use <form> with a <button type="submit"> — do not rely on JavaScript click handlers alone.
Use inputmode and type to show the right keyboard on mobile. type="email" shows an email keyboard, inputmode="numeric" shows a number pad.
Label association methods (in order of preference):
<label for="id"> — explicit association, most reliable<label> wrapping the input — implicit association, works in all browsersaria-label — invisible label, use only when no visible label is possiblearia-labelledby — references another element as the labeltitle attribute — last resort, announced inconsistentlyMulti-step forms: Indicate progress with a step indicator using aria-current="step". Announce step transitions with aria-live. Allow backward navigation without losing data.
Custom select/dropdown: Native <select> is fully accessible. Custom dropdowns must implement the combobox or listbox pattern with full keyboard support and ARIA attributes. Consider whether the customization is worth the complexity.
Common mistakes:
placeholder instead of <label> (disappears, poor contrast)https://www.w3.org/WAI/tutorials/forms/
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeEnsures all form inputs are correctly labeled, errors are programmatically associated, and the form is operable with keyboard and screen reader.
Designs form labels, instructions, and grouping for screen reader and cognitive accessibility. Use when creating or reviewing forms, input fields, checkboxes, radio buttons, select menus, or any data entry interface.
Accessibility patterns for React and Next.js covering semantic HTML, ARIA attributes, form labeling, keyboard navigation, focus management, and screen reader support.