# Antipatterns

Common accessibility mistakes and how to fix them.

## Contents
- [Div soup](#div-soup)
- [ARIA overuse](#aria-overuse)
- [Placeholder as label](#placeholder-as-label)
- [Empty or meaningless alt text](#empty-or-meaningless-alt-text)
- [Removing focus indicators](#removing-focus-indicators)
- [Mouse-only interactions](#mouse-only-interactions)
- [Auto-playing media](#auto-playing-media)
- [Ignoring motion preferences](#ignoring-motion-preferences)
- [Color as only indicator](#color-as-only-indicator)

## Div soup

**Wrong:**
```html
<div class="nav">
  <div class="nav-item" onclick="goto('/')">Home</div>
  <div class="nav-item" onclick="goto('/about')">About</div>
</div>
```

**Right:**
```html
<nav>
  <a href="/">Home</a>
  <a href="/about">About</a>
</nav>
```

The div version requires `role="navigation"`, `role="link"`, `tabindex="0"`, keyboard handlers, and focus styles. The semantic version works out of the box.

## ARIA overuse

**Wrong:**
```html
<button role="button" aria-label="Submit form" tabindex="0">
  Submit form
</button>
```

**Right:**
```html
<button type="submit">Submit form</button>
```

Problems with the wrong version:
- `role="button"` is redundant on `<button>`
- `aria-label` duplicates visible text (use only when no visible text)
- `tabindex="0"` is redundant on `<button>`

**Rule:** If a native HTML element does what you need, use it without ARIA.

## Placeholder as label

**Wrong:**
```html
<input type="email" placeholder="Email address">
```

**Right:**
```html
<label for="email">Email address</label>
<input type="email" id="email" placeholder="e.g. user@example.com">
```

Problems with placeholder-only:
- Disappears when typing (users forget what field is for)
- Low contrast by default (hard to read)
- Not announced as field name by all screen readers
- Placeholder is hint text, not label

## Empty or meaningless alt text

**Wrong:**
```html
<img src="graph.png" alt="">
<img src="graph.png" alt="image">
<img src="graph.png" alt="graph.png">
```

**Right:**
```html
<img src="graph.png" alt="Line graph showing revenue growth from $1M to $3M over 2024">
```

- `alt=""` marks image as decorative - only use for truly decorative images
- "image", "photo", "graph" are meaningless - describe the content
- Filenames are not descriptions

**For complex images:** Provide brief alt + detailed description nearby:
```html
<figure>
  <img src="flowchart.png" alt="Application deployment workflow">
  <figcaption>
    <details>
      <summary>Flowchart description</summary>
      <p>1. Developer pushes to main branch. 2. CI runs tests...</p>
    </details>
  </figcaption>
</figure>
```

## Removing focus indicators

**Wrong:**
```css
*:focus {
  outline: none;
}

button:focus {
  outline: 0;
}
```

**Right:**
```css
button:focus {
  outline: 2px solid #005fcc;
  outline-offset: 2px;
}

/* If you must customize, provide visible alternative */
button:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(0, 95, 204, 0.5);
}
```

Focus indicators tell keyboard users where they are. Removing them makes the site unusable for keyboard navigation.

## Mouse-only interactions

**Wrong:**
```jsx
<div 
  onMouseEnter={() => setOpen(true)}
  onMouseLeave={() => setOpen(false)}
>
  <span>Hover for menu</span>
  {open && <Menu />}
</div>
```

**Right:**
```jsx
<button
  onClick={() => setOpen(!open)}
  onKeyDown={(e) => e.key === 'Escape' && setOpen(false)}
  aria-expanded={open}
  aria-haspopup="menu"
>
  Menu
</button>
{open && <Menu onClose={() => setOpen(false)} />}
```

- Hover doesn't exist on touch devices
- Keyboard users can't trigger hover
- Click/keyboard activation works universally

## Auto-playing media

**Wrong:**
```html
<video autoplay>
  <source src="intro.mp4" type="video/mp4">
</video>
```

**Right:**
```html
<video controls>
  <source src="intro.mp4" type="video/mp4">
  <track kind="captions" src="intro.vtt" srclang="en" label="English">
</video>
```

- Auto-play disrupts screen readers
- Users with vestibular disorders need motion control
- Background audio conflicts with screen reader audio
- Always provide captions for video with speech

If auto-play is required:
```html
<video autoplay muted>
```
And provide visible pause control.

## Ignoring motion preferences

**Wrong:**
```css
.hero {
  animation: parallax 2s infinite;
}

.button {
  transition: all 0.5s ease;
}
```

**Right:**
```css
.hero {
  animation: parallax 2s infinite;
}

.button {
  transition: all 0.5s ease;
}

@media (prefers-reduced-motion: reduce) {
  .hero {
    animation: none;
  }
  
  .button {
    transition: none;
  }
}
```

- Animations can trigger vestibular disorders (vertigo, nausea)
- ~35% of adults over 40 have vestibular dysfunction
- Check `prefers-reduced-motion` for: parallax, transitions, auto-playing animations, smooth scrolling
- "Reduce" doesn't mean "remove all" - subtle opacity fades are usually fine

## Color as only indicator

**Wrong:**
```html
<span class="status-good">Available</span>  <!-- green -->
<span class="status-bad">Unavailable</span> <!-- red -->
```

**Right:**
```html
<span class="status-good">✓ Available</span>
<span class="status-bad">✗ Unavailable</span>

<!-- Or with icons -->
<span class="status-good">
  <svg aria-hidden="true"><!-- checkmark --></svg>
  Available
</span>
```

- ~8% of men have color vision deficiency
- Color alone doesn't convey meaning to screen readers
- Use text, icons, or patterns in addition to color
