Accessible accordion in native Android applications – simple solution

Note: This post is older than two years. It may still be totally valid, but things change and technology moves fast. Code based posts may be especially prone to changes...

(Loaded 1080 times)

I had to fail an Android app in a recent WCAG audit because it’s expand/collapse components didn’t have the semantics. On the review I got asked about possible solutions and here you go – hope it will help somebody make accordions on Android more accessible.

I’ve just done some accessibility audits of native apps and had to fail WCAG Success Criterion 4.1.2 Name Role Value because accordion didn’t have proper semantics. I didn’t bothered checking the Android documentation about technical possibilities of marking expand collapse (like in ARIA), but I just felt it could be done technically.

When I was later presenting the audit results I got a question from a developer if I have any tips on how to resolve it on native platform. I was honest about it and said that I will check. And this post is kind of a longer answer, hope it will help somebody someday.

React Native solution

As I am way more experienced with React Native compared to Java And Kotlin I decided to check what are our options there. And I quickly found that React Native supports some accessibility states with accessibilityState attribute (opens in new window);

  • disabled,
  • selected,
  • checked,
  • busy,
  • expanded.

Perfect, I thought – if React Native supports it for both iOS and Android then it has to be also possible in native Android. So I tried to check some code of native Android parts used in React Native and discovered that they aren’t really using the best option (at least yet). I’ve checked GitHub issues and stumbled upon an open issue that mentions the current downsides and also how to fix it properly (opens in new window);

The “expanded” accessibilityState for Android is currently faked by appending to the content description, rather than implemented correctly. 

GitHub issue on current implementation

Seems like they had some problems with some components, but I decided to test a proof of concept just to check it for myself. It worked well for my simple example, so I decided to publish it here. Please do test it before you use it in production;

import * as React from "react";
import { Text, View, TouchableHighlight } from "react-native";

export default function Accordion(props) {
  console.log("props", props);
  const [isOpen, setIsOpen] = React.useState(props.isActive);

  const onPress = () => {
    setIsOpen(!isOpen);
  };

  return (
    <View>
      <TouchableHighlight
        accessibilityState={{ expanded: isOpen }}
        accessibilityLabel={props.accTitle}
        accessibilityRole="button"
        onPress={onPress}
        style={{ paddingTop: 10, paddingBottom: 10, paddingLeft: 5 }}
      >
        <View>
          <Text style={{ fontSize: 20 }}>{`${props.accTitle} ${
            isOpen ? "-" : "+"
          }`}</Text>
        </View>
      </TouchableHighlight>
      {isOpen ? (
        <View style={{ paddingTop: 10, paddingBottom: 10, paddingLeft: 5 }}>
          <Text>{props.content}</Text>
        </View>
      ) : null}
    </View>
  );
}

And yes, before you ask – I tried to use a Button but it just didn’t work. Seems like we have to enforce the semantics on a less optimal element. And beware – if you are planning to use this code on web it is missing the proper aria-expanded, so I would not use this for a web version.

Anyway, I was happy with my TalkBack tests, the state was announced fine.

If you want to play, please check the working example on Expo snack (opens in new window).

Native Android solution

I didn’t test it by running it in an app. At least not yet, but it seems like the platform really supports expand and collapse states out of the box.

After digging in the documentation I stumbled upon ACTION_EXPAND that is available in AccessibilityAction and was added in API level 21 (opens in new window).

I am not an Android developer, so I will borrow the following text from the React Native issue that I mentioned previously (opens in new window);

Expandable and Collapsible are unique in the Android Accessibility API, in that they are not represented as properties on the View or AccessibilityNodeInfo, but are only represented as AccessibilityActions on the AccessibilityNodeInfo. This means that Talkback determines whether or not a node is “expandable” or “collapsible”, or potentially even both, by looking at the list of AccessibilityActions attached to the AccessibilityNodeInfo.

GitHub issue on React Native describing Android implementation

I think that this would have to make sense to a native Android developer and it looks like React Native will soon implement the “proper” expand and collapse in their native Android code as well.

If you managed to fix it in your app, please let me know. Would be useful to share with others and make apps more accessible.

Author: Bogdan Cerovac

I am IAAP certified Web Accessibility Specialist (from 2020) and was Google certified Mobile Web Specialist.

Work as digital agency co-owner web developer and accessibility lead.

Sole entrepreneur behind IDEA-lab Cerovac (Inclusion, Diversity, Equity and Accessibility lab) after work. Check out my Accessibility Services if you want me to help your with digital accessibility.

Also head of the expert council at Institute for Digital Accessibility A11Y.si (in Slovenian).

Living and working in Norway (🇳🇴), originally from Slovenia (🇸🇮), loves exploring the globe (🌐).

Nurturing the web from 1999, this blog from 2019.

More about me and how to contact me: