The last few weeks on the open source project have been, well, let's just say trying at best, but I've finally released a pager.
It's been on the roadmap since January, I just had other things to build first. The original plan was to dust off one of the pagers I've built over the years, tweak it for Blazor Ramp, test it with the usual browsers and screen readers and ship it. However, between then and now, I've had debates with some accessibility folks, which made me rethink my whole approach.
This post is partly a release announcement and partly a look at the process you go through when building an accessible component yourself, rather than just asking a friendly AI to build what it thinks might be accessible. If this is not your cup of tea then best you go read a different post, I did warn you.
I suspect you have used and/or built many pagers over the years, but have you really thought about them before, why we build them like we do?
The ones I used to build are the typical ones, you know, previous and next at each end, numbered selectors in between, an ellipsis for the gaps in the page sequence when you have lots of pages, with the current page always being in the middle of the pager. These buttons or links are usually list items in an unordered list inside a nav element, I am sure you know the type I mean.
A screen reader user, not only has the Tab key but has other means to get around a page, lists of headings, landmarks and interactive elements that they can jump directly to. However, a keyboard-only user doesn't have any of that; they've just got a Tab key, so unless you implement custom keyboard support to get to the other side of a pager, they have to go through it.
This then raises the first important question, for a pager, what keys should move you around it? Just the Tab or the arrow/home and end keys?
I have seen pagers that use a roving-tabindex approach - tab to the pager, then use the arrow, home and end keys from there. And with the roving index approach you tab to get to the pager and tab to leave it for the next focusable element on the page. This approach makes it easy for a keyboard-only user to skip the entire pager if need be, tab on tab off (Mr Miyagi style).
This is fine for keyboard-only users, provided you tell them what keys to use (as most users are not psychic) but it's a different story for screen reader users. Screen readers take over the arrow keys, as these are used for reading a page, so for these users, they would need to switch from browse to forms/focus mode (in the case of Windows-based screen readers) or use shortcut keys to pass the arrow keys through to the pager. What's good for one group of users may not be good for another and you should think about all of this during the design process. If you do go the custom keyboard route, you'd better put instructions on the page so users know what keys to use, otherwise you're setting yourself up to fail any WCAG audit that may be conducted, irrespective of annoying your screen reader users.
Now, what about that unordered list, why are the links or buttons even in a list, what does that list give you, ever thought about that?
For a screen reader user, a list gives you a count of items. The question I started to ask myself after using screen readers is whether or not that count is actually useful on a pager and is it even honest. A pager that has six buttons for six pages, fine, it lines up but most pagers I use sit under data tables with tens or hundreds of pages — so what does hearing "list, 6 items" actually tell you, beyond there being 6 items in a list? It tells you nothing about how many pages there really are, and nothing about what page it will take you to, until you land on it. Some will say, semantically it's a grouping of related items so a list makes sense, I would question that for just a simple pager as the nav element itself is a container for navigation items.
This is the bit that gets me: most of us do things because it's what we've always done, and is now what some people just tell AI to build for them, without ever asking why.
For most LOB apps, if you actually use a screen reader you may wonder what benefit it gives you if any, please do not trust me, go try for yourself and use your own judgement. My thought is that for a pager a list is just unnecessary noise.
What about those numbered items for pages, are they even necessary?
At this point you may be thinking this guy has lost the plot, it's a pager, obviously we need those numbers - Are you sure?
A book index gives you a page number because the content on that page is fixed. A pager under a data table isn't like that (I am assuming that's where the use of page numbers came from?) - its data is most likely coming from a database with rows constantly being added, updated, and deleted, so you generally don't know what's actually going to be on page X of Y. The only way I know of to reduce that uncertainty would be to stop any database changes or give people search and filtering mechanisms to reduce the number of pages, ideally to one so they have less to scan. And before you say "but what about jumping straight to page 100 of 200", that's not really a pagination problem, that's a filtering problem. No amount of numbered selectors fixes a data set that's too big to page through comfortably in the first place, and you still have no idea what is actually on page 100.
I am hoping at this point, I have made you think, and it does not matter if you agree or disagree with anything I have said, the point is you are thinking and this is exactly what you need to do when creating accessible components.
So far, I haven't even touched on the technical side of things, especially given we are working with Blazor and the way it works with the DOM and its diffing mechanisms. I mention this because lately I have seen way too many people post on Reddit, AI-generated libraries/NuGet packages all claiming their work is accessible, not because they know anything about accessibility, but because they have checked it against axe.core and it passed (any ideas why it did not pass my simple manual checks?). I cannot stress this enough, tools like axe.core are great and do help but they only handle approximately 40% of what you need to know for WCAG if that's important to you, for me it's about usability/accessibility-first and then making sure I tick the WCAG SC boxes.
At the start of this post I mentioned the last few weeks have been trying at best. I am not going to go into detail as sometimes the only way to learn and have it sink in is to discover it for yourselves. Screen readers have to access the DOM. They do cache and monitor various things, so when working with Server with and its DOM handling and diffing mechanism, things can go awry. Axe.core, Playwright or even your friendly AI are not going to help you find these types of issues that can render your component unusable, the only way to discover these is with old fashioned manual testing with a screen reader, you know the same thing that some of your audience might have to use, not by choice but out of necessity.
After all this, what's my pager like?
It's a nav element that contains four or just two visually labelled selectors (links or buttons) with the page information above them, sighted users can see the information, screen reader users have it announced. On entering the pager, which has the aria-labelledby attribute on the nav element, screen reader users will hear the pager's name and its current state. When you activate a selector, that same on-screen text gets announced via the Blazor Ramp Live Region Service so all users get exactly the same information. For a typical pager with all four selectors, you would see First, Previous, Next and Last selectors, but for a data set with only a few pages, you may ditch First and Last, so it's just Previous and Next. Just tab stops, no custom keyboard support and/or user manual required.
I am sure this will not be to everyone's taste, which is fine, there are more than one way to skin a cat, but I am happy enough with it to have included it in my library.
As much as I may waffle on about stuff, the proof of the pudding is always in the tasting, so irrespective of your views, next time you need a pager component or are looking for ideas when building your own, fire up a screen reader and go play around with my examples.
You can see/hear the pagers in isolation on both the test and doc sites, with a more realistic example shown on the Pagers usage page on the doc site.
GitHub repo: https://github.com/BlazorRamp/Components
Test site: https://blazorramp.uk (pager in isolation examples)
Doc site: https://docs.blazorramp.uk (pager on a filtered data table example)
Regards
Paul