Breaking the silence
Screen readers and React apps
- Almero Steyn
- almerosteyn.com
- @kryptos_rsa
The design of mainstream products and/or services that are accessible to, and usable by, as many people as reasonably possible... - British Standards Institute
const AppMain = () =>
<Switch>
<Route path="/tasks" component={Tasks} />
<Route path="/task" component={Task} />
<Route path="/contact" component={Contact} />
<Redirect to="/tasks" />
</Switch>
;
class PageFocusSection extends Component {
componentDidMount() {
this.header.focus();
}
render() {
const { children, headingText } = this.props;
return (<section>
<h2 tabIndex="-1"
ref={header => {this.header = header;}}>
{headingText}
</h2>
{children}
</section>);
}
}
reactjs.org/docs/refs-and-the-dom.html
const Announcer = ({ message }) =>
<div role="log"
aria-live="polite"
aria-relevant="additions"
aria-atomic="true">
{message}
</div>;
//Possible values
//role: log | status | alert
//aria-live: polite | assertive | off
//aria-relevant: additions | removals | text | all
//aria-atomic: true | false
<label htmlFor={inputId}>{labelText}</label>
<input
id={inputId}
aria-invalid={showErrors}
aria-describedby={showErrors ? describedById : null}
onChange={onChangeHandler}
value={value} />
{showErrors ?
<div
id={describedById}
aria-live="polite"
aria-atomic="true">
{errorText}
</div>
: null}
<label htmlFor={inputId}>{labelText}</label>
<input
id={inputId}
aria-invalid={!showErrors}
aria-describedby={showErrors ? this.describedById : null}
onChange={onChangeHandler}
value={value} />
<div
aria-live="polite"
aria-atomic="true">
{showErrors ?
<span id={describedById}>{errorText}</span>
: null}
</div>
//...
import { LiveAnnouncer, LiveMessage } from 'react-aria-live';
//...
render() {
return (
<LiveAnnouncer>
<LiveMessage message={this.state.a11yMessage}
aria-live="polite" />
{this.props.children}
</LiveAnnouncer>
);
}
//..
Presentation online at:
almerosteyn.com/slides/Example app at:
github.com/AlmeroSteyn/react-a11y-patterns/tree/react-amsterdam-talk