The homepage hero carousel is one of the most reused components on the web, and one of the most consistently broken. Three slides, striking photography, a fade or slide transition every four seconds, zero pause button in sight. It ships on almost every marketing site built in the last decade, and it almost never gets tested against a keyboard or a screen reader before launch.
Here is what that carousel does to a keyboard user trying to read the second slide's fine print: the timer does not care that they are reading. Four seconds pass, the slide changes, their place in the sentence is gone, and there is no way to pause it, because there is no pause control on the page at all. For a screen reader user, it is worse. Most auto-advancing carousels fire a live-region announcement on every transition, meaning every four seconds a new slide's content interrupts whatever else the user is trying to listen to, whether that is the carousel's own next slide or a completely different part of the page.
The Stat: WCAG 2.2 Success Criterion 2.2.2 Pause, Stop, Hide is a Level A requirement, the most basic conformance level in the standard, yet auto-advancing carousels without a visible pause control remain one of the most common homepage-hero components on the web. (Source: W3C WCAG 2.2, Success Criterion 2.2.2)
What Was Wrong With the Original Carousel
The starting point for this rebuild was a standard three-slide homepage hero: a <div>-based slider with CSS transitions, a setInterval timer advancing every four seconds, and small dot indicators for navigation. Auditing it against WCAG 2.2 surfaced four distinct, separately fixable problems.
First, no pause control existed anywhere in the markup, meaning it failed 2.2.2 Pause, Stop, Hide outright. Second, the dot indicators were plain <div> elements with click handlers, not real buttons, so they were invisible to keyboard navigation entirely, a Level A failure of 2.1.1 Keyboard. Third, each slide transition fired a generic aria-live="polite" announcement on the whole carousel container, meaning a screen reader user heard "Slide 2 of 3" interrupt them every four seconds whether they cared or not. Fourth, the active slide's focus order did not reset on transition, so a keyboard user who had tabbed into slide one's call-to-action button would find their focus silently sitting on a now-hidden, non-visible element after the slide changed underneath them.
Change One: A Real, Labeled, Keyboard-Reachable Pause Button
The rebuild's first change was the simplest and the most important: a real <button> element, visually placed beside the slide indicators, with a clear accessible name ("Pause slide rotation") and a toggled label ("Play slide rotation") once pressed. Pressing it stops the setInterval timer entirely, not just visually hides it. This single addition resolves the 2.2.2 failure completely, and it does so without removing the auto-advancing behavior for users who never touch it, which is the whole point of the criterion: give users control, don't force a static or a moving experience on everyone.
Change Two: Real Buttons for the Slide Indicators
The three dot indicators were converted from <div onclick> to real <button> elements, each with an accessible name identifying which slide it jumps to ("Go to slide 2 of 3"). This alone made the carousel fully navigable by keyboard for the first time: Tab reaches each indicator in order, Enter or Space activates it, and the active slide's indicator carries aria-current="true" so both sighted and screen reader users can tell which slide is showing without relying on color alone.
Change Three: Scoping the Live Region So It Stops Interrupting
Rather than removing the live-region announcement entirely, which would leave screen reader users with no way to know the slide changed at all, the rebuild scoped aria-live="polite" down to a small, visually hidden status element that only announces the current slide number and pause state, not the full slide content. Combined with the new pause button, a screen reader user who does not want the interruption can simply pause the rotation, the same choice a sighted user effectively already had by looking away from the carousel.
Change Four: Resetting Focus and Hiding Inactive Slides from Assistive Tech
The last fix addressed the silent-focus-loss problem. Inactive slides are now given aria-hidden="true" and tabindex="-1" on every focusable element inside them, so a screen reader and keyboard user can only ever reach content in the currently visible slide. When the active slide changes, focus is checked, and if it was resting on an element that just became hidden, it is moved to the carousel's own container rather than left dangling on an invisible node, which is what was silently breaking keyboard navigation in the original version.
What We Deliberately Did Not Change
It is worth being specific about what the rebuild left untouched, because it underscores how separable these two concerns actually are. The transition animation itself, a 400 millisecond cross-fade, stayed exactly as designed. The four-second auto-advance timing stayed the same for users who never interact with the pause control. The visual position, sizing, and styling of the dot indicators stayed identical; only their underlying element type changed, from <div> to <button>, with CSS reset to make the button look exactly like the original dot. A sighted user who never touches a keyboard would not notice a single visual difference between the before and after version. That is the actual goal of an accessibility rebuild done well: the fix lives entirely in the layer sighted mouse users never interact with directly.
Why This Matters Beyond One Carousel
None of these four fixes required redesigning the carousel visually. The photography, the transition animation, the layout, all stayed exactly as designed. Every change was structural: a real button here, a scoped live region there, a focus check on transition. That is the pattern worth taking away, not just for carousels specifically but for any custom interactive component built from scratch instead of native HTML elements: the visual design and the accessibility of the underlying interaction are almost always separable problems, and fixing the second rarely requires touching the first.
Carousels sit in the same family of custom-built interactive widgets as tooltips, and the same testing discipline that catches carousel failures catches hover-triggered tooltip failures too: try it with a mouse unplugged, then try it with a screen reader running, and see what breaks.
If your own site has an auto-advancing carousel that has never been tested this way, it is worth five minutes to find out what state it is actually in. Our free accessibility tools can flag many of these structural issues automatically, and our team is reachable at experts@wcag.world if a rebuild like this one would help your specific component. For the full technical pattern this rebuild follows, the W3C's Carousel design pattern and the official guidance on Pause, Stop, Hide are both worth reading directly.
