Recently, one of our clients asked us to build a filter UI for a list of things. I thought it might be fun to write up how I approach a brief like that. This will not be a technical tutorial, but there is a functional demo that you can inspect with dev tools.
What am I doing here?#
The first question I ask is, "What problem is this supposed to solve?" Sometimes the client's idea of a solution is a partial fit, but a different approach might be even better for their users. In this case, the brief is to add a filter to an existing list of things. The client wants to give customers the option to narrow down the amount of things and make the list easier to scan, which is pretty straightforward.
Do I go for a server-side or client-side solution?#
Here's the kind of checklist that I go through before making a decision:
Are there a lot of these things? Is the list going to be paginated?
Is the list of things the main content of the page, or is it more of an aside that could be considered "bonus content"?
If it's the main content I give a point to PHP for list rendering. Having everything in the HTML is a robust baseline.
What's the initial state? Show everything and allow users to filter it down, or show nothing and reveal matches upon user interaction?
If the default is to see all things, I have a strong preference for having all things in the server-rendered HTML payload. This answer would be a point for list rendering in PHP.
Do filtered results need to be persisted when users navigate away from the list page and come back via "back" button? Should filtered results be shareable via URLs?
Would the user experience be improved by an instant filter response instead of a whole server roundtrip?
Holding this list against my project, I see that there are 12-20 things at most, so pagination is not necessary. The list is the main content and the default state is to show all things. The client does not care about shareable URLs.
After looking at these answers, I decide to implement the filter function as a client-side enhancement for a server-rendered list. The main purpose is to let people access the things, and at a maximum of 20 things the filter is nice-to-have, not a necessity.
There are questions left to ask at this point, but for our kind of projects they're rhetorical:
What about older browsers like IE11?
We support them as much as possible. I'm confident I can build this filter in a way that it works in old IE, but I'd be okay with hiding this nice-to-have enhancement from old browsers if supporting them were to prove unreasonable.
How accessible are we going to make this?
Again, as much as possible. I try to deliver accessible solutions to the best of my ability.
How am I going to build this, then?#
Alpine.js has great documentation and works a lot like Vue.js, which has even more documentation, so I won't go into implementation details here. You can inspect the demo if you're curious.
Back to the plan. The filter will be a
no-js from the
A slight disadvantage of the client-side filter is that we have to notify the user about list updates ourselves. With a conventional form submit you get a full page load, which is a heavy-handed kind of notification to be sure, but it's also hard to miss. The Alpine.js-powered filter will update the list instantly, but without extra work, there is no guarantee users of assistive technology (AT) will have a similar experience as sighted users who notice the list changing in their field of view. This is what ARIA live regions are for. There was a timely livestream about them last week: The Many Lives of a Notification by Sarah Higley. The most important takeaway: keep notifications concise. It would be a bad idea to make the list a live region, because then every filter update would prompt AT to read out all remaining things. I am going to add a visually hidden live region instead that will announce the number of matches after each update.
Functional demo: a filterable list of talks for a fictitious conference#
☝️ Here's the plan put into practice. Is it done? I think it's good enough for a first iteration. I went with CSS Grid for now, meaning IE11 and others will get stacked form fields and list items instead of horizontal rows, but I think that's okay. And it could be remedied with flexbox if need be. Another possible tweak: hide the filters on small screens and show a toggle button. Right now, the stack of three
<select> elements takes up a lot of screen estate on a mobile phone and as I established earlier, the filter is a nice-to-have add-on and not a baseline feature.
Now, please play around with the demo and tell me if I missed anything. I'd be happy to learn how to make it (even) more accessible.