accessible-name-carousel
Ensure that carousel elements have an accessible name.
All interactive carousel elements must have an accessible name.
Pagination and navigation items in carousels declared using non-standard elements like divs break the accessible-name-carousel
rule if they don’t have accessible names. Accessible names ensure that assistive technologies, like screen readers, can accurately identify and communicate interactive elements to users. Users of assistive technologies can navigate and interact with carousels on your website effectively if such carousel elements have accessible names.
You can use the aria-label
attribute to describe the purpose of interactive elements in carousels. Also, you can use the aria-labelledby
attribute to name visible text elements in carousels.
Example
In the following example code, the ‘prev’ and ‘next’ elements don’t have accessible names. This code breaks the accessible-name-carousel
rule as assistive technologies like screen readers cannot identify the interactive elements.
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Carousel Example</title>
<style>
.carousel {
position: relative;
width: 300px;
margin: auto;
overflow: hidden;
}
.slides {
display: flex;
transition: transform 0.5s ease-in-out;
}
.slide {
min-width: 300px;
text-align: center;
background: #f0f0f0;
padding: 20px;
box-sizing: border-box;
}
.nav {
position: absolute;
top: 50%;
transform: translateY(-50%);
background: #007bff;
color: white;
padding: 10px;
cursor: pointer;
user-select: none;
}
.prev {
left: 10px;
}
.next {
right: 10px;
}
</style>
</head>
<body>
<div class="carousel">
<div class="slides">
<div class="slide">Slide 1</div>
<div class="slide">Slide 2</div>
<div class="slide">Slide 3</div>
</div>
<!-- Non-standard navigation elements -->
<div class="nav prev"><</div>
<div class="nav next">></div>
</div>
<script>
const slides = document.querySelector('.slides');
const prev = document.querySelector('.prev');
const next = document.querySelector('.next');
let currentIndex = 0;
prev.addEventListener('click', () => {
currentIndex = (currentIndex > 0) ? currentIndex - 1 : 0;
slides.style.transform = `translateX(-${currentIndex * 300}px)`;
});
next.addEventListener('click', () => {
currentIndex = (currentIndex < 2) ? currentIndex + 1 : 2;
slides.style.transform = `translateX(-${currentIndex * 300}px)`;
});
</script>
</body>
</html>
```
Copy icon
Copy
The following sample code corrects the issue in the earlier example by adding inner texts and aria-label
:
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Carousel Example</title>
<style>
.carousel {
display: inline-block;
margin-top: 10px;
text-align: center;
font-size: 12px;
color: #333;
}
.slides {
display: flex;
transition: transform 0.5s ease-in-out;
}
.slide {
min-width: 300px;
text-align: center;
background: #f0f0f0;
padding: 20px;
box-sizing: border-box;
}
.nav {
position: absolute;
top: 50%;
transform: translateY(-50%);
background: #007bff;
color: white;
padding: 10px;
cursor: pointer;
user-select: none;
}
.prev {
left: 10px;
}
.next {
right: 10px;
}
</style>
</head>
<body>
<div class="carousel">
<div class="slides">
<div class="slide">Slide 1</div>
<div class="slide">Slide 2</div>
<div class="slide">Slide 3</div>
</div>
<!-- Non-standard navigation elements -->
<div class="nav prev" aria-label="Previous Slide"><</div>
<div class="nav next" aria-label="Next Slide">></div>
</div>
<script>
const slides = document.querySelector('.slides');
const prev = document.querySelector('.prev');
const next = document.querySelector('.next');
let currentIndex = 0;
prev.addEventListener('click', () => {
currentIndex = (currentIndex > 0) ? currentIndex - 1 : 0;
slides.style.transform = `translateX(-${currentIndex * 300}px)`;
});
next.addEventListener('click', () => {
currentIndex = (currentIndex < 2) ? currentIndex + 1 : 2;
slides.style.transform = `translateX(-${currentIndex * 300}px)`;
});
// Enable keyboard navigation
[prev, next].forEach(el => {
el.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
el.click();
}
});
});
</script>
</body>
</html>
```
Copy icon
Copy
You can also solve the issue by using the aria-labelledby
attribute as follows:
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Carousel Example</title>
<style>
.carousel {
position: relative;
width: 300px;
margin: auto;
overflow: hidden;
}
.slides {
display: flex;
transition: transform 0.5s ease-in-out;
}
.slide {
min-width: 300px;
text-align: center;
background: #f0f0f0;
padding: 20px;
box-sizing: border-box;
}
.nav {
position: absolute;
top: 50%;
transform: translateY(-50%);
background: #007bff;
color: white;
padding: 10px;
cursor: pointer;
user-select: none;
}
.prev {
left: 10px;
}
.next {
right: 10px;
}
.visually {
display: inline-block;
margin-top: 10px;
text-align: center;
font-size: 12px;
color: #333;
}
</style>
</head>
<body>
<div class="carousel">
<div class="slides">
<div class="slide">Slide 1</div>
<div class="slide">Slide 2</div>
<div class="slide">Slide 3</div>
</div>
<!-- Labels for navigation -->
<div id="prev-label" class="visually">Previous Slide</div>
<div id="next-label" class="visually">Next Slide</div>
<!-- Non-standard navigation elements with aria-labelledby -->
<div class="nav prev" aria-labelledby="prev-label"><</div>
<div class="nav next" aria-labelledby="next-label">></div>
</div>
<script>
const slides = document.querySelector('.slides');
const prev = document.querySelector('.prev');
const next = document.querySelector('.next');
let currentIndex = 0;
prev.addEventListener('click', () => {
currentIndex = (currentIndex > 0) ? currentIndex - 1 : 0;
slides.style.transform = `translateX(-${currentIndex * 300}px)`;
});
next.addEventListener('click', () => {
currentIndex = (currentIndex < 2) ? currentIndex + 1 : 2;
slides.style.transform = `translateX(-${currentIndex * 300}px)`;
});
// Enable keyboard navigation
[prev, next].forEach(el => {
el.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
el.click();
}
});
});
</script>
</body>
</html>
```
Copy icon
Copy
How to fix?
Follow these steps to fix any violations in the accessible-name-carousel
rule:
Use the aria-label
attribute on non-standard interactive elements of a carousel to describe its purpose. For example, you can name the carousel arrows as ‘previous arrow’ and ‘next arrow’.
If a visible label exists, use the aria-labelledby
attribute with the element’s id containing the visible text, to describe its purpose.
Add a title attribute that describes the element’s purpose.
Add inner text to interactive elements whenever possible.
Set the role of non-interactive elements as “none” or “presentation”.
Reference