How to build an accessible Carousel in React
A section with aria-roledescription="carousel" holding one role="group" per slide. Inactive slides are `inert`, not `aria-hidden`: they stay in the layout and remain scroll-reachable, so aria-hidden alone declared them non-existent to assistive technology while every link and button inside them was still tabbable — the canonical aria-hidden-focus violation. The track is aria-live="polite" when the user drives the carousel and "off" while it rotates on its own, per APG; a rotating carousel always renders a play/pause control, and rotation additionally pauses on hover, on focus within, while the tab is hidden and under prefers-reduced-motion. Reduced motion is read in JS as well as CSS because a scrollTo `behavior` option overrides the stylesheet, which is why the previous build's stylesheet rule could never take effect. Arrow keys on the indicator row move the slide, not merely the roving tabindex, and the indicators are named as their own group rather than repeating the region's name. The indicator hit area reaches the target minimum through a pseudo-element, leaving the 10px visual mark unchanged.
When to use a Carousel
- Presenting a sequence of media or cards the user pages through one at a time
- Image galleries, onboarding decks, or featured-content rotators
- When horizontal swiping/scrolling is a natural interaction on touch devices
When not to use it
- Content users must compare side-by-side — show a grid instead
- Critical information that must always be visible — carousels hide most of their content
Keyboard interactions
Role group, verified at WCAG 2.2-AA.
ArrowLeftArrowRightHomeEnd
Common mistakes
Avoid: Auto-rotating carousel for primary navigation or key calls-to-action
Prefer: A static list or grid where every item is visible
Auto-rotation and hidden slides reduce discoverability and accessibility of important content
Example
<Carousel>
<img src="/1.jpg" alt="" />
<img src="/2.jpg" alt="" />
</Carousel>