Accessibility
Why
- Stardust is a flexible UI library that allows composition of components. Accessibility patterns are much more restrictive. We need a way of applying the restrictive patterns to the flexible UI components
- Stardust should provide accessibility by default, but the developer using Stardust should be able to modify/override the behavior to fit his specific needs
- All components need to be semantically correct
- RTL has impact on keyboard navigation as it switches right/left arrow key functionality
What is accessibility
- sufficient contrast in standard themes
- high contrast theme
- zoom
- keyboard navigation / focus handling / focus highlighting
- screen reader support for virtual cursor and narration
Proposal
Goal
- each component must be accessible
- each component should have correct color contrast and high contrast theme defined
- component should have its own default accessibility behavior, which can be overridden from parent component if needed
- accessibility behavior should be separated from the component, extendable and reusable
- accessibility behavior should be easy to use
Behaviors
Component uses accessibility behavior to apply event handlers and aria properties to rendered HTML elements.
The accessibility behavior of a component is initially determined by the component type and properties.
User of the component (developer) can override this default behavior by providing a different behavior type in the properties of the component.
There are two parts of accessibility, which should be encapsulated in the behaviors:
aria-*
attributes - specifies the set of attributes necessary for correct narrating by screen readers.
- keyboard navigation - allows using the application without mouse, whenever user navigates with keyboard he/she should be able to see where the focus is. The focus can be trapped in some areas (e.g. modal dialogs).
Stardust will use Office UI Fabric FocusZone component for basic keyboard navigation (arrow keys, home/end).
On top of the basic keyboard navigatoion, behaviors can attach event handlers to the rendered elements. Component defines actions which can be called by behaviors
to change the component state or focus a particular element.
Once an action is executed based on the event handler, the default event propagation should be prevented.
Themes and color contrast
- each component should have at least two themes: standard and high contrast, to support this we use CSS -in-JS technique
- each component's color palette should be verified for sufficient contrast
Focus management
Office UI Fabric FocusZone does the basic focus management which contains:
- setting tabIndex 0 to currently focused element and setting tabIndex -1 to other elements from the focus zone
- moving the focus according to the arrow keys pressed
On top of the basic focus management specific handling needs to be added for:
- focus trap - when focus is trapped inside of a dialog, all other focusable elements need to be prevented from getting the focus either by handling tab key events or by setting their tabIndex to -1. In addition to that, main container needs to have
aria-hidden='true'
to prevent the screen reader from reading anything else than the current dialog.
- focus stack - focus should return to the previous focused element if popups or modals were dismissed. To achieve that, Stardust will allow sending an onDismiss callback to relevant components and the components will call the callback when they are closed/removed from DOM.
- focus indicator - used to highlight focus when application is in the keyboard mode
Implementation
The idea behind accessibility behaviors is to decouple accessibility logic from the component and make it reusable, easy overridable and extendable.
Behavior is either a plain javascript object or a function that transforms props into such object. The object contains attributes
and keyHandlers
maps for each component part, for example:
export const ButtonBehavior: Accessibility = (props: any) => ({
attributes: {
root: {
role: 'button',
'aria-hidden': false,
'aria-disabled': !!props['disabled'],
},
},
})
props
in this case is combination of the component's props
and state
. This allows the behaviors to be framework agnostic and more robust. (If a property moves from props to state, the behavior will not need to be changed.)
Stardust framework should provide a set of predefined behaviors. Users can add their custom behaviors by registering them to AccessibilityFactory
using the static registerAccessibility(name: string, accessibility: Accessibility)
method.
The component needs to define its default accessibility in defaultProps
:
public static defaultProps = {
as: 'button',
accessibility: AccessibilityType[AccessibilityType.button],
}
If the accessibility behavior depends on the props or state of the component, the component should instead override getDefaultAccessibility
method:
getDefaultAccessibility() {
return this.props.selection
? AccessibilityType[AccessibilityType.selectableList]
: AccessibilityType[AccessibilityType.list]
}
The evaluated accessibility
object is passed to the component's renderComponent
method:
public renderComponent({ ElementType, classes, rest, accessibility }): React.ReactNode {
...
return (
<ElementType
className={classes.root}
disabled={disabled}
onClick={this.handleClick}
{...accessibility.attributes.root}
{...rest}
>
{getContent()}
</ElementType>
)
}
{…rest}
goes after behavior generated attributes, to give more flexibility and allow to override anything generated by the behavior if developers need it.
Whenever component changes its state, the new aria-*
attributes will be provided back to the component by the accessibility behavior.
Every component that can consume accessibility behavior, needs to be tested using handlesAccessibility
test helper:
handlesAccessibility(MenuItem, { defaultRootRole: 'presentation' })
handlesAccessibility(MenuItem, { defaultRootRole: 'menuitem', partSelector: 'a' })
Supported client/screen reader combinations
- Win/Desktop - JAWS
- Win/Chrome - JAWS
- Win/Edge - Narrator
- Win/Firefox - NVDA
- Mac/Desktop - VoiceOver
Glossary
Component - Stardust (react) component
Accessibility behavior - Javascript function,object or class that defines the behavior of a component
Element - HTML element rendered by the component
Event - HTML event (for example keydown)