Take SectionHeader as an example. This UI component is used to communicate the structure on the page and group content together. We mark this component to be an accessibility heading in the component code so it is accessible in all screens that contain this component.
We invested in automated accessibility testing and linting to run with every code commit, which creates a quick feedback loop for engineers and empowers them to make the app accessible at code writing time. The checks are fast, reliable, and scale well with our fast-growing features in the Android app.
Automated testing
We set up Espresso-based automated testing to check for accessibility issues. Espresso is a popular testing library for Android UI with built-in accessibility checks. It supports a comprehensive set of accessibility rules and is easy to set up:
If accessibility checks fail, the test outputs an error stack trace that engineers can use to debug the issue. For example:
In this example, engineers can provide a content description to the image view to satisfy accessibility requirements.
We also screenshot test our components with a larger font size to ensure the behavior is correct using Happo.
Linting
In addition to automated testing, we also enabled linting, including Android Lint rules for accessibility and custom lint rules built with Ktlint.
Here is an example of an Android accessibility lint rule:
Besides the built-in Android Lint, we also use Ktlint to build custom lint rules. For instance, when a user navigates to a new screen, we provide a page name for a screen reader to announce. We use the following rule to make sure that the page name is localized.
Lint rules are straightforward to set up and provide timely feedback, but linting has limitations — it can only perform static code analysis.
Today, these automated checks run as part of CI (Continuous Integration) checks for every code commit. If a pull request does not pass the checks, it will be blocked from being merged into the primary code branch. We still use manual testing to cover the areas that automated checks do not cover, such as the traversal order of UI elements on a page. Automated and manual checks complement each other well.
Over the past year, we have been integrating Jetpack Compose into our app. Google’s Accessibility in Compose documentation has been a great resource to ensure our Compose components and screens remain accessible. While there are some notable things missing that existed with Views (e.g. focus order modification), Compose is still a young library and we look forward to future improvements. Here are a couple of things worth mentioning about our Compose-specific accessibility tooling:
Proactively encourage content descriptions in the API
One of our guidelines for UI components is that content descriptions exposed via a function parameter should not use a default value. This brings accessibility to the top of mind when an engineer uses the component as they need to consider what value to pass. A null value is still acceptable in cases where that UI element is not important for accessibility.
Page name announcements
When using Fragments and Views, we use the View.setAccessibilityPaneTitle() and View.announceForAccessibility() APIs when navigating to a new screen to announce a descriptive page name to the user. These APIs do not exist in Compose but we wanted to keep the functionality since it helps to provide more context as to what the new screen displays. Our current workaround sets certain semantics on the screen’s outer composable:
We use the liveRegion property so changes can be announced when the content description changes. This is useful for pages whose entire content is determined by a response from the server. In this case, TalkBack would announce “Content Loading” while the network request is pending, followed by “Content Loaded” when it completes, and finally the page description defined in the server response. One downside of this approach is that it requires the outer container to be focusable, which requires an additional navigation action to get to the content.
Making our Android app more accessible has been an impactful journey. Improving app accessibility involves following best practices, adding rigorous enforcements, continually learning from mistakes, and putting in the work. All of these are worthy efforts to make sure an app works for all users.
If you are excited about building highly accessible products and the framework to support them, check out some of our related open positions:
Staff Android Software Engineer, Guest
Senior iOS Software Engineer, Infrastructure
It is a huge endeavor to make a complex app like the Airbnb Android app more accessible. This work wouldn’t be possible without the enormous efforts from the digital accessibility team and the close-knit Android community at Airbnb. Every engineer has contributed to making the features they own accessible. Making the Android app more accessible is an ongoing effort and it could not succeed without all of them.
All product names, logos, and brands are property of their respective owners. All company, product and service names used in this website are for identification purposes only. Use of these names, logos, and brands does not imply endorsement.
All bookings included in this blog post are intended to illustrate. Airbnb does not endorse or promote these listings or any other accommodations or experiences on the platform.