5 things I've learned trying to build a fully accessible website
- accessibility
- web
- aria
- development
- design
- wcag
- html
- css

You wouldn't believe how many publicly available websites are composed entirely of div elements. No semantic HTML, no ARIA labels, just divs stacked on divs. It's not just an inconvenience for users with disabilities; it's also terrible for SEO and makes your site harder to maintain.
I set out to build my portfolio website with accessibility in mind, and along the way I learned some things I want to share. Here are some practical lessons I learned that you can apply to your own projects.
Use the presentation aria role for stylistic div elements
HTML semantics are important. They tell your browser what the purpose of your element is. Better semantics means that screen-readers, search engines, and other tools have an easier time navigating your application.
Ever since I've become more aware of the importance of HTML semantics, I've noticed I have become somewhat scared of using div elements. See, sometimes I just need a div element purely for making my website look good, but I don't want to mess up my accessibility tree.
Luckily, there are ARIA roles to solve this problem. The 'presentation' role and 'none' role can both be used to let the world know that your div exists only for the sake of looking good. There's no functional difference between these two roles, but I like to use the 'presentation' role for stylistic purposes, and the 'none' role for when you need a div element for functional reasons.
Use display: contents when you want the right semantics, but don't want to mess up your styling
When I was working on a list of articles for my website, I encountered a problem. I had a <ul> element, which was a CSS grid. Inside the list, I had <li> elements which were using CSS subgrid. But because, each item on the list needed to be a link to an article, I had to use the <a> element inside the list item. Why was this a problem? Because the CSS subgrid only works on its direct children.
I couldn't just remove the <li> element, because then the semantics wouldn't be right. It was the opposite of the problem I had with my stylistic div; This time the semantic markup got in the way of my styling.
Luckily, there's a CSS property to solve this problem. I had seen 'display: contents' before, but its purpose wasn't very clear until now. What it does, is that it basically pretends that an element doesn't exist, and makes the element pass its styling down to its children. This means my HTML structure was still semantically sound, but now CSS subgrid worked as intended.
Create bypass blocks for better keyboard accessibility
Keyboard navigation is one of the key necessities of good website accessibility. With only the 'tab' and 'enter' keys you should be able to navigate most, if not all of your web content. Unfortunately repeatedly tapping the 'tab' button can become kind of time consuming. Especially when you have a navigation header with lots of items that you have to go through on every page.
Some very clever people came up with a solution for this. What if you could press an invisible button that would skip the navigation bar and bring you straight to the actual important content of the page? The Web Content Accessibility Guidelines (or WCAG for short) call this a bypass block, and it's a clever nice-to-have to make your webpages more accessible for users who can't rely on mouse navigation.
So how do we build this bypass block? You simply place an anchor element at the start of your page. By default, anchors have a tab index, so you can already tab to it. Then simply point your <a> element to an element of your choice. Make sure to give the targeted element a tab index of -1, so your browser scrolls to it and changes the tab index accordingly.
Okay, but now you have a button on your page that you don't like the look of. What do? Just turn it invisible, of course. Use 'visibility: hidden' in CSS to make the element not visible, and then only make it visible when the element is being focused. Just make sure you don't use 'display: none' as that will remove the element from the accessibility tree.
If you're using Tailwind.css, there's a cool utility class you can use called 'sr-only'. It visually removes an element entirely, while still readable by screen readers. Then you can use 'focus:not-sr-only' to show it again when you focus on it using tab.
Native multimedia elements are not accessible
I wanted my website to show all sorts of media. Not just pictures, but also audio clips and videos. Multimedia is tricky, because how would you show a video to a blind person, or an audio clip to a deaf person. I didn't really know much about accessible multimedia, so I thought I'd be wise to stick to the basics; add alt properties, add subtitle tracks to videos and use the native controls for the HTML5 video and audio elements.
As it turns out, the native HTML5 video and audio elements aren't accessible at all. As mentioned before, keyboard navigation is very important for accessibility, and these players have none of it. So I guess I had no choice but to make my own. If you remove the 'controls' attribute on a media element, you'll end up with a piece of media that can only be controlled by JavaScript. Then take a reference of the media element, and control the values with event handlers. Now here comes the important part; make sure that your new controls are not just controllable by keyboard, but also that screen-readers understand what they are focusing on. For example, if you're using a range input as a video timeline, use ARIA attributes like 'aria-valuemax', 'aria-valuenow' and 'aria-valuetext' to let the screen-reader know about what is going on.
I've found that it can be quite some work to create an accessible video or audio player, so consider using a library like Plyr or PlayerJs to do this for you.
Accessibility developer tools aren't perfect
There are a lot of guidelines for making websites accessible. So much that it's probably not a good idea to put them all in your head. You're better off using developer tools, such as the accessibility tree in FireFox, Google Lighthouse, or accessibility rules in your linter, to guide you through it.
For the development of this website I mostly relied on Firefox. While it was generally very helpful, I found that it would sometimes throw unsolvable accessibility complaints at me. For example, it considered the slider I use for controlling my video player not keyboard accessible, while range input elements are fully keyboard controllable by default.
No matter what I did, I couldn't get the errors to go away, and with each change I seemed to create more accessibility warnings. After some research it turned out that this has been a longstanding bug in Firefox, and that there was literally no way to get rid of these issues. There went my perfect accessibility score. I guessed my website was now an inaccessible nightmare?
It's then when I learned that accessibility tools are very eager to throw warnings at you, but sometimes it can get in the way of you making an awesome website.
My advice? Use multiple accessibility tools and don't forget to use common sense. If one tool is giving you a hard time, just check if the issue occurs in another tool. If your tools are still yelling at you, ask yourself if it is worth your time. Nothing is ever fully accessible, so make your own choices on what you consider to be accessible enough. Want to really find out if your website is accessible enough? Use a screen-reader and find out yourself. Or ask someone you know with a disability to look at your website, and see if they can use it.