Skip to content

Latest commit

 

History

History
435 lines (272 loc) · 7.65 KB

react-a11y.md

File metadata and controls

435 lines (272 loc) · 7.65 KB

React a11y

Require to have a non-empty alt prop, or role="presentation"

✅ Enabled (error)

// Bad
/*
function Foo(src) {
	return <img src={src} />;
}
*/

// Good
function Foo({ alt, src }) {
	return <img alt={alt} src={src} />;
}

Enforce all aria-* props are valid.

✅ Enabled (error)

// Bad
/*
<div id="address_label">Enter your address</div>;
<input aria-labeledby="address_label" />;
*/

// Good
<div id="address_label">Enter your address</div>;
<input aria-labelledby="address_label" />;

Enforce ARIA state and property values are valid.

✅ Enabled (error)

// Bad
/*
<span aria-hidden="yes">foo</span>;
*/

// Good
<span aria-hidden="true">foo</span>;

Require ARIA roles to be valid and non-abstract

✅ Enabled (error)

// Bad
/*
<div role="datepicker"></div>;
<div role="range"></div>;
<div role=""></div>;
*/

// Good
<div role="button"></div>;
<div role={role}></div>;
<div></div>;

Enforce that elements that do not support ARIA roles, states, and properties do not have those attributes.

✅ Enabled (error)

// Bad
/*
<meta aria-hidden="false" charSet="UTF-8" />;
*/

// Good
<meta charSet="UTF-8" />;

disallow href "#"

❌ Disabled

// Bad
<a href="#" />;
<a href={"#"} />;
<a href={`#`} />;

// Good
<a href="https://github.com" />;
<a href="#section" />;
<a href="foo" />;
<a href={undefined} />;

Prevent img alt text from containing redundant words like "image", "picture", or "photo"

✅ Enabled (error)

// Bad
/*
<img alt="Photo of foo being weird." src="foo" />;
<img alt="Image of me at a bar!" src="bar" />;
<img alt="Picture of baz fixing a bug." src="baz" />;
*/

// Good
<img alt="Foo eating a sandwich." src="foo" />;
<img
	aria-hidden
	alt="Picture of me taking a photo of an image"
	src="bar"
/>;
<img alt={`Baz taking a ${photo}`} src="baz" />;

require that JSX labels use "htmlFor"

❌ Disabled

// Bad
/*
function Foo(props) {
	return <label {...props} />;
}
*/

// Good
function Foo({ htmlFor, props }) {
	return <label htmlFor={htmlFor} {...props} />;
}

require that mouseover/out come with focus/blur, for keyboard-only users

❌ Disabled

// Bad
<div onMouseOver={() => {}} />;
<div onMouseOut={() => {}} />;
<div onMouseOver={() => {}} />;
<div onMouseOut={() => {}} />;

// Good
<div onMouseOver={() => {}} onFocus={() => {}} />;
<div onMouseOut={() => {}} onBlur={() => {}} />;
<div onMouseOver={() => {}} onFocus={() => {}} />;
<div onMouseOut={() => {}} onBlur={() => {}} />;

Prevent use of accessKey

✅ Enabled (error)

// Bad
/*
<div accessKey="h" />;
*/

// Good
<div />;

require onBlur instead of onChange

❌ Disabled

// Bad
<select onChange={updateModel} />;

// Good
(
	<select onBlur={updateModel}>
		<option />
	</select>
);

(
	<select>
		<option onBlur={handleOnBlur} onChange={handleOnChange} />
	</select>
);

Enforce that elements with onClick handlers must be focusable.

❌ Disabled

// Bad
<span onClick="submitForm();">Submit</span>;
<a onClick="showNextPage();">Next page</a>;

// Good
<div aria-hidden onClick={() => {}} />;
<span onClick="doSomething();" tabIndex="0">Click me!</span>;
<span onClick="doSomething();" tabIndex="-1">Click me too!</span>;
<button onClick="doSomething();">Click the button :)</button>;

require things with onClick to have an aria role

❌ Disabled

// Bad
<div onClick={() => {}} />;
<div onClick={() => {}} {...props} />;
<div onClick={() => {}} aria-hidden={false} />;
<a onClick={() => {}} />;

// Good
<div onClick={() => {}} role="button" />;
// Interactive element does not require role.
<input type="text" onClick={() => {}} />;
// tabIndex makes this interactive.
<a tabIndex="0" onClick={() => {}} />;
// button is interactive.
<button onClick={() => {}} className="foo" />;
// This is hidden from screenreader.
<div
	onClick={() => {}}
	role="button"
	aria-hidden
/>;
// This is a higher-level DOM component
<Input onClick={() => {}} type="hidden" />;

Enforce that elements with ARIA roles must have all required attributes for that role.

✅ Enabled (error)

// Bad
/*
<span
	role="checkbox"
	aria-labelledby="foo"
	tabIndex="0"
>
</span>;
*/

// Good
<span
	role="checkbox"
	aria-checked="false"
	aria-labelledby="foo"
	tabIndex="0"
>
</span>;

Enforce that elements with explicit or implicit roles defined contain only aria-* properties supported by that role.

✅ Enabled (error)

// Bad
/*
(
	<ul role="radiogroup" aria-labelledby="foo">
		<li aria-required tabIndex="-1" role="radio" aria-checked="false">Rainbow Trout</li>
		<li aria-required tabIndex="-1" role="radio" aria-checked="false">Brook Trout</li>
		<li aria-required tabIndex="0" role="radio" aria-checked="true">Lake Trout</li>
	</ul>
);
*/

// Good
(
	<ul role="radiogroup" aria-required aria-labelledby="foo">
		<li tabIndex="-1" role="radio" aria-checked="false">Rainbow Trout</li>
		<li tabIndex="-1" role="radio" aria-checked="false">Brook Trout</li>
		<li tabIndex="0" role="radio" aria-checked="true">Lake Trout</li>
	</ul>
);

Enforce tabIndex value is not greater than zero.

❌ Disabled

// Bad
<span tabIndex="5">foo</span>;
<span tabIndex="3">bar</span>;
<span tabIndex="1">baz</span>;
<span tabIndex="2">never really sure what goes after baz</span>;

// Good
<span tabIndex="0">foo</span>;
<span tabIndex="-1">bar</span>;
<span tabIndex={0}>baz</span>;