The most common misconceptions about accessibility are that it is time-consuming or can be easily left as the last thing to be added. But, of course, this is not the case. We’ll look at how easy and quick adopting accessibility as a First-Class Citizen and, on the contrary, how difficult and time-consuming it is to add accessibility to an existing project.
How Time-Consuming Is It to Build an Accessible Mobile App?
AI Generated Video Summary
Building an accessible app can be time-consuming but can be divided into two parts: making an existing app accessible and starting an accessible app from scratch. React Native Armour can help with accessibility fixes, but manual checks are still necessary. Accessibility best practices include focusing elements in the correct order, announcing UI changes to screen reader users, and ensuring consistency in behavior and appearance. Building accessible components can streamline the process of making an app accessible.
1. Introduction to Accessibility and Fixing Issues
Is it really true that building an accessible app is time consuming and expensive? We are going to divide the agenda in two parts: making an existing app accessible and starting an accessible app from scratch. Accessibility is the practice of making your mobile app usable by as many people as possible. The accessibility issue to fix is an actual report we received, highlighting a serious issue. I used React Native Armour to help with the fixes, which includes components and hooks to enforce accessibility requirements. I replaced imports of pressable from react-native to react-native-ama, resulting in about 200 typescript errors. I had to navigate through the screens to fix problems and ensure clear accessibility labels. Headers were marked as headers for the screen reader. The form presented another problem.
Hi, is it really true that building an accessible app is time consuming and expensive? In order to answer that question we are going to divide the agenda in two parts. The first part we take an existing app and we try to make it accessible. The second part we start an accessible app from scratch.
Before starting, my name is Alessandro Lemsenese and I am a software engineer in Formidable. I started work with accessibility about four years ago. My first work with React on a TV involved creating an accessible app for a bank. I started learning more and more about accessibility and never stopped since then.
Before we carry on, let's remember what we are trying to do. Accessibility is the practice of making your mobile app usable by as many people as possible. So that's our target. We want to make sure that as many people as possible can use our app. So let's start.
Now, the accessibility issue to fix is actually an actual report we got from an excellent company, where they highlighted a serious and critical issue that we had to fix in our app. In my case, I did use React Native Armour to help me with these fixes. And the library is a React Native library I built after I joined Formidable. It does contain a set of components, hooks that are designed to enforce minimum accessibility requirements. So the first thing I did, I did install the library with Yard, I did import the provider provided by the library and wrapped around my application. So to fix rules, labels, states and hints I did replace all the imports of the pressable from react-native to react-native-ama. And once I've done that I got around 200 typescript errors or missing rules and labels. And in this screenshot you can see how Ama works. Basically Ama does some runtime check, but apart from the typescript typing does a runtime check of the common accessibility issue and if it does, it does highlight the component inside the UI and shows a banner and also prints in the console which of the accessibility tests has failed.
Now in my case I had about 200 errors and the big problem I had I could not assume that everything was a button. And in fear from the code which I wrote to use was not always straightforward so the option I had was to actually navigate through the screen to fix the problem. The same was with accessibility label. I had to make sure that the accessibility label was clear for the screen reader user and also I had to check if it was enough because we had some cases like the one in the screenshot where the button did contain x information, in this case 5 messages, unread messages. So I had to communicate this information to the screen reader user using accessible hint. Also because I haven't worked on all the parts of the app I wasn't sure how many screens we actually had so the only option I had was to actually go through the entire app by myself and figure it out, all the information that was missing. For headers, it was easier. Just make sure that each screen had a header and everything that looked like a header was actually read, marked as a header for the screen reader. The form, there was another problem we had.
2. Fixing Form and Bottom Sheet Accessibility Issues
The user couldn't focus the form label and input as separate fields. The return key on the keyboard didn't function correctly. The form submission didn't focus on the first field with an error. We used React Native ARMA to fix these issues. We also addressed the accessibility problem with the bottom sheet by using a built-in component from ARMA. The entire process, including bug fixes, took over 5 weeks. To build an accessible app, we can create shared components that handle accessibility properties. However, manual checks are still necessary to ensure compatibility with screen readers.
The bigger problem was the user wasn't able to focus the form label and the form input as two different fields. The required option was not read and the message was not read as part of the field. Another problem was that by default React Native does show a keyboard with the return key, but that return key doesn't do anything. The right behavior was to allow the user to navigate through the fields using the next button if needed and allow to submit the form with return 1.
The last problem was that if the form gets submitted and there is an error, the first field that does contain an error should gain the focus, this was not happening for us. So to fix this I did use React native ammo, especially the form field is a wrapper that handles the form submit. So if the in this case handle submit fails, the form that contains a reference to all the inner field will try to focus the first field that we marked as that contains an error. And also yeah the form itself is used internally by the text field and the use text field hook to figure out which keyboard button to show.
For the text field we had to share the components so what I had to do was wrap. We had to use the text input use text input hook where the ref is used to allow focusing the field internally and let's see if the field had no validation if had no errors or the various attributes and pass that properties back to the text input. Also we have a form field like check box or combo in this case I had to wrap in the generic form that field provided by Ama and this field is able to actually get receive a screen focus from the screen reader. For the bottom sheet this show we had the one we were using, did not get any focus the user was still able to select anything underneath the overlay the bottom sheet itself. So we had to replace that and in this case I used one built in Ama that did address the accessibility issue. Also, this one has the benefit of disabling the animation, the sliding and slide-out animation if the user sets the disabled animation in the device. The time of the action, in this case if you add an attempt to the card, we show a message that for a few seconds and the report that it was supposed to behave more or less like a bottom sheet, it was supposed to gain the focus, allow the user enough time to actually interact with it and the user was not supposed to select anything, be able to focus anything underneath. In this case we did use the bottom sheet provided by React Native ARMA, so we add all the focus feature and the focus feature, and we used in conjunction with the useTimeout action. This hook basically allows trigger the function we give according to the status of the screen reader and the device, for example in iOS if the screen reader is on the timeout is never triggered, while on Android we use the time to take action settings from the device.
So that was the critical and the serious issue we fixed, so how long did it take? To this I did also add bug fixes, because during the process we had to replace some libraries with others that were accessible. So we introduced some bugs, some of them we were not aware, because for example the bottom sheet does use model, but React Native at the moment does not support opening multiple models unless they are nested and so we had to change also a bit of logic around that to make it work in the app. And the whole process took more than 5 weeks to fix. So let's build an accessible app as first class citizen. How do we do that? How can we make sure our app is accessible by day 0? The solution is easy, we can build shared components that are accessible by default. So wherever you have buttons with different states, with different hints, we just create one or multiple shared components that handle all the required properties we need. The same we can do for a header, the same we can do for forms, we just make sure the fields are accessible already. Bottom sheet, overlay if needed, and carousel, in our case we added carousel, we had to make it accessible. So whatever component we actually need in our app we can create once and make it accessible. So what we're going to use is already accessible by default. We need a bit, just a bit extra time to make sure that everything works. Because accessible out-of-box doesn't mean that your app is going to be fully accessible, there are some checks that are needed to be done manually that we cannot automate. For example, we still need to make sure that whatever feature we build can be done with the screen reader.
3. Accessibility Best Practices and Considerations
The focus is important and elements should be focused in the correct order. UI changes should be announced to screen reader users. Consistency in behavior and appearance is crucial. Group content should be checked to ensure it is accessible. Multiple text elements can be wrapped in an accessible view to be read as one. Deleting accessibility has no benefit and is expensive. The time needed to address accessibility issues depends on the app's size. Checking and fixing problems requires navigating the entire app. Checks must be done on both iOS and Android to avoid introducing bugs.
The screen reader user is able to do that feature. Also, the focus is very important. We need to make sure that the elements get focused in the correct order. If a specific order is needed for any reason, we need to make sure that also screen reader users are able to do that order.
UI changes are announced. If as a consequence of some user action we do update the UI or we show some toast elements, we need to make sure that those information are actually provided to the screen reader user. We can make it announced using one of the accessibility APIs provided by React Native. For example, if some elements appear, for example, some error after an API fails, we need to make sure that this element gains the focus, and though gets announced by the screen reader user.
One thing very important is that we need to make sure that the behavior and the appearance is consistent across the entire app. The app needs to be predictable. And if we have a Bluetooth button still the same thing, they need to look like the same. Another important thing to keep in mind is group content if needed. Sometimes this can be part of the shared component we created, but we need to double check it every time. We need to make sure that it's ok. In our case we have a context here where we have a title, a header and the copy and the author and a button. The quote of the day, that's a header, can be a focus separately, that's fine, but the of the quote, in our case it's a quote from Walt Disney, the copy and the author should be focused as one thing. And to do that usually you can just wrap the multiple text in accessible view. This will force the screen reader to actually read the text as one. That's another example from our app, where we have a SUBTOTAL and $20, we want to make sure that the user select the row SUBTOTAL $20 and read it as one. We don't want the user to select SUBTOTAL first and $20 after. So whenever you have released information that needs to be read together, just make sure that's the case.
So here there is a small table of quick cons of deleting accessibility or build an accessible app from scratch. If you delete accessibility, you don't get any benefit. Config after the efforts I had to put in, a config of a single benefit that gives you deleting accessibility is actually very expensive and requires a big effort. The time needed to address accessibility issue depends by the size of your app, so the bigger the worse. To find a problem and fix them, you need to navigate through the entire app. You can't just look at the code and guess how it's going to work. Also, don't forget, all the check needs to be done on both platforms, iOS and Android. So this means double work. You have a risk of introducing bugs.
4. Building Accessible Components
If you start with accessible, you only need to make components accessible once. The component will handle most of the accessibility needs, and you just need to put it together. When testing, you only need to test what you're actually building.
It can happen if you need to replace a library with another, or you need to modify the logic to make it accessible. While if you start with accessible, it does require some effort to make a component accessible, The thing is, you do once. You only make accessible when you create it. Sure, the component will already handle most of the accessibility needs. Eventually, you just need to put it together. When you do a test, because you still need to test, you only test what you're actually building. You don't need to test the entire app all the time from scratch. You just test what you build.