Feedback on a Pagination Pattern
Remy Sharp asked on the Mastodon about pagination as he found it in the W3C Design System:
<nav aria-label="pagination" class="l-cluster pagination">
<ul class="pagination__list">
<li><a href="path/to/page">Previous <span class="visuallyhidden">page</span></a></li>
<li><a class="pagination__list__first" href="path/to/page"><span class="visuallyhidden">page</span> 1<span
class="visuallyhidden"> (first page)</span></a></li>
<li><a class="ellipsis">…</a></li>
<li><a href="path/to/page"><span class="visuallyhidden">page</span> 6</a></li>
<li><a href="path/to/page"><span class="visuallyhidden">page</span> 7</a></li>
<li><a href="#" aria-label="page 8" aria-current="page">8</a></li>
<li><a href="path/to/page"><span class="visuallyhidden">page</span> 9</a></li>
<li><a href="path/to/page"><span class="visuallyhidden">page</span> 10</a></li>
<li><a class="ellipsis">…</a></li>
<li><a class="pagination__list__last" href="path/to/page"><span class="visuallyhidden">page</span> 20<span
class="visuallyhidden"> (last page)</span></a></li>
<li><a href="path/to/page">Next <span class="visuallyhidden">page</span></a></li>
</ul>
</nav>
He was curious if difference between the visible page numbers (as only numbers) and the expression via screen readers (as “page #”) is ok.
Broadly, yeah, it’s fine. However, I am of the opinion it is unnecessary.
How the Code Works
-
The pagination is in a navigation region (
<nav>
) and that navigation region has an accessible name of “pagination” via thearia-label
. -
Nearly every page number has a visually-hidden
<span>
“page” pre-pending the number, giving them an accessible name of “page #”. -
The first page link in the set also has a visually-hidden
<span>
“(first page)” appending the number, giving it an accessible name of “page 1 (first page)”. -
The last page link in the set also has a visually-hidden
<span>
“(last page)” appending the number, giving it an accessible name of “page 20 (last page)”. -
The link representing the current page drops that
<span>
and instead usesaria-label="page #"
and addsaria-current="page"
to programmatically indicate the link represents the current page. -
The links for the previous and next page also append visually-hidden
<span>
“page”, giving them an accessible name of “previous page” and “next page”. - Then there is the ellipsis, which represents a gap of page numbers. It’s not a link and it also has no hidden instance of “page”.
- All of this is in a list.
The Overall Experience
- A screen reader will tell the user they have entered navigation named “pagination”.
- The screen reader will tell the user every link is to a “page” number.
- The screen reader will tell the user that the link representing the current page is to “page” and is also the “current page”.
-
Both instances of
aria-label
may not auto translate. This means both the navigation region name and the current page. - A voice control user won’t know that “page” is prepended to all number links and appended to the word links.
- A voice control user won’t know that “first page” is also appended to the first number link nor that “last page” is also appended to the last number link.
- This overall pattern mixes accessible naming techniques, mixing my two least preferred approaches.
Specific Examples
This is using only desktop screen readers and only the most popular ones at that. After all, free labor.
- Using NVDA with Firefox:
- Navigation by landmark:
pagination navigation landmark list with 11 items Previous link - Pressing Tab
page 1 (first page) link
: - Pressing Tab
page 8 link current page
to the link for the current page: - Pressing Tab
lnk current page page 8
to the link for the current page, but with a Braille display:
- Navigation by landmark:
- Using JAWS with Chrome:
- Navigation by landmark:
pagination navigation region - Pressing Tab
Link Previouspage (it concatenates all of them, with novel pronunciations as a result)
: - Pressing Tab
page 8 Current Page Link (the only one it does not concatenate)
to the link for the current page: - Pressing Tab
lnk page 8 (the only one it does not concatenate)
to the link for the current page, but with a Braille display:
- Navigation by landmark:
- Using VoiceOver with Safari:
- Navigation by landmark:
pagination navigation - Pressing Tab
link, Previous page, list 11 items
: - Pressing Tab
current page, link, page 8
to the link for the current page: - Pressing Tab
current pg lnk page 8
to the link for the current page, but with a Braille display:
- Navigation by landmark:
My Recommendation
- Remove all the visually-hidden text.
- Remove all the
aria-label
s. - Use
hidden
text to name the landmark witharia-labelledby
.
Maybe something like:
<nav aria-labelledby="Pagination">
<h2 id="Pagination" class="visuallyhidden">Pagination</h2>
<ul>
<li><a href="path/to/page">Previous</a></li>
[…]
<li><a href="#" aria-current="page">8</a></li>
[…]
</ul>
</nav>
I am torn on whether the current page should even be a link.
Why
The region is named “pagination” and the aria-current
tells screen reader users that at least one of those is a page. This is really the only context users need. Voice users can benefit from an accessible name that matches what is shown. Braille display users can benefit from far briefer link text, particularly if using a display with few cells. Overall screen reader users get a less verbose experience.
I would probably ignore WCAG Success Criterion 2.4.9: Link Purpose (Link Only) (Level AAA) unless your own testing with users shows adding “page” to each link helps. In which case, do not visually hide “page”.
Understand that the pattern is not a W3C recommendation or even suggestion. It is a pattern built by the W3C’s web site vendor. You are not obligated to follow it. I did file an issue so the team could make up its own collective mind: #634 Simplify pagination pattern.
I understand your use case may differ and you may disagree with my opinions. Good for you.
4 Comments
Thank you always for your wonderful posts! Wouldn’t using the
hidden
attribute make the element excluded from machine translation?I feel it might be better to do something like the following:
Pagination
….What do you think?
In response to .ryo_manba, the comment form accepts limited HTML and no MarkDown. I think you were asking about adding the class.
Yes, nodes with
hidden
do not always auto-translate (only descendant nodes seem to in Chrome, while in Firefox they translate). I adjusted to use the class. My mistake; I was writing too fast last night. Mostly I was trying to prevent there being another heading in the DOM, but there are plenty of other ways to do that.
In response to .Ah, sorry, it seems the markup was broken.
I think it was a good decision to use the class. Thanks for the fix!
Good article. I like your recommendation except (because I like a can of worms as much as the next man), I’d make previous and next page links into buttons (visually and programmatically).
Sure they do not act as buttons, at least no more than any other link in the list, but you do it visually to give users quick and easy access to o to next/previous page, because that is the most likely action they want to take.
If you make that action visually emphasized by making it look more “buttony”, a screen reader user deserves the same affordability, after all they can quickly navigate to next/previous buttons. Also, I swear I saw this somewhere, next/previous actions in most desktop wizard or navigations are represented as buttons, so there is a tradition for this.
Leave a Comment or Response