Skip to content

Find what you need, instantly

Search components, ARIA roles & attributes, and WCAG criteria.

Overlays

Carousel

A set of rotating slides with controls to move between them and pause automatic rotation.

Last verified against WCAG 2.2 and WAI-ARIA APG 1.2 on 2026-07-02.

Implementation

New: Team workspaces

Share projects with your whole team in one shared space.

carousel-pattern.tsx
function CarouselPattern() {
  const [index, setIndex] = useState(0);
  const [playing, setPlaying] = useState(false); // off by default
  const [announcement, setAnnouncement] = useState("");

  function goTo(next, userInitiated) {
    const wrapped = (next + SLIDES.length) % SLIDES.length;
    setIndex(wrapped);
    if (userInitiated) {
      // Never announce pure autoplay ticks — only user-driven changes.
      setAnnouncement(`Slide ${wrapped + 1} of ${SLIDES.length}: ${SLIDES[wrapped].title}`);
    }
  }

  useEffect(() => {
    if (!playing) return;
    const id = setInterval(() => setIndex(i => (i + 1) % SLIDES.length), 5000);
    return () => clearInterval(id);
  }, [playing]);

  return (
    <div role="region" aria-roledescription="carousel" aria-label="Product announcements"
         onKeyDown={e => {
           if (e.key === "ArrowRight") goTo(index + 1, true);
           if (e.key === "ArrowLeft") goTo(index - 1, true);
         }}>
      <div aria-roledescription="slide" aria-label={`${index + 1} of ${SLIDES.length}`}>
        {SLIDES[index].title}
      </div>
      <div aria-live="polite" aria-atomic="true" className="sr-only">{announcement}</div>
      <button onClick={() => goTo(index - 1, true)} aria-label="Previous slide">‹</button>
      <button onClick={() => goTo(index + 1, true)} aria-label="Next slide">›</button>
      <button onClick={() => setPlaying(p => !p)} aria-pressed={playing}>
        {playing ? "Pause" : "Play"}
      </button>
    </div>
  );
}

Live demo

New: Team workspaces

Share projects with your whole team in one shared space.

Required roles, states & properties

Required roles, states, and properties
ElementAttributeWhy
Carousel wrapperrole="region" + aria-roledescription="carousel" + aria-labelThere is no native ARIA carousel role, so region + roledescription gives AT a landmark plus a more specific spoken name ("carousel" instead of generic "region").
Each slidearia-roledescription="slide" + aria-label="N of M"Announces each slide's position, so screen reader users always know where they are, independent of visual pagination dots.
Live regionaria-live="polite" + aria-atomic="true", visually hiddenAnnounces the new slide's title after a user-initiated change. Kept silent during autoplay ticks so it doesn't interrupt users every few seconds.
Pause/Play buttonaria-pressedExposes autoplay state as a toggle button. Must always be present and operable whenever autoplay can run — required by SC 2.2.2.
Previous / Next buttonsaria-label="Previous slide" / "Next slide"Gives icon-only buttons an accessible name, since a chevron glyph alone has none.

Keyboard interaction model

Keyboard interaction model
KeyBehavior
Tab / Shift+TabMoves through the active slide's content, then the Previous/Next/Pause controls, then out to the next page element. Inactive slides are never in the tab order.
Left / Right Arrow (focus within region)Moves to the previous / next slide. Also acceptable per APG to rely on the dedicated Previous/Next buttons alone, operable via Tab + Enter.
Enter / Space (on Previous, Next, or Pause/Play button)Activates that control.

Focus management rules

  • Only the active slide's content is in the Tab order — inactive slides are never reachable, avoiding focus landing on off-screen content.
  • Tab moves through the active slide, then the Previous/Next/Pause controls, then continues to the next element on the page — the region never traps focus.
  • Changing slides via a control never forcibly moves keyboard focus away from that control.

WCAG 2.2 success criteria mapping

WCAG 2.2 success criteria that apply to this component
SCNameLevelWhy it applies
2.1.1KeyboardAAll functionality is operable through a keyboard interface with no specific timing.
2.2.2Pause, Stop, HideAMoving, blinking, or auto-updating content lasting more than 5 seconds can be paused, stopped, or hidden.
2.4.3Focus OrderAFocusable components receive focus in an order that preserves meaning and operability.
4.1.2Name, Role, ValueAFor all UI components, name, role, and value are programmatically determinable; states and changes are announced.

References