There are a few types of carousels.
Single item carousels are carousels where only one child element is readable at a time. Examples include the APG tabs carousel, the APG buttons carousel, slideshows, or hero carousels. There may be peeking into the next or previous items but they are not intended to be interacted with.
These carousels should:
.carousel { scroll-snap-type: inline mandatory; }
.item { scroll-snap-align: center; }
role="region" and an aria-labelon the carousel container so that the carousel is announced as a landmark.
It should also use aria-live="polite" which in combination with interactivity will announce slide changes
when using any of the scrolling mechanisms to change the active slide. E.g.
<div class="carousel" role="region" aria-label="Slideshow" aria-live="polite">
...
</div>
scroll-state query to make only the current item interactive.
As the item which is in view changes it will result in an announced change to the user.
E.g.
.item {
container-type: scroll-state;
}
.item > * {
interactivity: inert;
@container scroll-state(snapped: inline) {
> .content {
interactivity: auto;
}
}
}
role=tabpanel for the slide elements, which are inerted when not current by the scroll-state query above.
The following example demonstrates all of these practices:
A common pattern is to use scroll markers and scroll buttons to step through pages of contents. E.g. a list of products, choice of tv shows, etc. You can present this content in one of two ways.
To present as a carousel with discrete pages:
role=tabpanel element. This single tabpanel will update its content to reflect the active tab / page.
E.g.
<div role=region class="carousel" aria-label="Carousel">
<div role=tabpanel>
<div class="item">Item 1</div>
...
<div class="item">Item n</div>
</div>
<div>
@keyframes interactive-when-visible {
0% { interactivity: auto; }
}
.item {
interactivity: inert;
animation: interactive-when-visible steps(1);
animation-timeline: view(inline);
}
aria-live=polite could be confusing if users are able to scroll slowly as items will come in one by one. It should be avoided.To present as a list of contents:
<ul> element to represent the content as a list.::scroll-marker-group / ::scroll-marker pseudo-elements.Multi-item carousels are carousels where multiple child elements are readable at a time. These are tricky to match the expected tab pattern. To match it correctly use the pattern from the per-item full page carousel and ensure sufficient padding such that every item can be snapped to. The 3d carousel demo has an example of this.
If you want all of the items to be accessible, the best practices would be:
<ul> element to represent the content as a numbered list.