A row of numbers under a list of results is obvious to look at. Read aloud it is one, two, three, four, next, with nothing saying what any of it does or where in the set you currently are.
Worth saying at the start that there is no published W3C pattern for pagination. The Authoring Practices Guide covers thirty component patterns and this is not one of them, which is why every component library does it differently and why the advice below is assembled from four criteria rather than lifted from one page.
Name the Region and the Links
- Wrap it in a
navelement with an accessible name, usually Pagination, so it can be found and skipped. Where the same pagination appears above and below a long list, which is the common layout, do not invent two names. W3C's own worked example of that situation is a search results table with identical controls top and bottom, and its advice is to give both the same label, because the extra words distinguishing them are more distracting than helpful. - Mark it up as a real list. The structure is what lets a screen reader announce how many pages there are and step between them, so somebody hears twelve items rather than tabbing to find out. It also earns the naming shortcut below.
- Give each link a name that stands alone. Visible 3, accessible name Page 3, through visually hidden text. That is worth doing and it is worth knowing you are meeting the Level AAA standard when you do. At Level AA a link's purpose may be determined from its text plus its programmatically determined context, and a list item counts as that context, so a bare 3 inside a properly marked-up pagination list already passes at AA. Standing entirely alone is the AAA version.
- Previous and next have to say what of. Next page rather than a bare chevron. And name the right criterion when you write it up. An icon-only link with no accessible name at all fails 4.1.2 Name, Role, Value, at Level A, before link purpose is even reached. Link purpose is the one for a control that has a name and the name is uninformative.
- Mark the current page with
aria-current="page". This is the specification's own worked example of that attribute, so it is exactly what it was defined for. Only one element in the set gets marked. Bold carries nothing on its own.
Disabled or absent
On page one, Previous should either not be there or be marked unavailable. A link that looks live and goes nowhere is worse than one that says it cannot be used. Prefer aria-disabled="true" over the HTML disabled attribute here, which is what W3C's own guidance recommends for navigation-style controls, because it keeps the control in the tab sequence where a screen reader user can still find it and hear that it is unavailable.
Where Focus Goes
If pressing a page number triggers a full page load, the browser handles it. Focus starts at the top, and the new page title does the work, which is why 2.4.2 Page Titled matters more here than people expect.
If the results update in place, you have to do something deliberate, and the two things to choose between are alternatives rather than a pair. Move focus to the results heading, so the reader lands where the new content is. Or leave focus on the control and announce the change through a live region. Do the first and the update is a change of context, which takes it out of the status message rule entirely, so the live region has nothing left to do and adding one announces everything twice. Do the second and focus stays put, which some people prefer, and then the announcement is the only thing telling them anything happened.
Targets and Spacing
Page numbers are among the smallest controls on any site, and they sit shoulder to shoulder, which is precisely the case 2.5.8 Target Size was written for. It is Level AA and new in WCAG 2.2, so a site last audited against 2.1 has never been checked against it.
The number is 24 by 24 CSS pixels, and the exception people reach for is not what they think it is. Spacing does not get added to the size. It is a separate test with its own geometry. Draw a circle 24 pixels across centered on each undersized target, and if none of those circles intersects another target or another undersized target's circle, you pass. Two 20-pixel numbers therefore need 24 pixels between their centers, not four pixels of margin bolted onto twenty.
Two other exceptions are worth knowing before anybody redesigns anything. A target whose size the browser decides and you have not modified is excepted. And a function reachable through a different control on the same page that does meet the size is excepted too, which is an argument available to a design where Previous and Next are comfortably large and only the numbers are tight.
On a phone, fewer and larger numbers beats a complete range squeezed edge to edge. Nobody has ever needed page seven of twelve badly enough to justify a four-pixel gap.
Say Where They Are
Page 3 of 12, in text near the control. It costs one line and it is the thing that turns a row of numbers into a position. It also gives you a natural place to announce the change when results update in place.
Keep the page in the browser URL so a position can be shared and returned to. That is not a WCAG requirement in itself, and it connects to one. 2.4.5 Multiple Ways at Level AA asks for more than one route to a page, and an address that identifies where you are is part of how anybody gets back. It is also the clearest advantage pagination has over infinite scroll.
One thing not to search for
There is a WCAG technique with pagination in its description and it is not about this. It covers a page-selection mechanism for digital publications built from a fixed-layout source, so an e-book that carries the page breaks of its printed edition. It uses roles from the publishing ARIA module and it is filed as advisory rather than sufficient. Anybody searching the techniques for pagination lands on it. It belongs to e-books, not to your results list.