Accessibility
Accessibility
Embla Carousel is designed to be lightweight and unopinionated. By default, it provides the carousel logic and physics but does not automatically inject ARIA attributes or manage keyboard focus. This ensures you have full control over the markup to meet specific WCAG (Web Content Accessibility Guidelines) requirements.
To create an accessible carousel, you should focus on three areas: ARIA roles, Keyboard navigation, and Motion preferences.
Recommended ARIA Attributes
To make the carousel identifiable to screen readers, apply the following roles and attributes to your HTML structure.
Carousel Container
The outermost element should be defined as a carousel region.
role="region": Identifies the section as a landmark.aria-roledescription="carousel": Provides a more specific description than "region".aria-label: A descriptive title (e.g., "Latest News" or "Product Gallery").
Slide Items
Each slide should be treated as a group within the carousel.
role="group": Defines the slide as a distinct set of content.aria-roledescription="slide": Clarifies the role of the group.aria-label: Indicates the current slide position (e.g., "1 of 5").
Example Structure
<section
class="embla"
role="region"
aria-roledescription="carousel"
aria-label="Product Showcase"
>
<div class="embla__viewport">
<div class="embla__container">
<div
class="embla__slide"
role="group"
aria-roledescription="slide"
aria-label="1 of 3"
>
<!-- Slide Content -->
</div>
<!-- ... -->
</div>
</div>
</section>
Keyboard Navigation
While Embla handles touch and mouse drag, keyboard interaction must be implemented using the API methods.
Interactive Controls
Ensure that "Previous" and "Next" buttons are actual <button> elements. This provides native tab focus and "Enter/Space" activation.
const prevBtn = document.querySelector('.embla__prev')
const nextBtn = document.querySelector('.embla__next')
prevBtn.addEventListener('click', () => emblaApi.scrollPrev(), false)
nextBtn.addEventListener('click', () => emblaApi.scrollNext(), false)
Managing Focus
When a user navigates via buttons, the focus should remain on the button. If you implement "Dot" navigation, ensure each dot has an aria-label indicating which slide it controls (e.g., aria-label="Go to slide 2").
Live Regions
Use an aria-live region to announce slide changes to screen reader users. You can update a visually hidden element using the select event.
const logStatus = (emblaApi) => {
const index = emblaApi.selectedSnap() + 1
const count = emblaApi.scrollSnapList().length
statusElement.innerText = `Showing slide ${index} of ${count}`
}
emblaApi.on('select', logStatus)
Reducing Motion
Users with vestibular motion disorders may prefer reduced animation. Embla Carousel respects the duration option, which can be adjusted based on the user's system preferences.
Using CSS
You can use a media query to disable smooth scrolling or transitions on the container:
@media (prefers-reduced-motion: reduce) {
.embla__container {
transition: none !important;
}
}
Using JS Configuration
You can detect the preference in JavaScript and pass a 0 duration to the Embla options to snap slides instantly.
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches
const emblaApi = EmblaCarousel(viewportNode, {
duration: prefersReducedMotion ? 0 : 25
})
Autoplay Considerations
If you are using the Autoplay plugin, accessibility standards require:
- A way to stop/pause: Provide a visible "Pause" button.
- Pause on Interaction: The plugin should be configured to pause on hover or focus so users can read the content without the slide changing unexpectedly.
import Autoplay from 'embla-carousel-autoplay'
const emblaApi = EmblaCarousel(viewportNode, { loop: true }, [
Autoplay({ stopOnInteraction: true, stopOnMouseEnter: true })
])