react-responsive-carousel (100%)
A11y Score 100% (opens in new tab)
Version Tested 3.2.23
GitHub Stars 2.7k
Nav Style Below carousel
Dots/Pagination Custom (DIY)
Built-in showIndicators uses li[role=button] (a11y violation). Using custom controls instead.
Summary
- No workarounds required — use custom controls
- Built-in
showIndicatorsusesli[role=button](invalid ARIA) — avoid it - All reported issues resolved, but 100% score is misleading
Accessibility Analysis
Score: 100% — All 22/22 accessibility issues resolved Median resolution time: 259.6 days
Caveat
100% score is misleading — the closed issues reveal poor accessibility architecture in the library. Known problems:
role="button"on<li>elements — invalid ARIA- Carousel root receives unwanted focus
- Missing focus styles on control arrows
- Visual elements (dots, arrows) read to screen readers without
aria-hidden - Invalid HTML structure during render phases
Implementation Notes
Use custom controls instead of built-in showIndicators and showArrows to avoid a11y issues.
The Problem with Built-in Controls
The library’s showIndicators prop renders pagination as:
<ul class="control-dots">
<li role="button" class="dot">...</li> <!-- Invalid ARIA! -->
</ul>
Using role="button" on <li> elements is invalid. Buttons should be <button> elements.
Our Approach
Disable built-in controls and implement custom ones:
<Carousel
selectedItem={currentSlide}
onChange={setCurrentSlide}
showArrows={false}
showIndicators={false}
showThumbs={false}
showStatus={false}
infiniteLoop={true}
>
{/* slides */}
</Carousel>
{/* Custom prev/next buttons */}
<div className="flex justify-center gap-4 mt-4">
<button onClick={prev}>Previous</button>
<button onClick={next}>Next</button>
</div>
{/* Custom dots with proper ARIA */}
<div className="flex justify-center gap-2 mt-4">
{slides.map((_, i) => (
<button
key={i}
onClick={() => goTo(i)}
aria-label={`Go to slide ${i + 1}`}
aria-current={i === currentSlide ? 'true' : undefined}
/>
))}
</div>
Container Accessibility
Added to our implementation:
role="region"on wrapperaria-label="Featured content carousel"on wrapper