React Slick (57.4%)
A11y Score 57.4% (opens in new tab)
Version Tested 0.31.0
GitHub Stars 12.0k
Nav Style Below carousel
Dots/Pagination Custom (DIY)
Built-in dots use improper list structure. aria-hidden traps focus. Custom controls with tabindex management needed.
Accessibility Fixes Applied
- tabindex management for hidden slides (library traps focus with aria-hidden)
Summary
- Workaround required: Library sets
aria-hidden="true"on inactive slides but focusable elements inside remain tabbable, creating focus traps - Fix: Manage
tabindexon focusable elements viauseEffect - Built-in dots use
liwithrole="button"(invalid ARIA) — use custom controls if pagination needed
Accessibility Analysis
Score: 57.4% — 35/61 accessibility issues resolved Median resolution time: 558.8 days Oldest unresolved issue: 3,178 days (8+ years)
Open Issues (26)
aria-hidden="true"combined with focusable elements — breaks ARIA rules- Focus doesn’t move properly through slides
- TalkBack and VoiceOver report poor accessibility on mobile
Our Workaround
React Slick sets aria-hidden="true" on inactive slides but doesn’t manage tabindex on focusable elements inside. This creates a focus trap.
useEffect(() => {
if (!containerRef.current) return
const slideElements = containerRef.current.querySelectorAll('.slick-slide')
slideElements.forEach((slide) => {
const focusables = slide.querySelectorAll('a, button, input, select, textarea')
const isActive = slide.classList.contains('slick-active')
focusables.forEach((el) => {
el.setAttribute('tabindex', isActive ? '0' : '-1')
})
})
}, [currentSlide])
Custom Controls
Built-in dots use <li> with role="button" (invalid ARIA). Use custom buttons:
<Slider ref={sliderRef} dots={false} arrows={false}>
{/* slides */}
</Slider>
{/* Custom controls */}
<button onClick={() => sliderRef.current?.slickPrev()}>Previous</button>
<button onClick={() => sliderRef.current?.slickNext()}>Next</button>