Why Carousels Are Hard To Get Right
Carousel widgets with auto-rotating content are everywhere, usually because teams want to fit more messaging into a confined area while still highlighting their top priorities. Unfortunately, this common component is also one of the easiest places to introduce real usability and accessibility problems. The risk is highest when design and development details get rushed, and the people most likely to feel the impact are users with cognitive, motor, or visual impairments. That user group finds complex patterns like these especially demanding to operate and interpret.
What you choose to call a carousel — flashy showcase or unnecessary complexity — matters less than the effort you put into making it actually work. A long checklist of requirements sits behind every decision, from markup to interaction logic. If you stick to them, you can avoid the worst of the “eye candy” reputation and deliver something usable.
First, Ask Whether You Need It At All
Before you build anything, it is worth remembering how poorly carousels usually perform in the wild. The Nielsen Norman Group reports that users tend to ignore auto-forwarding content entirely. According to Eric Runyon’s data, only about 1% of visitors interact with a carousel at all, and of that group, 89% only ever engage with the very first slide. Jared Smith’s answer to the “should I use a carousel?” question is blunt: “Seriously, you really shouldn’t.”
Still, not everyone takes such a hard line. The usefulness of the pattern depends on factors including its function, design, target platform (desktop against mobile) and, most of all, context. There is no universal answer. If you do decide the carousel is the right call for your specific situation, the goal is straightforward; build it so that it is both user-friendly and accessible, not just visually appealing.
Anatomy Of An Unusable Carousel
The typical carousel gets its bad name from design flaws, not from being an inherently bad idea. The biggest offenders are visually rich components that prioritise style over control. Take, for instance, the section-dial or display of multiple panels that rotate automatically. When the rotation cycles quickly through slides, reading becomes a race against time. The user sees a flash of new content, and by the time their eyes have tracked it, the slide has already changed.
Adding to the chaos is the hidden or visually complex controls that get dropped in to steer the rotation — no proper next or previous labels, no clear state indication for the current slide, and often no way to stop the motion at all. The combination of several moving parts, from the slide animation to the navigation dots, becomes a burden rather than a gift. And when the carousel contains decorative elements that merely seduce the eye, the screen-reader output can become completely confusing, announcing parts that have no business being announced as meaningful content.
A Selection Checklist For Every Carousel Part
Each piece of the carousel needs its own set of semantic requirements. The technical structure must be deliberate; otherwise, you end up with mismatched behaviour between what a mouse user sees, what a keyboard user experiences, and what a screen reader announces.
Here are the parts you need to design, name, and code:
- The stage: the wrapper for the slides. This container should be exposed to assistive tech, but only if it holds interactive content. If the stage merely gathers slides, it needs to remain unstyled for screen readers — the opposite of the common practice of making every container focusable and described.
- The slide list: the set of panels, ordered semantically. Identifying by list semantics really helps those relying on a screen reader to grasp the sequence of slides, but include only what should be read — no stray labels.
- Slide: each panel must carry identified content. Panels are often explicitly sized, which adds clipping. When a panel is focused, it should never become invisible through the combination of overflow and small fixed viewport.
- The next/prev buttons: The buttons toggle the slide from left to right, or vice versa. Screen-reader text should announce direction. The existing content — the name of the slide or that “next” carries on, or allows going forward — has to be a constant, not a variably articulated utterance.
- The “current” indicator: the inline list of dots that signals which slide is onscreen. The code has to let you know which dot is the selected one. Using a live region that keeps announcing updates for each slide change gets verbose and unmanageable, so such an announcement should happen carefully — perhaps only after interaction.
- The pause/play button: It stops all auto-rotating behaviour. It must be visually distinct and named semantically. Pausing is not just a matter of comfort; for any user with attention or coordination issues, auto-playing carousels can be simply unusable without a stop.
Design decisions should support these elements. Give each carousel a name or heading, which makes it easily skimmable and targetable by assistive tech. Slides need scalable dimensions — design for larger text sizes without dropping them from view. Remember accessibility is a design component: proper contrast ratios, identifiable focus states and sensible touch targets do more for the system than any cleverly engineered edge case.
Use a live-regioned preview only as a design layer, and keep the actual control labels robust. Too many widgets misuse rather obvious pieces of WAI-ARIA markup by copying common patterns instead of aiming for the properties that are actually required. Follow the details and the result favours all users, regardless of their abilities.
Semantics That Make Sense
Screen reader users can’t see a carousel as a single visual unit. They encounter its parts linearly: controls, headings, links, and images one after another. Without deliberate structure in the markup, those parts feel disconnected, and users have no way to build a mental model of the widget. Landmarks and group roles provide that structure.
The <section> element (or role="region") with an accessible name marks the carousel as a landmark region of the page. Screen reader users can pull up a list of landmarks and jump directly to the carousel; when they enter it in browse mode, the assistive technology announces something like “Carousel — region.” role="group" serves a similar purpose but is not a landmark, so it doesn’t appear in landmark listings. Use it for logical sets, such as custom form controls or — as in this case — for structures that are secondary to the main regions of a page. Both role="region" and role="group" require an accessible name, and both have their boundaries announced as users navigate into and out of them.
To make the announcement of a container’s meaning more specific than “region” or “grouping,” you can add aria-roledescription. The screen reader then reads that string instead of the implicit role. For a carousel wrapper, that changes the output from “Carousel — region” to something like “Design patterns — carousel.” The attribute is intended for clarifying the purpose of non-interactive container roles such as group or region; it should only be applied to elements that already have a valid implicit or explicit role. Use it cautiously — if changing the announced role would prevent users from knowing how to interact with an element, don’t do it. And avoid naming redundancy: if aria-roledescription is “carousel,” don’t also set aria-label to “carousel.”
<div role="region" aria-label="Carousel">
<!-- Slides and controls -->
</div>
The same boundary-marking approach applies to each slide. Wrap a slide’s content in role="group" and give it an accessible name via aria-labelledby, pointing to the slide’s own heading. The screen reader then announces, for example, “Slide — heading level 2 — Modal dialogs.” If a slide has no useful heading to reference, use aria-label to convey its position, such as “1 of 3.” Again, if you set aria-roledescription to “slide,” avoid the redundant aria-label value of “slide.”
<div role="group" aria-roledescription="slide" aria-labelledby="carousel_item-1_heading">
<h2 id="carousel_item-1_heading">Modal dialogs</h2>
<!-- Some more content -->
</div>
The wrapper for the slide picker controls is also a set of related items, and role="group" is the right fit there as well. Name it by its purpose, e.g. “Slide controls” or “Choose a slide.” The screen reader then announces “Slide controls — grouping” when the user enters it.
<div role="group" aria-label="Slide controls">
<!-- Slide controls -->
</div>
This structure corresponds to what the W3C’s ARIA Authoring Practices Guide calls a “grouped carousel.” An alternative is the “tabbed carousel” pattern, which uses role="tablist" for the controls, role="tab" for each picker, and role="tabpanel" for the slide container — in that case, drop aria-roledescription entirely and follow the tab pattern’s own roles and keyboard expectations.
Considerations For Auto-Rotating Carousels
Auto-advancing content creates problems that go beyond mild annoyance. Readers with cognitive disabilities may need more time to process text than the rotation allows; people with attention deficit disorder can be distracted by movement, and some people on the autistic spectrum may have to leave the page altogether. Screen reader users face a subtler issue: they may hear a heading from one slide, issue a “read next item” command, and get an element from another slide without any indication that the context has changed.
WCAG 2.1 success criterion 2.2.2 (Pause, Stop, Hide) therefore requires a way to stop the movement. The minimum is a visible “Pause” button. On top of that, make rotation pause whenever a slide is hovered — a mouse over a slide usually signals interest in its content, and you don’t want a slide change to fire just before a link activation. Rotation should also stop permanently as soon as any interactive element within the carousel receives keyboard focus or the user changes slides themselves.
Visibility And Control Usability
Carousel controls fail a large group of users when they’re hard to see. Icons for “Previous,” “Next,” and the slide dots frequently sit directly on top of images with poor contrast. Positioning those controls outside the slides is the simplest remedy. For people navigating with a keyboard, the visible focus indicator must not rely on a subtle color shift: if color alone distinguishes focused from unfocused states, the two colors need a contrast ratio of at least 3.0:1 with each other, and icons must meet the same threshold against their backgrounds. Target sizes should be generous, with enough spacing between controls to prevent accidental activation — that helps users with limited dexterity as well as anyone on a touchscreen.
The indicator for the currently displayed slide must not communicate by color alone. Progress dots are common, but a filled versus unfilled state, or another non-color cue, is necessary so users with color vision deficiencies can tell the current position from the rest.
Finally, don’t rely on swipe gestures as the only way to move between slides on touch devices — WCAG 2.5.1 (Pointer Gestures) requires that functionality be available through a simple pointer input. When no slide picker controls exist, that means providing explicit “Previous” and “Next” buttons that work with a plain click or tap.
Marking Up And Naming The Controls
Every control — “Previous,” “Next,” “Pause,” and each slide picker — needs an accessible name. For a picker control, combine the group context with a description of the target: “Show slide 1 of 4” makes sense when the surrounding group is named “Slide controls.” Alternatively, use the corresponding slide’s heading as the button’s label via visually hidden text, or provide text alternatives for the icon buttons so their purpose is announced.
The active control needs semantic, not just visual, indication. Use aria-disabled="true" rather than the HTML disabled attribute for this: aria-disabled keeps the button in the tab order, which is helpful here because users may still want to focus it to orient themselves within the set. The aria-current property set to "true" is another valid option for marking the current item.
<div role="group" aria-label="Slide controls">
<button aria-label="Show slide 1 of 4">
<!-- SVG icon -->
</button>
<!-- Some more controls -->
</div>
Keyboard And Focus Behaviour
All non-native interactive elements must be made operable. If you build controls from div or span elements, assign role="button", add tabindex="0" to get them into the tab sequence, and implement press-and-release activation with Space and Enter in JavaScript — ARIA roles don’t give elements keyboard behavior on their own.
Focus order and DOM order matter because keyboard users tab through interactive elements exactly in DOM source order, and screen reader users explore all content in that same order. The sequence should follow the page’s visual flow wherever possible, with one exception worth making for carousels:
If the controls sit below the slides in both the visual layout and the DOM, focus passing through the “Next” button or picker triggers a content change, so the user must navigate backwards to reach the content that just appeared. Putting the “Pause” button and slide pickers before the slides in the DOM gives a far easier reading and focus order, even when the controls remain positioned below the carousel via CSS:
- Pause
- Slide controls
- Previous
- Next
- Slide
Activating “Pause,” “Previous,” or “Next” must leave focus on that button so keyboard users can trigger it repeatedly and watch the content update. There is also a benefit for magnification software users if the pause control is visually placed near the start of the widget: with a magnified viewport they often can’t see the whole carousel, and locating controls that match the focus order is easier when they’re visually near its beginning.
For a “tabbed carousel,” keyboard interaction changes: users navigate among the tabs with arrow keys, and the ARIA roles must match the tab pattern so screen readers announce the expected operation correctly.
<div role="region" aria-roledescription="carousel" aria-label="Tips & Techniques">
<div role="group" aria-label="Slide controls">
<button aria-label="Stop auto-rotation">
<!-- SVG icon -->
</button>
<button aria-disabled="true">
<span class="hide-element">Show slide 1 of 3: Hiding Accessibly</span>
<!-- SVG icon -->
</button>
<button aria-disabled="false">
<span class="hide-element">Show slide 2 von 3: Accessible Contrasts</span>
<!-- SVG icon -->
</button>
<button aria-disabled="false">
<span class="hide-element">Show slide 3 von 3: Semantics, WAI-ARIA and Assistive Technologies</span>
<!-- SVG icon -->
</button>
<button aria-label="Previous slide">
<!-- SVG icon -->
</button>
<button aria-label="Next slide">
<!-- SVG icon -->
</button>
</div>
<div role="group" aria-roledescription="Slide" aria-labelledby="carousel-item-1__heading" id="carousel-item-1">
<h2 id="carousel-item-1__heading">Hiding accessibly</h2>
<!-- Further slide contents -->
</div>
<div hidden role="group" aria-roledescription="Slide" aria-labelledby="carousel-item-2__heading" id="carousel-item-2">
<h2 id="carousel-item-2__heading">Accessible Contrasts</h2>
<!-- Further slide contents -->
</div>
<div hidden role="group" aria-roledescription="Slide" aria-labelledby="carousel-item-3__heading" id="carousel-item-3">
<h2 id="carousel-item-3__heading">Semantics, WAI-ARIA and Assistive Technologies</h2>
<!-- Further slide contents -->
</div>
</div>
Hiding Off-Screen Slides And Content Change Flow
Slides that are not currently visible should be hidden from every user, not just visually: use display:none, visibility:hidden, or the HTML hidden attribute. When hidden slides are really removed from the accessibility tree, it no longer makes sense to mark the whole slide set as a list — screen readers announce the number of list items they can see, so with three of five slides hidden they would report only three items, which misrepresents the total.
Final Checklist
Before shipping a carousel, run through this list to make sure nothing critical is missed:
- Add a button to pause or stop all movement.
- Ensure that controls have sufficient contrast (meet WCAG’s color contrast requirements).
- Provide a visible focus indicator.
- Don’t rely solely on swiping. In addition, provide interaction with a simple pointer input (e.g., simple click or tap).
- Provide semantic markup and labeling so that screen readers can identify the carousel as a whole, the slides, and the set of controls. For the “grouped carousel,” use
role="group"with anaria-labeloraria-labelledbyattribute. For the carousel container, you can also use a landmark (role="region"with an accessible name) instead ofrole="group". - The
aria-roledescriptionattribute allows you to specify the meaning of the container tagged withrole="region"orrole="group". - If you go for the “tabbed carousel,” use the ARIA attributes specified in the tab pattern. Ensure keyboard navigation within the set of “tabs” can be operated with the arrow keys.
- All controls must be keyboard operable and require meaningful text alternatives for icons.
- Ensure a proper focus order. It is recommended to position at least “Pause” and slide controls in the DOM before the slides, even if this order may differ slightly from the visual tab order.
Conclusion
This article describes one way of implementing carousel widgets in an accessible way. Even W3C working groups provide different approaches in the ARIA Authoring Practices Guide (APG) and the W3C Accessibility Tutorial — see also a discussion on GitHub and the comment by Jason Web regarding user testing of “tabbed carousels” and focus management. The important thing is:
- Be familiar with semantic markup options and their impact on users.
- The objective is to provide a predictable and understandable way of operating the widget, also for non-visual users.
- Test with a keyboard and a screen reader. This will show most clearly whether you have met your goal or not.
- If you do not have the resources to implement a carousel in an accessible way — or if it is just the wrong pattern — better explore alternatives, and you might notice the benefits they entail.
“Carousels are complex components, and making complex components accessible adds more complexity.”
— Léonie Watson
Note: This article was first published in German on tollwerk.de in April 2022.
Useful Resources
- “ARIA Authoring Practices Guide (APG), Carousel,” W3C Web Accessibility Initiative
- “How To Build A More Accessible Carousel Or Slider,” Jason Web
- “The Unbearable Inaccessibility Of Slideshows,” Gian Wild
- “W3C Accessibility Tutorial,” W3C Web Accessibility Initiative (WAI)
- “Using The
aria-roledescriptionAttribute,” Léonie Watson - “Designing A Better Carousel UX,” Vitaly Friedman
- “Carousel/Slider Design Best Practices (with examples),” Vitaly Friedman (Video)
- “5 Alternatives To Using A Carousel On Your Website Homepage,” Mightybytes
- “More Alternatives To Using A Carousel On Your Website,” Mightybytes




