skills / design

fixing-accessibility

what it does

Audit and fix HTML accessibility issues including ARIA labels, keyboard navigation, focus management, color contrast, and form errors. Use when adding interactive controls, forms, dialogs, or reviewing WCAG compliance.

Verified Before and after: 2 models 1 try per case View source Download .zip

Compare the answers

Answers from Claude Sonnet 5.5 and GPT-6.1 Sol · try 1 of 1

Same request, same model, once with the skill and once without. Read both and judge for yourself.

“Our signup form fails accessibility review. What needs fixing?”

Source material given with the request

<form> <input type="email" placeholder="Email *"> <span class="error" style="color:red">Enter a valid email</span> <input type="password" placeholder="Password *"> <span class="hint">At least 12 characters</span> <button type="submit" disabled>Create account</button> </form>
with the skill
The main issues are missing labels, unannounced requirements, disconnected feedback, and an unexplained disabled action. | Snippet | Issue | Minimal fix | |---|---|---| | `placeholder="Email *"` and `placeholder="Password *"` | Placeholders are not persistent labels, and `*` does not reliably announce that fields are required. | Add visible `<label>` elements linked by `for`/`id`, and add `required`. | | `<span class="error" style="color:red">Enter a valid email</span>` | The error is not associated with the email field, and the field is not marked invalid. | Link it with `aria-describedby` and set `aria-invalid="true"` **only after validation fails**. | | `<span class="hint">At least 12 characters</span>` | Screen readers may not announce the hint when focusing the password field. | Give the hint an ID and reference it with `aria-describedby`. Add `minlength="12"` for validation. | | `<button type="submit" disabled>` | Users cannot focus or activate the button to discover validation problems, and there is no explanation for its disabled state. | Prefer keeping it enabled and validating on submit. If it must stay disabled, provide a visible explanation. | ### Minimal corrected markup This example shows the form **after email validation has failed**: ```html <form> <label for="email">Email (required)</label> <input id="email" name="email" type="email" autocomplete="email"
Show the rest (31 more lines)
required aria-invalid="true" aria-describedby="email-error" > <span id="email-error" class="error" aria-live="polite"> Enter a valid email </span> <label for="password">Password (required)</label> <input id="password" name="password" type="password" autocomplete="new-password" required minlength="12" aria-describedby="password-hint" > <span id="password-hint" class="hint"> At least 12 characters </span> <button type="submit">Create account</button> </form> ``` **Validation behavior matters too:** - Initially, omit `aria-invalid` and the email error text. Set them when validation fails, and clear them when corrected. - For dynamic announcements, keep the live-region container in the DOM before updating its text. - On an unsuccessful custom-validation submission, focus the first invalid field. - Ensure error text has sufficient contrast—“red” alone does not guarantee this—and preserve visible keyboard focus indicators.
without
The main issues are missing labels, missing programmatic relationships, and a disabled submit button that offers no explanation. ### What to fix - **Add persistent labels.** Placeholders disappear when users type and aren’t a substitute for `<label>`. Connect each label to its input using `for` and `id`. - **Mark required fields programmatically.** An asterisk alone doesn’t communicate a requirement reliably. Add `required` and explain required fields in visible text. - **Associate errors and hints with their fields.** Use `aria-describedby` so screen readers announce the email error and password requirements when the relevant input receives focus. - **Expose validation state.** Set `aria-invalid="true"` on an input only after validation has found an error. Don’t show “Enter a valid email” before the user has had a chance to enter one. - **Announce dynamically displayed errors.** Use an appropriate live region or move focus to an error summary after unsuccessful submission. Avoid announcing errors on every keystroke. - **Check color contrast.** The error already includes text, so it isn’t conveyed by color alone. However, its text must meet contrast requirements—typically **4.5:1** for normal-sized text. Red on white, for example, is about 4:1 and fails that threshold. - **Reconsider the disabled submit button.** Disabled controls generally aren’t keyboard-focusable, and this one doesn’t explain how to enable it. Prefer allowing submission, then showing clear validation feedback. A disabled button isn’t automatically an accessibility failure, but it can make completion difficult. - **Add autocomplete and actual constraints.** Use `autocomplete="email"` and `autocomplete="new-password"`, and enforce the stated password minimum with `minlength="12"`. Add `name` attributes so the values can be submitted. ### Improved starting point ```html <form action="/signup" method="post"> <p>All fields are required.</p> <div>
Show the rest (34 more lines)
<label for="email">Email</label> <input id="email" name="email" type="email" autocomplete="email" required > <p id="email-error" class="error" hidden> Enter a valid email address. </p> </div> <div> <label for="password">Password</label> <input id="password" name="password" type="password" autocomplete="new-password" minlength="12" aria-describedby="password-hint" required > <p id="password-hint">At least 12 characters.</p> </div> <button type="submit">Create account</button> </form> ``` This uses native browser validation as a baseline. If you implement custom validation, reveal the error, add `aria-invalid="true"` and `aria-describedby="email-error"` to the email input, and make sure the error is announced or receives appropriate focus. Clear those error states when the field is corrected. Also test the rendered form for **keyboard operation, visible focus, zoom/reflow, and screen-reader announcements**—the snippet alone can’t establish those.

Answers are shown exactly as the model wrote them, from the first try, and nothing is picked by hand.

The models

2 models

Claude Sonnet 5.5

made by Anthropic

claude-sonnet-5-5 · revision not reported by the provider

1 try per case · tested 3 Nov 2026

GPT-6.1 Sol

made by OpenAI

gpt-6.1-sol · revision not reported by the provider

1 try per case · tested 3 Nov 2026

The tasks

written by Carte Blanche
accordion-minimal-fix

“Fix the accessibility issue in this accordion. Please keep the change small, we can't refactor this component right now.”

contrast-and-focus-css

“Accessibility audit on our marketing site CSS, please.”

custom-modal-focus

“Here's our custom modal. Keyboard users say it's hard to use. What's wrong and how do I fix it?”

signup-form-errors

“Our signup form fails accessibility review. What needs fixing?”

toolbar-names-and-buttons

“Can you check this toolbar for accessibility problems?”

Install

Download the .zip and unzip it. Put its files, in a folder named fixing-accessibility, here:

Claude Code~/.claude/skills/Every project. For one project only, use .claude/skills/ inside it.
Codex~/.agents/skills/Every project. For one repository only, use .agents/skills/ inside it.
Cursor~/.cursor/skills/Cursor also reads the Claude Code and Codex folders above.

Then start a new session and ask for what the skill does. Other agents that read SKILL.md have their own skills folder; check their docs.

Coming after launch: get skills from inside your agent, no download needed.