Your pagination is probably not accessible
4 min read —
Tags:
webdev
tutorial
html
a11y
Pagination is a feature widely implemented in several visual interfaces that require listing data (like blogs, social media, and e-commerce) since it provides a better user experience and prevents the need to fetch all data from the server at once (imagine that!). But are you doing it right?
Table of contents
What do I mean by "right"?
By "right" I mean "to include all users on the web." Or are you just stacking pretty links with numbers? Or even worse: stacking buttons (we will discuss that)!
Initial code
In order to understand what is happening, let's start with a very basic version of the component, and over the article we can improve it. In this tutorial, for didactic purposes, we are going to use only HTML and CSS. No need to render the number of pages dynamically or anything like that.
<nav>
<ul class="pagination__list">
<li class="pagination__list-item">
<a class="pagination__anchor" href="path/to/page">Previous</a>
</li>
<li class="pagination__list-item">
<a class="pagination__anchor" href="path/to/page">1</a>
</li>
<li>
<a class="pagination__ellipsis">…</a>
</li>
<li class="pagination__list-item">
<a class="pagination__anchor" href="path/to/page">6</a>
</li>
<li class="pagination__list-item">
<a class="pagination__anchor" href="path/to/page">
7
</a>
</li>
<li class="pagination__list-item pagination__list-item--current">
<a href="#" class="pagination__anchor">8</a>
</li>
<li class="pagination__list-item">
<a class="pagination__anchor" href="path/to/page">9</a>
</li>
<li class="pagination__list-item">
<a class="pagination__anchor" href="path/to/page">10</a>
</li>
<li>
<a class="pagination__ellipsis">…</a>
</li>
<li class="pagination__list-item">
<a class="pagination__anchor" href="path/to/page">20</a>
</li>
<li class="pagination__list-item">
<a class="pagination__anchor" href="path/to/page">Next</a>
</li>
</ul>
</nav>
This version is a good start, actually. At least we are using semantic html correctly, explicitly saying it's a nav component, and listing the links properly. But this is far from accessible: people using assistive technologies, such as screen readers, will need to guess several things by context!
Why not buttons?
Simply put: don't frustrate your users. Someone navigating on your website expects elements to behave as they should.
A link will redirect you to a new page or section within the same page, and a button will trigger an action.
Both are focusable, but people interacting through the keyboard trigger links with the enter
key, and buttons with the space
key.
There are interesting discussions about it regarding filters that change router parameters, though. But we will keep it out of this article.
And please, no div
tags with event listeners. It requires a lot of code, it harms the readability of other developers and it's not semantic at all!
Final code
First, let's break down the improvements you will see below:
- We added an
aria-label="pagination"
because that's most likely not the only navigation component on the page, so we should explicitly differentiate it from the main nav. - Pay attention to the
.visually-hidden
class. We use it in span tags to provide more context for people using screen readers while hiding it from the visual user interface. It is implemented in accessibility-focused component libraries like Radix-ui. - For the current page, we add
aria-current="page"
as an indicator of the active link. Thearia-label="page"
provides the same additional context as the visually-hidden span.
<nav aria-label="pagination">
<ul class="pagination__list">
<li class="pagination__list-item">
<a class="pagination__anchor" href="path/to/page">Previous <span class="visually-hidden">page</span></a>
</li>
<li class="pagination__list-item">
<a class="pagination__anchor" href="path/to/page"><span class="visually-hidden">page</span> 1 <span class="visually-hidden">(first page)</span></a>
</li>
<li>
<a class="pagination__ellipsis">…</a>
</li>
<li class="pagination__list-item">
<a class="pagination__anchor" href="path/to/page"><span class="visually-hidden">page</span> 6</a>
</li>
<li class="pagination__list-item">
<a class="pagination__anchor" href="path/to/page">
<span class="visually-hidden">page</span> 7
</a>
</li>
<li class="pagination__list-item pagination__list-item--current">
<a href="#" class="pagination__anchor" aria-label="page 8" aria-current="page">8</a>
</li>
<li class="pagination__list-item">
<a class="pagination__anchor" href="path/to/page"><span class="visually-hidden">page</span> 9</a>
</li>
<li class="pagination__list-item">
<a class="pagination__anchor" href="path/to/page"><span class="visually-hidden">page</span> 10</a>
</li>
<li>
<a class="pagination__ellipsis">…</a>
</li>
<li class="pagination__list-item">
<a class="pagination__anchor" href="path/to/page"><span class="visually-hidden">page</span> 20 <span class="visually-hidden">(last page)</span></a>
</li>
<li class="pagination__list-item">
<a class="pagination__anchor" href="path/to/page">Next <span class="visually-hidden">page</span></a>
</li>
</ul>
</nav>
Here is the full CSS:
/* Small reset */
body {
box-sizing: border-box;
}
* {
padding: 0;
margin: 0;
list-style-type: none;
}
.pagination__list {
display: flex;
align-items: center;
justify-content: center;
margin-top: 20px;
gap: 5px;
}
.pagination__list-item {
background-color: #4682b4;
padding: 10px;
border-radius: 5px;
cursor: pointer;
}
.pagination__list-item--current {
background-color: #191970;
}
.pagination__anchor {
text-decoration: none;
color: #ffffff;
}
.visually-hidden {
border: 0;
padding: 0;
margin: 0;
position: absolute !important;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px 1px 1px 1px);
clip: rect(1px, 1px, 1px, 1px);
clip-path: inset(50%);
white-space: nowrap;
}
You can also check it out on code sandbox:
{% codesandbox thirsty-hypatia-h6dx3n %}
Conclusion
Keep in mind that there is always room for improvement. There are different assistive technologies, different disabilities, and different ways to interact with websites. Our main goal here is to start thinking about best practices and to build a better web for everyone, step-by-step.
Please, feel free to contribute in the comments. We are all learning!
Thank you for reading.