| name | web-accessibility |
| description | WCAG 2.1 AA accessibility standards for this landing page. Use when building interactive components, navigation, forms, or reviewing accessibility compliance. |
Web Accessibility (WCAG 2.1 AA)
Semantic HTML Structure
<body>
<a class="skip-link" href="#main">Skip to content</a>
<header>
<nav aria-label="Main navigation">...</nav>
</header>
<main id="main">
<section aria-labelledby="hero-heading">...</section>
<section aria-labelledby="features-heading">...</section>
</main>
<footer>...</footer>
</body>
Rules:
- Use semantic elements:
header, nav, main, section, footer, article
- Every
<section> has aria-labelledby pointing to its heading
- One
<main> per page
- One
<h1> per page, sequential heading levels (h1 > h2 > h3)
- Never use
<div> or <span> for interactive elements
Focus Management
Visible focus indicators
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-primary)] focus-visible:ring-offset-2"
All interactive elements MUST have visible focus states. Use focus-visible (not focus) to avoid showing rings on mouse click.
Skip-to-content link
<a href="#main" className="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-50 focus:px-4 focus:py-2 focus:bg-[var(--color-primary)] focus:text-white focus:rounded-md">
Skip to content
</a>
Mobile menu focus trap
When mobile menu opens:
- Focus moves to first focusable element inside
- Tab cycles within the menu (trap)
Escape closes the menu
- Focus returns to the trigger button on close
Keyboard Navigation
- All interactive elements reachable via
Tab
Enter/Space activates buttons and links
Escape closes modals, menus, and overlays
- Arrow keys for custom widgets (tabs, dropdowns) if applicable
- Tab order follows visual order (no positive
tabIndex)
Color Contrast
WCAG AA minimum ratios:
- Normal text (< 18px / < 14px bold): 4.5:1
- Large text (≥ 18px / ≥ 14px bold): 3:1
- UI components and graphics: 3:1
Verified pairings for this project:
#CDD7DC on #131A20 — 10:1 (dark theme text)
#2C2F33 on #EFE9DD — 11:1 (light theme text)
- White on
#FA2F4C — 4.6:1 (dark CTA button text)
- White on
#C4983B — 4.5:1 (light CTA button text)
#E1BE7F on #EFE9DD — 1.4:1 FAILS (never use as text on cream)
ARIA Attributes
aria-label on elements without visible text (icon buttons, logo links)
aria-expanded on toggle buttons (hamburger menu, theme toggle)
aria-hidden="true" on decorative icons/images
aria-current="page" on active navigation links
role="img" + aria-label on SVG icons that convey meaning
- Never use ARIA to fix what semantic HTML solves
Interactive Elements
Buttons
- Always use
<button> with explicit type="button" or type="submit"
- Icon-only buttons MUST have
aria-label
- Minimum touch target: 44x44px on mobile
Links
- Use
<a> for navigation, <button> for actions
- External links: add
rel="noopener noreferrer" and indicate external
- Never use empty
href="#" — use <button> instead
Forms (CTA email capture)
- Every input has a visible
<label> with htmlFor
- Error messages linked via
aria-describedby
- Invalid inputs marked with
aria-invalid="true"
- Submit button clearly labeled
Images & Icons
- Meaningful images: descriptive
alt text
- Decorative images:
alt=""
- Icon-only elements:
aria-hidden="true" on icon + aria-label on parent
next/image handles alt requirement
Reduced Motion
Respect prefers-reduced-motion: reduce:
const prefersReducedMotion = typeof window !== "undefined"
? window.matchMedia("(prefers-reduced-motion: reduce)").matches
: false;
When active, skip all Framer Motion entrance/exit animations.
Testing Checklist