Accessibility States
Severity | Serious |
---|---|
Accessibility Principle | Understandable |
Affected users | Visual |
Success criterion | 4.1.2 |
Accessibility states are specific attributes that can be added to a component to communicate its current status to assistive technology.
aria-busy
• Severity: SeriousIndicates an element is being modified and that assistive technologies may want to wait until the changes are complete before informing the user about the update.[^1]
Type | Default |
---|---|
boolean | false |
Expectations
- When: The user triggers (double tap) a component
- And: The component is performing a long (or async) task
- Then: The Screen Reader announces the component as busy
- And: The component is performing a long (or async) task
Screen Reader behaviour
Assuming we have a button that adds the given product ID to the cart, which requires an API call:
const AddToCart = ({ productID }: { productID: string }) => {
const { addToCart, isAddingToCart } = useQuery(ADD_TO_CART);
const onPress = async () => {
const result = await addToCart();
};
return (
<Pressable
accessibilityLabel="Add to cart"
accessibilityRole="button"
ariaBusy={isAddingToCart}
onPress={isAddingToCart ? undefined : onPress}
>
{isAddingToCart} ? <ActivityIndicator /> : <Text>Add to cart</Text>
</Pressable>
);
};
In the example, while the adding action is happening, the button:
- Ignores any press action
- Shows a loading spinner
While this works fine for sighted users, we must add the ariaBusy={isAddingToCart}
property for visually impaired users to signal that the action is still happening.
The user double taps on the example component:
VoiceOver | Talkback | |
---|---|---|
plays a sound as confirmation | Good |
The user focuses again on the component while the API is still in flight:
VoiceOver | Talkback | |
---|---|---|
Add to cart, busy, button, double tap to activate | Good |
aria-checked
• Severity: SeriousIndicates the state of a checkable element. This field can either take a boolean or the "mixed" string to represent mixed checkboxes.
Type | Default |
---|---|
boolean, 'mixed' | false |
Expectations
- When: The component can be toggle
- And: Receives the focus
- Then: The checked status is announced
- And: And the accessibility label is announced
- And: And the accessibility role is announced
- And: And the available action is announced
- And: Receives the focus
aria-checked
is not to be confused with aria-selected
.
aria-checked
is only meant to be used with checkboxes and toggle buttons.
Screen Reader behaviour
type ToggleButtonProps = {
checked: boolean;
label: string;
};
export const ToggleButton = ({ checked, label }: ToggleButtonProps) => {
return (
<Pressable
accessibilityLabel={label}
ariaChecked={checked}
ariaRole="button"
>
{label}
</Pressable>
);
};
Assuming the button label
is: Add me to the list
The user selects the component
State | VoiceOver | Talkback | |
---|---|---|---|
checked | ticked, Add me to the list, tickbox, double tap to toggle | Good | |
not checked | not ticked, Add me to the list, tickbox, double tap to toggle | Good |
The user double taps on the example component:
New state | VoiceOver | Talkback | |
---|---|---|---|
checked | ticked | Good | |
not checked | not ticked | Good |
aria-disabled
• Severity: SeriousIndicates that the element is perceivable but disabled, so it is not editable or otherwise operable.
Type | Default |
---|---|
boolean | false |
Expectations
- When: The component is disabled
- And: Receives the focus
- Then: The Screen Reader announces its disabled status first
- And: The accessibility label is announced
- And: The accessibility role is announced
- And: Receives the focus
Screen Reader behaviour
const AddToCart = ({ disabled }) => {
return (
<Pressable
accessibilityLabel="Add to cart"
accessibilityRole="button"
disabled={disabled}
>
Add to cart
</Pressable>
);
};
When the component receives the focus
Is Disabled? | VoiceOver | Talkback | |
---|---|---|---|
false | Add me to the cart, button, double tap to activate | Add me to the cart, button, double tap to activate | Good |
true | dimmed, Add me to the cart, button | disabled, Add me to the cart, button | Good |
aria-expanded
• Severity: SeriousIndicates whether an expandable element is currently expanded or collapsed.
Type | Default |
---|---|
boolean | false |
Expectations
- When: The user triggers (double tap) a collapsable component, i.e. Accordion
- And: Its content is expanded/collapsed
- Then: The Screen Reader announces the component as expanded/collapsed
- And: Its content is expanded/collapsed
Screen Reader behaviour
export const Content = ({ content }) => {
const [isShowingMore, setIsShowingMore] = React.useState(false);
return (
<View>
<Text numberOfLines={isShowingMore ? undefined : 2}>{content}</Text>
<Pressable
accessibilityLabel="Show more"
accessibilityRole="button"
ariaExpanded={isShowingMore}
onPress={() => setIsShowingMore(showMore => !showMore)}
>
{isShowingMore ? 'Show less' : 'Show more'}
</Pressable>
</View>
);
};
When the component receives the focus
Is the content expanded? | VoiceOver | Talkback | |
---|---|---|---|
false | Show more, collapsed button, double tap to activate | Good | |
true | Show less, expanded, button, double tap to activate | Good |
When the component is activated (double-tap)
Is the new state expanded? | VoiceOver | Talkback | |
---|---|---|---|
false | Show more, collapsed button, double tap to activate | Good | |
true | Show less, expanded, button, double tap to activate | Good |
aria-selected
Indicates whether a selectable element is currently selected or not.
Type | Default |
---|---|
boolean | false |
Expectations
- When: The user selects an option component, i.e. radio button
- Then: The Screen Reader announces the component selected state first
Screen Reader behaviour
const OptionButton = ({ selected, label }) => {
return (
<Pressable
accessibilityLabel={label}
accessibilityRole="button"
ariaSelected={selected}
>
<Text>{label}</Text>
</Pressable>
);
};
const TestScreen = () => {
const [selectedOption, setSelectedOption] = React.useState('Big');
return ['Big', 'Medium', 'Small'].map(size => {
return (
<Option
label={size}
selected={optionSelected}
onPress={() => setSelectedOption(size)}
/>
);
});
};
When the component receives the focus
Is Selected? | VoiceOver | Talkback | |
---|---|---|---|
false | accessibility label, button, double tap to activate | Good | |
true | Selected, accessibility label button, double tap to activate | Good |
When the component is activated (double-tap)
Is the new state selected? | VoiceOver | Talkback | |
---|---|---|---|
true | accessibility label, selected | Good | |
false | Good |
Related AMA components
External references
- MDN: aria-busy
- MDN: aria-checked
- MDN: aria-disabled
- MDN: aria-expanded
- MDN: aria-selected
- How Not To Misuse ARIA States, Properties and Roles