---
name: frontend-accessibility
description: Generates accessible HTML, React, and frontend code following WCAG 2.2 AA guidelines. Use when creating UI components, forms, navigation, or any user-facing frontend code. Focuses on semantic HTML, keyboard accessibility, and screen reader compatibility.
user-invocable: true
license: LicenseRef-MutuaL-1.2
metadata:
  author: Amolith <amolith@secluded.site>
---

Write accessible frontend code by default. Semantic HTML covers most needs; ARIA is for gaps HTML can't fill.

## Core principles

1. **Semantic HTML first**: Use `<button>`, `<nav>`, `<main>`, `<form>` - not divs with roles
2. **Keyboard accessible**: Everything clickable must be focusable and operable with Enter/Space
3. **Screen reader context**: Labels, alt text, and heading hierarchy convey structure without vision
4. **No ARIA is better than bad ARIA**: ARIA overrides native semantics and can make things _worse_

## Quick reference

### Images

```html
<!-- Informative: describe content -->
<img src="chart.png" alt="Sales increased 25% from Q1 to Q2" />

<!-- Decorative: empty alt -->
<img src="divider.png" alt="" />

<!-- Complex: summarize + link to details -->
<img
	src="flowchart.png"
	alt="User registration flow. Details in following section."
/>
```

### Interactive elements

```html
<!-- Correct: semantic button -->
<button onclick="save()">Save</button>

<!-- Wrong: div cosplaying as button -->
<div onclick="save()" role="button" tabindex="0">Save</div>
```

The div requires `role`, `tabindex`, `onkeydown` for Enter/Space, focus styles, and still won't work with voice control. Use `<button>`.

### Forms

```html
<label for="email">Email address</label>
<input
	type="email"
	id="email"
	name="email"
	required
	aria-describedby="email-hint"
/>
<span id="email-hint">We'll never share your email</span>
```

Every input needs a visible `<label>` with matching `for`/`id`. Use `aria-describedby` for hints, `aria-invalid` for errors.

### Headings

```html
<!-- One per page -->
<h1>Page title</h1>
<!-- Don't skip levels -->
<h2>Section</h2>
<h3>Subsection</h3>
<h2>Another section</h2>
```

Headings create navigable structure. Screen reader users jump by heading level.

### Links

```html
<!-- Good: describes destination -->
<a href="/pricing">View pricing plans</a>

<!-- Bad: meaningless out of context -->
<a href="/pricing">Click here</a>
```

## When to use ARIA

Use ARIA only when HTML has no semantic equivalent:

| Need           | Solution                                    |
| -------------- | ------------------------------------------- |
| Button         | `<button>` not `role="button"`              |
| Navigation     | `<nav>` not `role="navigation"`             |
| Live updates   | `aria-live="polite"` (no HTML equivalent)   |
| Current page   | `aria-current="page"` (no HTML equivalent)  |
| Expanded state | `aria-expanded="true"` (no HTML equivalent) |
| Custom widget  | ARIA + keyboard handling (last resort)      |

## Common violations to avoid

See [antipatterns.md](references/antipatterns.md) for detailed examples.

1. **Missing form labels** - Every input needs `<label for="...">`
2. **Empty alt on informative images** - `alt=""` marks image as decorative
3. **Div/span as buttons** - Use `<button>`, not `<div onclick>`
4. **Missing skip link** - Add "Skip to main content" for keyboard users
5. **Low color contrast** - 4.5:1 for normal text, 3:1 for large text
6. **Keyboard traps** - Focus must be able to leave modals/menus
7. **Missing focus indicators** - Never `outline: none` without replacement

## Motion and animation

Respect user preferences for reduced motion:

```css
/* Default: full animation */
.animated {
	transition: transform 0.3s ease;
}

/* Reduce or remove for users who prefer it */
@media (prefers-reduced-motion: reduce) {
	.animated {
		transition: none;
	}
}
```

Or start safe and enhance:

```css
/* Safe default */
.animated {
	transition: none;
}

/* Only animate if user hasn't requested reduced motion */
@media (prefers-reduced-motion: no-preference) {
	.animated {
		transition: transform 0.3s ease;
	}
}
```

Applies to: transitions, transforms, parallax, auto-playing video, animated illustrations. Essential for users with vestibular disorders.

## Framework-specific notes

### React/JSX

- `htmlFor` instead of `for`
- `className` instead of `class`
- Fragments (`<>`) don't affect accessibility tree
- Manage focus with refs when content changes dynamically

### Component libraries

If using Radix, Headless UI, or similar - they handle ARIA. Don't add redundant attributes.

## Validation

Generated code should pass:

- **axe-core**: Catches ~57% of WCAG issues automatically
- **Manual keyboard test**: Tab through everything, operate with Enter/Space
- **Screen reader spot check**: VoiceOver (Mac), NVDA (Windows), Orca (Linux)

Automated tools miss context-dependent issues (alt text quality, heading hierarchy logic, reading order). Human review still required.

## Detailed patterns

See [patterns.md](references/patterns.md) for:

- Modal dialogs and focus management
- Custom select/combobox
- Tabs and tab panels
- Accordions
- Toast notifications
- Data tables
