Search topics
Pagination links that don't say which page or where they go
A shop's page links mark the current page only with a class, and the links themselves are bare numbers and arrows, so a screen reader can't tell which page you are on or where each link goes.
<a href="/kitchen/mugs?page=3">›</a> The problem
Screen reader users can't tell which page of a list they are on, or where the arrow links go, because the page links say it only by their look.
A shop's category page for mugs ends with a row of page links: an arrow back, the numbers 1 to 4, and an arrow forward. The design is tidy and familiar. On the second page the 2 sits in a dark square, so a sighted shopper sees at once that they are on the second of four pages, and that the arrows lead back to page 1 and on to page 3.
A screen reader user pressing Tab through the same row hears "‹, link", "1, link", "2, link", "3, link", "4, link", "›, link". Nothing says that 2 is the page of results being shown, or that the numbers are pages at all. The arrows are read by whatever name the screen reader or its voice has for the symbol, or not at all, depending on the reader and its punctuation setting, so the link that means Next page says nothing about where it goes. The row is not announced as navigation and doesn't appear in the screen reader's list of landmarks, so there is no way to jump to the page links at the end of a long grid of products.
The page links show relationships: they are a set of links for moving through one list, and one of them is the page being shown. WCAG 1.3.1 Info and Relationships requires information shown by presentation to be available in the code, or in the text. Here the grouping is only in the layout, because the row is a div, which groups nothing for assistive technology; technique H97 meets 1.3.1 by putting related links in a nav. The current page is only in the look, because the current class means nothing to the browser.
The links also fail 2.4.4 Link Purpose (In Context). A link's purpose must be clear from its text, or from its text with its programmatically determined context: the sentence, paragraph, list item or table cell it sits in, or the header cell of that cell. "›" says nothing about where it goes, and "2" doesn't say it is a page of these results. The eye takes that meaning from where the row sits under the grid and from what the arrows usually mean; the markup carries neither, and the div around the links gives a screen reader no context to reach.
Pagination sits under nearly every shop category, search results page, blog archive and forum, and it usually comes from one shared component, so one fix reaches every list on the site. It matters most on long lists, where people need to know how far through they are and how much is left.
2 of 10 groups affected
- No vision (affected)
- Low vision (not affected)
- Colour vision (not affected)
- No hearing (not affected)
- Hard of hearing (not affected)
- No speech (not affected)
- Motor (affected)
- Reach, strength (not affected)
- Cognitive (not affected)
- Seizures (not affected)
- Without vision A screen reader reads the page numbers like any other links, so the person can't tell which page they are on, and hears a symbol's name, or nothing, where the eye sees Next. With no named group to jump to, the page links at the end of a long list are found only by reading through it.
- Limited manipulation Someone using voice control says "click Next" and nothing answers, because the link is named "›". They have to show numbers over every link on the page and pick one instead.
A scanner finds links that all have names, "2" and "›" included, and passes them; an empty link would fail, a symbol doesn't. It can't tell that one link is drawn as current, because a class called current or active means nothing to it, and it can't judge whether a number says what it leads to. EaseWeb's audit reports a link to the page being shown, in page links named so by their class or name, that is drawn unlike the links beside it and has neither aria-current nor text saying it is current, and a link whose whole name is symbols with no words in the sentence, list item or cell around it. Page links outside any navigation landmark go to a person as a review lead, with their links; bare numbers such as "2" are left to that person too. Pagination written by hand, copied from a component library or generated by an AI tool needs the same check: listening to the page links on two pages is the only way to confirm they say where you are and where they go.
Try it
Press Tab to move through the page links, and Enter to go to a page.
The panel reads the page links as a screen reader does. Can you tell which page you are on, and where each arrow goes?
The fix
Put the page links in a named nav, say the current page in code or text, and name every link in words.
- 1Put the page links in a
navwith its own short name. - 2Mark the current page with
aria-current="page", or state it in text, and draw the look from the same condition. - 3Give every page link a name in words, such as "Page 2" or "Next page", whatever it shows.
<h1>Mugs</h1>…<div class="pagination"> <a href="/kitchen/mugs?page=1">‹</a> <a href="/kitchen/mugs?page=1">1</a> <a href="/kitchen/mugs?page=2" class="current">2</a> <a href="/kitchen/mugs?page=3">3</a> <a href="/kitchen/mugs?page=4">4</a> <a href="/kitchen/mugs?page=3">›</a></div>A named nav, aria-current and hidden names
<h1>Mugs</h1>…<nav class="pagination" aria-label="Pagination"> <ul> <li> <a href="/kitchen/mugs?page=1"> <span aria-hidden="true">‹</span> <span class="visually-hidden">Previous page</span> </a> </li> <li> <a href="/kitchen/mugs?page=1"> <span class="visually-hidden">Page </span>1 </a> </li> <li> <a href="/kitchen/mugs?page=2" aria-current="page"> <span class="visually-hidden">Page </span>2 </a> </li> <li> <a href="/kitchen/mugs?page=3"> <span class="visually-hidden">Page </span>3 </a> </li> <li> <a href="/kitchen/mugs?page=4"> <span class="visually-hidden">Page </span>4 </a> </li> <li> <a href="/kitchen/mugs?page=3"> <span aria-hidden="true">›</span> <span class="visually-hidden">Next page</span> </a> </li> </ul></nav>Why this fix: Style from the state, not a class
The current class gives the 2 its look and nothing else, and the div groups nothing. A screen reader reads "2, link" like its neighbours, and "‹, link" and "›, link" for the arrows.
The design is unchanged: every number and arrow looks as it was drawn. The row is now a nav with its own name, so a screen reader announces "Pagination, navigation" on the way in and lists it among its landmarks. aria-current="page" on the 2 makes screen readers say "current page" there, and the dark square now selects on the attribute, so the look and the code can't mark different links. The hidden text names the links: "Page 2", "Previous page" and "Next page". The arrows themselves are hidden with aria-hidden, so their symbols are not read on top of the names.
Both pass 1.3.1 and 2.4.4. Keep the numbers, with aria-current and hidden names, when lists run long or people jump around in them, such as search results and archives: the design stays as drawn, and the look comes from the attribute. Use Previous page and Next page around the page count when lists are short, when the numbers don't fit on a phone anyway, or when you want every name in plain sight: nothing is hidden, and voice control users can say exactly what they see.
The page links have a few more choices.
aria-labelinstead of hidden text:<a href="…" aria-label="Page 2">2</a>gives the same name. Browser translation has been less reliable with anaria-labelthan with text in the page.- The name: "Pagination" is enough, because the screen reader adds "navigation" itself; see navigation landmarks that all sound the same. When the same page links appear above and below the list, give the two
navelements different names, or leave the top row out of anav. - The first and last page: on page 1, leave Previous page out or show it as plain text, not a link that leads to the same page or nowhere. An ellipsis between page numbers stays as text in a list item of its own, outside any link.
- Lists that change without a page load: when sorting, filtering or a page link swaps the results in place, move
aria-currentwith them, and say that the list changed; see single-page app route changes that are never announced. - Page titles: "Mugs, page 2 of 4" in the page's title tells screen reader users where they are as soon as the page loads, which 2.4.2 Page Titled covers in page titles that are the same on every page.
Keep the current page visible in a Windows contrast theme. Forced colors drop the dark fill behind the 2, so it would look like the other numbers; its bold weight survives, and so would a border drawn thicker than the others'.
Fixing this with an AI coding assistant? Get this guide as Markdown, or get every guide in your coding agent.
Design ideas
Page links can be drawn in more than one look.
Three more ways to draw them. Point at the links, Tab through them, and click a page to move the mark there. Two pass; one draws the pages as dots, so no link has a word to be read or said.
In the design file
- Write the name of every page link beside it, such as Page 2, Previous page and Next page, even where only a number or an arrow shows.
- Draw the page you are on as its own state, marked by more than a fill, such as bold type or a line, because a Windows contrast theme drops fills.
- Draw the first and the last page too, where one of the arrows has nowhere to go.
Passes
- Bold number on a bar, words at the ends The 2 is bold #1d1d1f with a 3px bar under it, 16.83:1 on white, and both the weight and the bar stay in a contrast theme. Previous and Next are written out, so voice control users can say them, and the code adds the word page to the names of the numbers and of Previous and Next. On the first and last page, the arrow with nowhere to go is faded and is not a link.
- Round arrows around a page count Two round 40px arrows hold “2 / 4” in bold between them, so the page you are on is written, not drawn. Nothing on screen spells out the arrows, so write their names, Previous page and Next page, in the design file.
Verify the fix
5 checks, no mouse.
Test with: Screen reader, Keyboard, Speech input
Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after the shared pagination component changes.
Limits
This is a starting pattern, not a guarantee for every list. Screen readers word aria-current and landmarks differently, and how they read symbols depends on the punctuation setting; test with the ones your visitors use. Lists that load more on scroll or with a Load more button need their own care, because they have no pages to mark. The breadcrumb trail at the top of the same page has a failure of its own; see breadcrumbs that mark the current page only by its look. The site's main menu marks its current page with the same attribute; see navigation that marks the current page only by its look.
This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.
Keep this fixed as your code changes.
Turn guides like this into rules your coding agents and CI follow, then have the result retested with a keyboard and screen readers.