Let’s Make Our Single Page Application Accessible


This talk is going to provide recommendations to make a single page application accessible. The talk is not going to cover the basic of accessibility, like adding ALT text or using correct HTML elements, but it is going to cover major shortfalls existing in our SPA.

In no particular order, this talk is going to cover the following topics:

  • - Provide a list of resources (blog, book and people to follow)
  • - Introduce the issue with SPA with a live example
  • - Describe and solve issues with Routing issue in SPA
  • - Describe and solve issues with dynamic changes in a Vue app
  • - A quick intro to correct HTML element (and how to leverage Vue for this)
  • - Describe and fix Browser history


Hello everyone and welcome. It is a great pleasure to talk about vue.js. And today's topic is going to be, let's make our single page application accessible. But before we start, let me introduce myself. My name is Simone Cuomo and I'm a software architect at Distal Labs. And you can reach me on Twitter at Zelig880 or read any of my articles on my website at zelig880.com. I also wanted to let you know that I'm going to be very active on Discord and Twitter. So feel free to reach out if you got any questions or you would like to have a chat regarding any topics vue.js related. Today's agenda is going to be extremely busy. We have 20 minutes, but we have 20 minutes full of content. This talk is not going to be the very basic accessibility talk where we talk about all tags and headings and so on and so forth. But it's going to focus on single page application issues. I also want to let you know that I'm going to share the slides and I'm going to share the repository and everything that we're discussing during this talk later. So there's no reason for you to actually get notes. Where should we start? Well, first and foremost, what should we care about accessibility? That's the first question to answer. Well, the internet is not optional. What do I mean by this? A few years ago, accessibility talks were all about giving disabled people the chance to access the internet just for accessing the free internet. Truth today is that they have to access the internet. Banks are closing down, forcing users to use the app. Shopping is online. Even the tax return is happening online. So if we don't make our application accessible, how are these people going to do the day-to-day operation? How are they going to book flights? How are they going to do that? So it is our duty to actually make the web more accessible. There's a few stats down there that are big stats, 11 million people are disabled, disabled users accessing the internet in 2022. That's 20% of the UK population. It's quite a bit. And the last point is accessibility is not only for people with disability. What does that mean? People have the perception that we write accessibility code just for people that are disabled, but that may not be the case. A dad that is holding a child sleeping on his hand, it happened to me when my child was younger, he may not be able to use the mouse and may have to navigate the site with the keyboard. The same if you have a cast. Then a person with dyslexia who may struggle to understand long text. And a non-native speaker that is actually going to switch on the screen reader to be able to read the caption of it. So there's a lot of these things that are not really accessibility for the sake of disabled people, but accessibility to allow everybody in every circumstances to actually access the internet. The last two examples there, very noisy environment, somebody has to switch on caption, or a very sunny day where there's so much glare on your screen that you cannot actually see colors. They may require an accessible website to actually see it all. So what's the problem with single page application? We know that the web was already broken. What I mean by broken is that we are not really doing a good job at writing code that is accessible. We got incorrect elements, incorrect structure, and missing properties are really making the web unaccessible to start with. But the truth is that even if this is the way it is, so as you can see, this is a report from the web 8 million 2022 report that shows they were doing a really bad job at this. No contracts, 83%, missing all text, 55% of every single whole page they checked. So as you can see, there's some improvement there, but it's very minimal. But fortunately, that's out of scope for today, and we're really going to focus on single page application. We're going to cover three main aspects. The first one is dynamic content. The second one is page refreshing. And third one is what they call custom component bonanza. It's just a name I created, so I don't think you know what it is. Each of these points, we're going to explain what it is. We're going to have a live coding, and we're going to have a solution. So you should be able then to go out after this talk with three solutions that will bring in your day-to-day activities, day-to-day development. So let's start. Let's go. Before we move on and cover this, I want to show you the application. We're going to use this very simple application where a simple application, three links with the three things we're going to cover. There is a heading, there's a navigation bar, and also we're going to use voiceover with the captions so you can see as well what the screen reader will actually say. So let's see our first example. Dynamic content loading. We like to fill our website with visual clues. A loading spinner, a loading skeleton, or a add to cart flying around or an email flying around when you click the send button. All these are great. They look fantastic, but not really accessible. A user using the screen reader will not know of the situation happening. So what you're going to see in our example is that the content is loading, there's an api call or something with the progress bar. A screen reader user will have no idea of anything that is happening on the screen right now, and that's what we're going to fix. How are we going to fix it? We're actually going to use something called live region. Live region is not invented by a framework. It's actually something that is available in html. Live region is a part of the page that is going to be read out on a screen reader every time its content changes. Luckily for us, we don't have to actually implement all different live region arounds, but there is a plugin that is called View Announcer that is actually going to do the heavy work for us. So after we set it up, you will see that it's going to be very easy to actually use the live region whenever we need. So let's go over in the hub and let's start to set it up together. This is our application. It's a very simple one. As I said, we have the three components here. We have the welcome page and we have a default layout. To add the live region, the first step is to actually add the package. The package is called announcer. And because we're using the view three, we're going to do the announcer at next for the view three version. So after adding the plugin, I'm going on the screen so I can get things and actually be faster for the talk. After we add the plugin, the package, we're going to go on a main.js file. We're going to put it on the top. And next, we're actually going to import the style. So the VNN to make sure that the component is not actually displayed. So there's some styles necessary as well for it to be done. And lastly, on this file, we're going to go down below and we're going to make sure that we actually add the plugin to our application. Step one, done. The next thing we have to do before we can actually use it is we need to add this component. So you could add this component on every single file that you want to use it, but I do suggest to actually add it in the layout. So as you can see, I have my main layout with the navbar in the main. So I'm going to add the view announcer on the very top of this page. Lastly, we're going to actually use the view announcer. We're going to do it on our dynamic data. So if you open up the dynamic data file, you can see that on the very top, we have a loading that is referenced to zero. We have a fake loader. So this increased to 25 every second. And then at the very end, we're going to set the names. It's actually going to set the names. Before I actually change things here, we're going to go into application and see what the screen reader actually reads out to us. So we go back here. I'm going to switch my screen reader on. So you should be able to actually see the caption over here. So if we go on dynamic data and we click the load data, as you can see, the screen reader is saying absolutely nothing. The data is loaded and nothing has still been said. So user will have no idea what is actually happening behind the scene. So we're going to go back now. We're going to switch off the voiceover and we're going to go back to our code. And we're going to import an answer on this file. So import an answer like every other package, like this. It does expose a composable that is the user answer. And the user answer has two different methods. One is called polite, that it will wait for the previous message before it sees the next message. And the next one is going to be assertive, that it's actually going to shout it out. So you can see that polite queues are assertive shouts. That's the way I usually explain it. And then we're actually going to use it very simply. You can use it whenever you want on the page to actually notify the user. So I'm actually going to say here, every time that you change the loading to inform the user, the same way we do visually, let's do it also and add it to two seconds so we can see more. If we do something for the user visually, let's do it for people that are on screen readers. And the next thing I'm going to go is a little bit down below where we set the names. We're also going to inform the user that the name has been loaded. So the user knows, the user can go through and understand that something changed. So if I'm going to save here now, and we're going to go back to our application, and I'm going to refresh the page. Screen reader is on, sorry, voiceover is on. And I press this button, let's see what happens. So as you can see, the loading progress now is being read out. So the user knows that something's happening, knows that it's actually happening, nothing stable. And when the names are actually loaded, it will actually notify the users as well. So what we've done today, we've implemented something that, as you can see, is very simple to implement. And it's very simple to then, after you add it once to the application, it's very simple to go through and export and continue to use for the user. So very simple way to support all users that use the voiceover and to inform them of visual event. And what I usually remind people is, if you're doing something visually, add this line in. That's all it is, not too complicated. Let's go back to our slides for number two. The next topic is going to be the problem with routing. So single page application do not fully refresh. That's something that is actually great in some aspect, is the main reason why single page application were created, is to be able to change some part of the page. But the main problem is this is not really accessible. If you are on a screen reader, if you're using your keyboard to navigate, what will happen is that you are actually left in a... If you navigate around one, you don't know that the page has changed. And number two, you don't actually know where you are, because your focus stays in the old buttons or wherever you were previously. The solution for this is a mixture. So we're going to use different techniques that will help us to make the navigation accessible. First, we're going to enhance our announcer. So we're going to use the same plugin we added before to actually inform the user every time we change the page. Secondly, we are going to add something called the skip link. Skip link is like a hidden link on the very top. So if you ever press the tab button on some website like GitHub or any other big website, you will notice that there's a skip link showing up. So it's a link that allows the user to jump all the user's content, go straight in the important part of the page. And lastly, we're going to make sure that every time we change page, the focus goes back on the very top on that skip link. So we really help the user navigate. So again, slides to explain, and it's time for coding. So we go back in our application. And again, I'm going to move things around here. So number first thing first is we need to add the plugin. So the plugin is called is always offered from the view dash a11y. That is image accessibility. So if you go on that GitHub repo, you will see all this package offer. But this one is the skip tool. And as before, we need to use the next version. Next one up, as before, we need to go on our main.js file and actually port the plugin. So on the very top, we can import the plugin here. And just like before, we are also going to import the site. So you can start it yourself. But this is a very genetic style that is actually used as almost everywhere. I've actually seen the view, the skip to actually being used. Lastly, by now this we're going to add the skip link here. And very similar to before. So as you can see this consistency here, we're going to go on. We're going to save this page. We're going to go on default layout. And we're going to add our skip link on the very top. So the skip link as a reference, and you accept the tool. So you choose where it's going. So as you can see, my case, I'm saying that it has to go to an ideal main. And you can see I got my main that is actually the main content. You can change these and it is also going to be per page. And the view skip link also allows more than one entry. And then this is what is actually going to be read out to the user. So if I refresh the page now, if I go back to our application, and if I actually start the voiceover, you can see there's a skip to main content. If I press enter, I go straight to the main content. So the skip link is working, but we need to do a bit more. So let's go back to our application. So what we need to do, as I mentioned, this number one was to skip link, but we need to actually enhance our announcer. So if we go back to our main.js file, we're able to actually pass the router to our announcer. So we can do this. What this is actually going to do is that your announcer plugin knows how to read the router. And every time things change, it's going to read out to the user. So he is able to listen to that. So what you can also do, you can choose what is actually being announced out to the user. So at the moment, it's just going to say page changed, but what you can actually, you can choose what actually gets said. So if we go to the router.js file, you're able to actually define a message. So in this case, let's go on the welcome page, on the page navigation, and I'm going to say announcer. So you can pass an announcer and you can pass a message. So if I go to the page navigation page, it's actually going to say my navigation page. What is also useful for this plugin, what I usually do is I create a little, I create a little, apologies, I was taking the code. I create a little piece of code that actually is going to take all the, that is going to go on the router and before it, it's going to take the title and change the title. So what the announcer will actually do, the announcer will automatically read by passing the router, the announcer will automatically read the titles. So what I'm doing by doing this will automatically change the title and the announcer knows what actually is supposed to read. So again, this code, you can find it on GitHub. I know we're going quite fast, but as I push it over, you can see and actually use the code yourself. So if I save here and save here and I go back to our application, open the screen reader. Okay. So we're in our main app now. So I'll skip to the main content, page navigation. Let's say go to page navigation, I press on it. You can see that it goes, the focus goes straight, straight to the focus, go straight on the top and then the page was read out. So if I press skip again and I go back, if I go on a different page, for example, I go to dynamic load data, you can see the say skip to main content because that's where the focus is, dynamic data is loaded. So what we've done now, we've implemented the three things. As you notice, we're not actually forcing the focus to go back because all that has been handled by the plugin for us. That's great. So solution number two, we now have accessible page loading and everything is actually been done. As I said, the code, we probably went very fast on the code to change the title. So you can see the very top here, the title changes every time you go on a different page. And that was from the little piece of code. That's very simple. There's even plugins that can do that for you. So if you need any help, just reach out to me. The last part that we're going to cover is called component bonanza. Actually, I call it component bonanza. Okay. So it doesn't really have any existing meaning. Call them in by component bonanza. Component-based architecture offered by frameworks such as vue, react, and angular pushed us away from plain html. So it was so simple to create very small component that we went a little bit away from the native way. Of course, we had the IntelliX Explorer to support and component didn't look that pretty. Unfortunately, pretty element don't really match to be accessible element. For example, scroll bar, date pick, drop down, input field are just a few of the components that are usually implemented and to make things look better, but reduce their feature for user. So I'm going to add you something, but before we do that, I'm going to cover three different parts of this. Number one, when creating a new component that emulate html, always try to use existing component libraries or packages. I know that usually people, you know, that there's always the urge of not wanting to use packages for no reason, but some of these packages are, you know, there's a lot of work goes behind this in touch to make them accessible. You will be surprised of how many features there are behind a package. How many features there are behind the date picker. How many features and things are required to just behind the drop down. It's important though that you actually make sure that the package that you're using is accessible. I've seen cases where there's packages with thousands and thousands of download that are actually not accessible. So please do your own work. The next thing is to check the basic. So what I usually do personally, I go to the W3 sites of the Mozilla site because they always have snippets that you can download, html snippets and pages information to tell you what is actually the component expected to do. So if you really want to redo it yourself, start from a good base. And lastly, why don't you add accessibility on your workflow? So it's really good if you add accessibility as part of your development experience. Over time you will learn how things will be accessible and you will actually make it better. You will make your application will become more accessible because it's become kind of a muscle memory for you. And that's actually what you're going to do in this part of the code. Let's jump over that. In this case, we're going to add something called vue-Axe, I think. So it's a plugin that will help you, will actually notify you as you use your application development environment, will notify you, will check your application for anything that is, it doesn't pass the axe core plugin, the axe core library. So as with before, we're going to always add our package first. So the package that we're going to add is two. Number one is going to be called axe core. And the second one is going to be vue-Axe next. Just so you know, we added it just in development environment because we're not going to push this actual plugin on production. So we add the plugin. And then as you may expect, we're then going to add the plugin on our main.js file. On the very top, we import it. As you can see, I'm importing two things. I'm importing the vue-Axe and I'm also importing the vue-Axe popup because we're going to need both of them. And then next one up, we're going to go a little bit down below. Now this is going to be a little bit trickier than what we usually did. So the vue-Axe is actually a vue instance on its own. So what you're going to see is that we actually changed our normal create app to say that if it's in a development environment, please create an application and a vue-Axe popup. And if it's not, just create a normal app. So this is the only piece of code that you probably do it once. And after you do it, it's going to work for all dev environment and all production environment. So if you don't want to add something in your application itself, you can actually use a plugin. The only reason why I, sorry, a Chrome plugin. So there's a Chrome extension that actually does all this. I do prefer to add it in the application because by doing so, everyone is actually going to use it. So I'm going to save this here. And last but not least, we have to go in our config, vit config. And we're actually going to add the Axe code as optimized. So we're going to actually go back on our code now. We're going to refresh the page. What you can see at the bottom here that we got a lovely popup. It checks everything every time you go through. And what you notice is that this plugin, these links, add a 409 contrast where it's expected to be 405. It tells you everything about the color. It gives you, it tells you where it's coming from and it's very, very easy to fix that. So I found this extremely useful. Also, if you click here, it actually highlights them. So it's easy to find them on a page. This will save you tons and tons of time on actually, you know, of time for you to debug. Very useful. As I say, it will just show in development environment. And if you want, there is a Chrome plugin as well. I know that it was a lot of content to go over in the 20 minutes, and I hope that you managed to fetch everything that I mentioned. If you do need any follow up or question, please reach out. As I mentioned, I'm more than happy to answer all your questions. I'm going to be in Discord right now, but as I mentioned, I'm also going to be in Twitter later on. This is all the reference page. And I want to thank you for joining my talk and I hope to see you around.
25 min
15 May, 2023

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

Workshops on related topic