---
id: seed:conventions/accessibility-basics
title: Accessibility basics
source_type: seed_convention
priority: warm
---

Accessibility is not optional. Generated markup must meet WCAG 2.1 AA basics; anything less excludes users and exposes the site owner to legal risk in most jurisdictions.

**Semantic landmarks.** Use the elements that already mean what you mean.

- `<header>` for site header / banner
- `<nav>` for primary navigation, with `aria-label="Primary"`
- `<main>` once per page, wrapping the page content
- `<section>` for distinct content groups, with a heading inside each
- `<footer>` for site footer
- `<aside>` for tangential content (sidebars, callouts)

**Heading order.** One `<h1>` per page (usually the hero). Below it, `<h2>` for section titles, `<h3>` for card titles inside sections. Never skip levels for visual effect — use CSS to change size.

**Alt text on every image.** `alt=""` for purely decorative images (so a screen reader skips them). Descriptive alt text for anything informational. Never use the filename as alt text.

**Focus states.** Every interactive element must have a visible `:focus-visible` style. The browser default is often invisible on custom buttons. Use a contrasting outline:

```css
.cta-button:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
```

**Color contrast.** Body text must have a contrast ratio of at least 4.5:1 against its background. Large text (18px+ or 14px bold+) needs 3:1. Tools: WebAIM Contrast Checker. Don't rely on light gray text on white backgrounds — common AI default, common WCAG fail.

**Form labels.** Every input needs a `<label for="id">`. Placeholders are NOT labels — they disappear on focus and confuse screen readers. Required fields get `aria-required="true"` (visual asterisk separately, with sr-only "required" text if needed).

**Reduced motion.** Wrap motion-heavy animations in:

```css
@media (prefers-reduced-motion: reduce) {
  .scroll-reveal { animation: none; opacity: 1; }
}
```

**Skip links.** A "Skip to main content" link as the first focusable element. Hidden visually until focused, jumps focus past the nav.

**Don't trap focus.** Modals and dropdowns must release focus to the trigger on close. Esc must close any modal/popover. Visitors with keyboards rely on this.
