Handbook
Checklist

A11y checklist in React

Last updated by Noel Varanda (opens in a new tab),
React
Accessibility
A11y
Checklist
WCAG
WAI-ARIA

Please note that these examples are simplified snippets to demonstrate specific concepts. In real-world scenarios, you would integrate these concepts into your React components and application architecture based on your specific requirements.

The following takes the A11y Project Checklist (opens in a new tab) and applies it to React.

Content

Content is the most important part of your site.

Use plain language

Avoid figures of speech, idioms, and complicated metaphors.

Describe interactive elements

  • Make sure that button, a, and label element content is unique and descriptive.
  • Use the useId (opens in a new tab) hook to get a unique ID.
import { FC, useId } from 'react';
 
const MyButton: FC = () => {
  const emailId = useId();
 
  return (
    <>
      {/* Descriptive text "Click Me" that conveys the purpose or action associated with the button */}
      <button>Click Me</button>
      {/* Descriptive text "Learn More" that indicates the purpose or destination of the link */}
      <a href="/about">Learn More</a>
      {/* Label describing  */}
      <form>
        <label htmlFor={emailId}>Email:</label>
        <input type="email" id={emailId} />
      </form>
    </>
  );
};

Use correct language direction

Use left-aligned text for left-to-right (LTR) languages, and right-aligned text for right-to-left (RTL) languages.

Global code

Global code is code that affects your entire website or web app.

Validate your HTML

Use the Valid HTML (opens in a new tab) validator to provide a consistent, expected experience across all browsers and assistive technology.

lang attribute

Use a lang attribute on the html element. This helps assistive technology such as screen readers to pronounce content correctly.

const App = () => <html lang="en">{/* Your app content here */}</html>;

Unique page titles

Provide a unique title for each page or view, as it is often the first piece of information announced by assistive technology. Ensure proper page titles using react-helmet (opens in a new tab).

import React from 'react';
import { Helmet } from 'react-helmet';
 
const MyComponent: React.FC = () => (
  <>
    <Helmet>
      <title>My Page Title</title>
      <meta name="description" content="This is the description of my page" />
    </Helmet>
    {/* Your component's JSX */}
  </>
);

Use the <Head /> component for Next.js (opens in a new tab).

Viewport Zoom

Some people need to increase the size of text to a point where they can read it. Do not stop them from doing this, even for web apps with a native app-like experience.

  • Don't set custom attribute to user-scalable="no"
  • Remove the user-scalable="no" parameter from the content attribute of the <meta name="viewport"> element if it's been added element
<!-- Bad 👎 -->
<meta name="viewport" content="width=device-width, user-scalable=no" />

Landmark Elements

Use landmark elements to indicate important content regions.

HTML Semantic Elements

Use semantic HTML elements like <h1>, <nav>, <main>, <section>, etc., to provide clear structure and meaning to your content. Understand the fundamentals:

Linear Content Flow

Ensure a linear content flow.

  • Remove tabIndex attribute values that aren't either 0 or -1.
  • Do not add tabIndex to interactable elements (a, button).

Avoid autofocus

Additionally, autofocus can be problematic for people with motor control disabilities, or who are blind or who have low vision. Avoid using the autofocus attribute.

// Bad 👎
const MyForm = () => <input autoFocus />;
// Bad 👎
import React, { useEffect, useRef } from 'react';
 
const MyForm: React.FC = () => {
  const inputRef = useRef<HTMLInputElement>(null);
 
  useEffect(() => {
    if (inputRef.current) {
      inputRef.current.focus();
    }
  }, []);
 
  return (
    <form>
      <input ref={inputRef} type="text" />
    </form>
  );
};

Allow extending session timeouts

This task involves backend implementation and is not specific to React. You would typically handle session timeouts on the server-side and provide mechanisms for extending or adjusting session timeouts.

Remove the title attribute

  • The title attribute has numerous issues, and should not be used if the information provided is important for all people to access.

  • Only use a title on iframes.

Keyboard

It is important that your interface and content can be operated and navigated by the use of a keyboard. Some people cannot use a mouse or may be using other assistive technologies that may not allow for hovering or precise clicking.

Visible Focus Style

Make sure there is a visible focus style for interactive elements that are navigated to via keyboard input.

import './styles.css';
 
const VisibleFocusStyleExample = () => (
  <button className="focusable-button">Click Me</button>
);
styles.css
.focusable-button:focus {
  outline: 2px solid blue;
}

Remove Invisible Focusable Elements

Remove the ability to focus on elements that are not presently meant to be discoverable. This includes things like inactive drop down menus, off screen navigations, or modals.

import './styles.css';
 
const RemoveInvisibleFocusableElementsExample = () => (
  <div>
    <button className="hidden-button">Button 1</button>
    <input type="text" className="hidden-input" />
    <a href="#" className="invisible-link">
      Link
    </a>
  </div>
);
styles.css
.hidden-button {
  visibility: hidden;
}
 
.hidden-input {
  display: none;
}
 
.invisible-link {
  opacity: 0;
}

Keyboard Focus Order

Check to see that keyboard focus order matches the visual layout.

Images

Images are a very common part of most websites. Help make sure they can be enjoyed by all.

Ensure img elements have an alt attribute

const ImgAltAttributeExample = () => (
  <img src="path/to/image.jpg" alt="A beautiful landscape" />
);

Use null alt attribute for decorative images

Decorative images do not communicate information that is required to understand the website's overall meaning. Make sure that decorative images use null (empty) alt attribute values.

const DecorativeImageExample = () => (
  <img src="path/to/decorative-image.jpg" alt={null} />
);

Include image text in alt description

If there is text in the image, make sure to add it to the alt tag.

Logo of ABC Company - Providing innovative solutions

const DecorativeImageExample = () => (
  <img
    src="path/to/image-with-text.jpg"
    alt="Logo of ABC Company - Providing innovative solutions"
  />
);

Provide a text alternative for charts, graphs, and maps

Make sure to convey what the chart/graph/map is communicating in the alt field.

const ComplexImageExample = () => (
  <img
    src="path/to/complex-image.jpg"
    alt="Flowchart depicting the sales process:
          - Start: Incoming leads
          - Step 1: Lead qualification
          - Step 2: Product demo
          - Step 3: Negotiation
          - Step 4: Closing the deal
          - End: Successful sale"
  />
);

Headings

Heading elements (h1, h2, h3, etc.) help break up the content of the page into related "chunks" of information. They are incredibly important for helping people who use assistive technology to understand the meaning of a page or view.

Use heading elements to introduce content

Use heading elements to introduce different sections or chunks of content on your page or view.

const HeadingExample = () => (
  <div>
    <h1>Welcome to Our Website</h1>
    <h2>About Us</h2>
    <p>...</p>
  </div>
);

Use only one h1 element per page or view

Ensure that each page or view has only one h1 element, as it represents the main heading of the page and holds the highest level of importance.

// Bad 👎
const BadHeadingExample = () => (
  <div>
    <h1>Welcome to Our Website</h1>
    <h1>About Us</h1>
    <p>...</p>
  </div>
);
 
// Good 👍
const GoodHeadingExample = () => (
  <div>
    <h1>Welcome to Our Website</h1>
    <h2>About Us</h2>
    <p>...</p>
  </div>
);

Heading elements should be written in a logical sequence

Ensure that heading elements are written in a logical sequence, where higher-level headings (e.g. h1) are followed by lower-level headings (e.g. h2, h3, etc.) in a hierarchical order.

// Bad 👎
const BadHeadingExample = () => (
  <div>
    <h2>About Us</h2>
    <h1>Welcome to Our Website</h1>
    <p>...</p>
  </div>
);
 
// Good 👍
const GoodHeadingExample = () => (
  <div>
    <h1>Welcome to Our Website</h1>
    <h2>About Us</h2>
    <p>...</p>
  </div>
);

Don't skip heading levels

Avoid skipping heading levels, as it can lead to confusion and make it harder for users to understand the organization and structure of the content.

// Bad 👎
const BadHeadingExample = () => (
  <div>
    <h1>Welcome to Our Website</h1>
    <h3>About Us</h3>
    <p>...</p>
  </div>
);
 
// Good 👍
const GoodHeadingExample = () => (
  <div>
    <h1>Welcome to Our Website</h1>
    <h2>About Us</h2>
    <p>...</p>
  </div>
);

Lists

Lists elements let people know a collection of items are related and if they are sequential, and how many items are present in the list grouping.

Use list elements (ol, ul, and dl elements) for list content

When presenting a collection of related items, use appropriate list elements to structure the content. The <ol>, <ul>, and <dl> elements are used to create ordered, unordered, and definition lists, respectively.

const ListExample = () => (
  <div>
    <h2>Ordered List Example</h2>
    <ol>
      <li>Item 1</li>
      <li>Item 2</li>
      <li>Item 3</li>
    </ol>
 
    <h2>Unordered List Example</h2>
    <ul>
      <li>Item A</li>
      <li>Item B</li>
      <li>Item C</li>
    </ul>
 
    <h2>Definition List Example</h2>
    <dl>
      <dt>Term 1</dt>
      <dd>Definition 1</dd>
      <dt>Term 2</dt>
      <dd>Definition 2</dd>
    </dl>
  </div>
);

Controls

Controls are interactive elements such as links and buttons that let a person navigate to a destination or perform an action.

Use the a element for links

In the bad example, a non-semantic <span> element is used as a clickable area instead of using the appropriate <a> element. The good example demonstrates the correct usage of the <a> element with a valid href attribute.

// Bad 👎
const BadLinkExample = () => (
  <p>
    Click <span onClick={handleClick}>here</span> to visit our website.
  </p>
);
 
// Good 👍
const GoodLinkExample = () => (
  <p>
    Click <a href="/about">here</a> to visit our website.
  </p>
);

Ensure that links are recognizable

Make sure that links are visually distinguishable from other text and provide clear visual cues that they are clickable.

  • Don't only rely on colour.
  • Underlines are also useful.
// Bad 👎
const BadLinkStyleExample = () => (
  <p>
    Visit our website <a href="/about">here</a> for more information.
  </p>
);
 
// Good 👍
const GoodLinkStyleExample = () => (
  <p>
    Visit our website{' '}
    <a href="/about" style={{ textDecoration: 'underline' }}>
      here
    </a>{' '}
    for more information.
  </p>
);

In the bad example, the link is not visually distinguished from the surrounding text, making it difficult for users to identify it as a clickable element. The good example demonstrates using CSS styles (in this case, underlining) to make the link visually distinct.

Ensure that controls have :focus states

Provide a visible focus state for interactive elements, such as links and buttons, to indicate to users when they have keyboard focus.

// Bad 👎
const BadFocusStyleExample = () => <button>Submit</button>;
 
// Good 👍
const GoodFocusStyleExample = () => (
  // In a real life example this would not be in an inline style
  <button style={{ outline: '2px solid blue' }}>Submit</button>
);

Use the button element for buttons

Use the <button> element when creating buttons for interactive actions.

// Bad 👎
const BadButtonExample = () => (
  <div>
    <span onClick={handleClick}>Submit</span>
  </div>
);
 
// Good 👍
const GoodButtonExample = () => (
  <div>
    <button onClick={handleClick} type="button">
      Submit
    </button>
  </div>
);

In the bad example, a non-semantic <span> element is used as a clickable area instead of using the appropriate <button> element. The good example demonstrates the correct usage of the <button> element.

Provide a skip link and make sure that it is visible when focused

Include a "skip link" at the beginning of your page to allow users to skip repetitive navigation and jump directly to the main content. Ensure that the skip link becomes visible and accessible when it receives focus.

// Bad 👎
const BadSkipLinkExample = () => (
  <div>
    <a href="#main-content">Skip to main content</a>
    {/* Rest of the page content */}
  </div>
);
 
// Good 👍
const GoodSkipLinkExample = () => (
  <div>
    <a
      href="#main-content"
      style={{
        position: 'absolute',
        left: '-10000px',
        top: 'auto',
        width: '1px',
        height: '1px',
        overflow: 'hidden',
      }}
    >
      Skip to main content
    </a>
    {/* Rest of the page content */}
    <main id="main-content">{/* Main content */}</main>
  </div>
);

In the bad example, the skip link is included in the markup but is visible to sighted users, cluttering the page visually. The good example shows the skip link positioned off-screen using CSS to make it invisible to sighted users, but still accessible to screen readers and keyboard users.

Identify links that open in a new tab or window

Indicate links that open in a new tab or window by adding an appropriate visual cue, such as an icon or text, to let users know the link behavior.

// Bad 👎
const BadNewTabLinkExample = () => (
  <a href="/external" target="_blank">
    Visit external website
  </a>
);
 
// Good 👍
const GoodNewTabLinkExample = () => (
  <a href="/external" target="_blank" rel="noopener noreferrer">
    Visit external website
    <span aria-hidden="true"> (opens in a new tab)</span>
    <span className="sr-only">(opens in a new tab)</span>
  </a>
);

In the bad example, a link that opens in a new tab is not visually distinguishable from regular links, potentially causing confusion for users. The good example demonstrates adding the text "(opens in a new tab)" visually and using a visually hidden class (sr-only) to provide the same information to screen reader users.

The noopener noreferrer value for the rel attribute in the example serves two purposes when opening a link in a new tab or window:

  1. noopener: It prevents the newly opened window from having access to the window.opener property, which helps protect against certain security vulnerabilities. By setting noopener, the new window cannot modify the window that opened it, preventing potential malicious actions.

  2. noreferrer: It prevents the referring URL from being sent as a Referer header when the new window is opened. This enhances privacy by not revealing the source URL of the previous page to the linked website.

Including both noopener and noreferrer provides an extra layer of security and privacy when opening links in new tabs or windows. It's generally recommended to include these attributes when using target="_blank".

Tables

If you need to display data in rows and columns? Use the table element.

// Bad 👎
const BadTableExample = () => (
  <>
    <div>
      <div>Column 1</div>
      <div>Column 2</div>
      <div>Column 3</div>
    </div>
    <div>
      <div>Data 1</div>
      <div>Data 2</div>
      <div>Data 3</div>
    </div>
  </>
);
 
// Good 👍
const GoodTableExample = () => (
  <table>
    <thead>
      <tr>
        <th>Column 1</th>
        <th>Column 2</th>
        <th>Column 3</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Data 1</td>
        <td>Data 2</td>
        <td>Data 3</td>
      </tr>
    </tbody>
  </table>
);

Use the correct table head elements

Use the thead element to wrap table heads and use the th element for table headers (with appropriate scope attributes).

// Bad 👎
const BadTableHeaderExample = () => (
  <table>
    <tr>
      <td>Column 1</td>
      <td>Column 2</td>
      <td>Column 3</td>
    </tr>
  </table>
);
 
// Good 👍
const GoodTableHeaderExample = () => (
  <table>
    <thead>
      <tr>
        <th scope="col">Column 1</th>
        <th scope="col">Column 2</th>
        <th scope="col">Column 3</th>
      </tr>
    <thead>
  </table>
);

Use the caption element

Use the caption element to provide a title for the table.

// Bad 👎
const BadTableCaptionExample = () => (
  <table>
    <tr>
      <th scope="col">Column 1</th>
      <th scope="col">Column 2</th>
      <th scope="col">Column 3</th>
    </tr>
    {/* Rest of table... */}
  </table>
);
 
// Good 👍
const GoodTableCaptionExample = () => (
  <table>
    <caption>Table Caption</caption>
    <thead>
      <tr>
        <th scope="col">Column 1</th>
        <th scope="col">Column 2</th>
        <th scope="col">Column 3</th>
      </tr>
    </thead>
    {/* Rest of table... */}
  </table>
);

Forms

Forms allow people to enter information into a site for processing and manipulation. This includes things like sending messages and placing orders.

Add corresponding label elements

All inputs in a form are associated with a corresponding label element.

💡

Tip: Use the useId (opens in a new tab) hook to create unique IDs in React.

// Bad 👎
const BadInputLabelExample = () => (
  <form>
    <input type="text" id="name" />
  </form>
);
 
// Good 👍
import { useId } from 'react';
 
const GoodInputLabelExample = () => {
  const emailId = useId();
 
  return (
    <form>
      <div>
        <label htmlFor={emailId}>Email</label>
        <input type="email" id={emailId} />
      </div>
    </form>
  );
};

Use fieldset and legend elements

  • Use fieldset to group related form sections.
  • Use legend to provide context for what the section is for.
// Bad 👎
const BadFieldsetLegendExample = () => (
  <form>
    <div>
      <label htmlFor="email">Email</label>
      <input type="email" id="email" />
    </div>
  </form>
);
 
// Good 👍
const GoodFieldsetLegendExample = () => (
  <form>
    <fieldset>
      <legend>Personal Information</legend>
      <div>
        <label htmlFor="firstName">First name</label>
        <input type="text" id="firstName" />
      </div>
      <div>
        <label htmlFor="lastName">Last name</label>
        <input type="text" id="lastName" />
      </div>
    </fieldset>
  </form>
);

Autocomplete where applicable

Inputs use autocomplete where appropriate.

// Bad 👎
const BadAutocompleteExample = () => (
  <form>
    <div>
      <label htmlFor="name">Name</label>
      <input type="text" id="name" />
    </div>
  </form>
);
 
// Good 👍
const GoodAutocompleteExample = () => (
  <form>
    <div>
      <label htmlFor="name">Name</label>
      <input type="text" id="name" autoComplete="name" />
    </div>
  </form>
);

Any input errors are displayed

  • Make sure that form input errors are displayed in a list above the form after submission.
  • Associate input error messaging with the input it corresponds to.
// Bad 👎
const BadFormErrorExample = () => (
  <form>
    <div>
      <label htmlFor="name">Name</label>
      <input type="text" id="name" />
    </div>
    <button type="submit">Submit</button>
  </form>
);
 
// Good 👍
const GoodFormErrorExample = () => (
  <form>
    <div>
      <label htmlFor="name">Name</label>
      <input type="text" id="name" />
    </div>
    <ul>
      <li>Error 1</li>
      <li>Error 2</li>
      <li>Error 3</li>
    </ul>
    <button type="submit">Submit</button>
  </form>
);
 
// Better ⭐️
const GoodErrorAssociationExample = () => (
  <form>
    <div>
      <label htmlFor="name">Name</label>
      <input type="text" id="name" />
      <span role="alert" aria-labelledby="name">
        Error message
      </span>
    </div>
    <button type="submit">Submit</button>
  </form>
);

Adequate communication

Make sure that error, warning, and success states are not visually communicated by just color.

// Bad 👎
const BadStateCommunicationExample = () => (
  <form>
    <div>
      <label htmlFor="name">Name</label>
      <input type="text" id="name" className="error" />
    </div>
    <button type="submit">Submit</button>
  </form>
);
 
// Good 👍
const GoodStateCommunicationExample = () => (
  <form>
    <div className="form-field error">
      <label htmlFor="name">Name</label>
      <input type="text" id="name" />
      <span role="alert" aria-labelledby="name">
        Error message
      </span>
    </div>
    <button type="submit">Submit</button>
  </form>
);

Media

Media includes content such as pre-recorded and live audio and video.

Don't autoplay

Make sure that media does not autoplay, this can be distracting and disruptive.

// Bad 👎
const BadMediaAutoplayExample = () => (
  <video src="video.mp4" autoplay controls></video>
);
 
// Good 👍
const GoodMediaAutoplayExample = () => <video src="video.mp4" controls></video>;

Ensure that media controls use appropriate markup

⚠️
TBD
// Bad 👎
const BadMediaControlsExample = () => (
  <audio src="audio.mp3">
    <button>Play</button>
    <button>Pause</button>
  </audio>
);
 
// Good 👍
const GoodMediaControlsExample = () => <audio src="audio.mp3" controls></audio>;

Make sure space pauses media

⚠️
TBD

Provide a global pause function on any media element. If the device has a keyboard, ensure that pressing the Space key can pause playback. Make sure you also don't interfere with the Space key's ability to scroll the page/view when not focusing on a form control.

Video

Confirm the presence of captions

Help people who can't hear the audio content of a video by using the <track> component to insert captions into a video.

// Bad 👎
const BadCaptionExample = () => <video src="video.mp4" controls></video>;
 
// Good 👍
const GoodCaptionExample = () => (
  <video src="video.mp4" controls>
    <track kind="captions" src="captions.vtt" label="English" default />
  </video>
);

Remove seizure triggers

Use the disablePictureInPicture prop to remove any seizure triggers.

// Bad 👎
const BadSeizureTriggerExample = () => <video src="video.mp4" controls></video>;
 
// Good 👍
const GoodSeizureTriggerExample = () => (
  <video src="video.mp4" controls disablePictureInPicture></video>
);

Audio

Confirm that transcripts are available

Help people who can't hear the audio content of a video by providing transcripts.

// Bad 👎
const BadTranscriptExample = () => <audio src="audio.mp3" controls></audio>;
 
// Good 👍
const GoodTranscriptExample = () => (
  <audio src="audio.mp3" controls>
    <track kind="captions" src="transcript.vtt" label="English" default />
  </audio>
);

Appearance

How your website app content looks in any given situation.

Check your content in specialized browsing modes

Activate modes such as High Contrast and make sure your icons, borders, links, form fields, and other content are still present and legible.

Ensure that text can be resized up to 200%

By ensuring that the website supports resizable text up to 400%, you allow users with visual impairments or those who prefer larger text sizes to comfortably read and interact with your content without loss of content or functionality.

const App = () => (
  <div className="App">
    <h1 style={{ fontSize: '2rem' }}>Resizable Text</h1>
    <p style={{ fontSize: '1.5rem' }}>
      This text can be resized up to 400% without loss of content or
      functionality.
    </p>
  </div>
);

Use relative font sizes and responsive design techniques:

To support resizable text, it's recommended to use relative font sizes instead of fixed pixel sizes. This allows the text to scale proportionally when users adjust their browser's font size settings. Additionally, utilizing responsive design techniques ensures that the layout and content adapt effectively to different screen sizes.

import './App.css';
 
const App = () => (
  <div className="App">
    <h1 className="heading">Resizable Text</h1>
    <p className="content">
      This text can be resized up to 400% without loss of content or
      functionality.
    </p>
  </div>
);
App.css
.heading {
  font-size: 2rem;
}
 
.content {
  font-size: 1.5rem;
}
 
@media (max-width: 768px) {
  .heading {
    font-size: 1.5rem;
  }
 
  .content {
    font-size: 1rem;
  }
}

Setting fixed dimensions that prevent text from resizing:

Setting text sizes in pixels (e.g., font-size: 16px;) restricts users from resizing the text using browser settings. Instead, it's recommended to use relative units such as percentages or ems, which allow users to scale the text according to their needs.

import './App.css';
 
const App = () => (
  <div className="App">
    <h1 className="heading">Resizable Text</h1>
    <p className="content">
      This text can be resized up to 400% without loss of content or
      functionality.
    </p>
  </div>
);
App.css
.heading {
  font-size: 2rem;
}
 
.content {
  font-size: 1.5rem;
}

Test the website's text resizing functionality at various magnification levels:

  1. Copy chrome://settings/fonts to the address bar and then return on your keyboard.
  2. Increase the font size to 32px (default is 16px).
  3. Ensure your web app works as expected.

Make sure color isn't the only way information is conveyed

Ensure that information is not conveyed solely through color, as some users may have difficulty perceiving color differences. Example:

// Bad 👎
const BadA11yComponent = () => <span style={{ color: 'red' }}>✘</span>;
 
// Good 👍
const GoodA11yComponent = () => (
  <span style={{ color: 'red' }} aria-label="Rejected">

  </span>
);

Make sure instructions are not visual or audio-only

// Bad 👎
const BadInstructionsExample = () => (
  <div>
    <p>Click on the red button to submit the form</p>
    <button style={{ background: 'red' }}>Submit</button>
  </div>
);
 
// Good 👍
const GoodInstructionsExample = () => (
  <div>
    <p>Press the Enter key or use the Submit button to submit the form</p>
    <button style={{ background: 'red' }}>Submit</button>
  </div>
);

Use a simple, straightforward, and consistent layout

// Bad 👎
const BadLayoutExample = () => (
  <div>
    <div style={{ float: 'left' }}>Content 1</div>
    <div style={{ float: 'right' }}>Content 2</div>
    <div style={{ clear: 'both' }}></div>
  </div>
);
 
// Good 👍
const GoodLayoutExample = () => (
  <div>
    <div style={{ display: 'flex', justifyContent: 'space-between' }}>
      <div>Content 1</div>
      <div>Content 2</div>
    </div>
  </div>
);

Animation

Content that moves, either on its own, or when triggered by a person activating a control.

Ensure animations are subtle and do not flash too much

To avoid distractions and potential seizures, limit flashing to less than 3x per second.

Provide a mechanism to pause background video

// Bad 👎
const BadBackgroundVideoExample = () => (
  <div>
    <video src="video.mp4" autoPlay loop muted></video>
  </div>
);
 
// Good 👍
const GoodBackgroundVideoExample = () => (
  <div>
    <video src="video.mp4" autoPlay loop muted controls></video>
  </div>
);

Make sure all animation obeys the prefers-reduced-motion media query

⚠️
TBD

Color contrast

Color contrast is how legible colors are when placed next to, and on top of each other.

Check the contrast for text and icons

// Bad 👎
const BadContrastExample = () => (
  <div>
    <p style={{ color: '#ff0000', backgroundColor: '#00ff00' }}>Hello World</p>
  </div>
);
 
// Good 👍
const GoodContrastExample = () => (
  <div>
    <p style={{ color: '#000000', backgroundColor: '#ffffff' }}>Hello World</p>
  </div>
);

Check the contrast of borders for input elements (text input, radio buttons, checkboxes, etc.)

// Bad 👎
const BadBorderContrastExample = () => (
  <div>
    <input type="text" style={{ borderColor: '#ff0000' }} />
  </div>
);
 
// Good 👍
const GoodBorderContrastExample = () => (
  <div>
    <input type="text" style={{ borderColor: '#000000' }} />
  </div>
);

Check text that overlaps images or video

Check custom ::selection colors

// Bad 👎
const BadSelectionColorExample = () => (
  <div>
    <style>
      {`
        ::selection {
          color: #ff0000;
          background-color: #00ff00;
        }
      `}
    </style>
    <p>Select this text</p>
  </div>
);
 
// Good 👍
const GoodSelectionColorExample = () => (
  <div>
    <style>
      {`
        ::selection {
          color: #000000;
          background-color: #ffffff;
        }
      `}
    </style>
    <p>Select this text</p>
  </div>
);

Mobile and touch

Things to check mobile experiences for.

Check that the site can be rotated to any orientation

The web site should allow for portrait and landscape mode.

Remove horizontal scrolling

// Bad 👎
const BadHorizontalScrollExample = () => (
  <div style={{ width: '1000px', overflowX: 'scroll' }}>
    <p>Content</p>
  </div>
);
 
// Good 👍
const GoodHorizontalScrollExample = () => (
  <div style={{ width: '100%', overflowX: 'hidden' }}>
    <p>Content</p>
  </div>
);

Ensure that button and link icons can be activated with ease

// Bad 👎
const BadIconActivationExample = () => (
  <div>
    <button>
      <span role="img" aria-label="Save">
        💾
      </span>
      Save
    </button>
  </div>
);
 
// Good 👍
const GoodIconActivationExample = () => (
  <div>
    <button>
      <span role="img" aria-hidden="true">
        💾
      </span>
      Save
    </button>
  </div>
);

Ensure sufficient space between interactive items in order to provide a scroll area

This helps people with motor control issues such as hand tremors.

// Bad 👎
const BadScrollAreaExample = () => (
  <div style={{ height: '200px', overflowY: 'scroll' }}>
    <button>Button 1</button>
    <button>Button 2</button>
    <button>Button 3</button>
    <button>Button 4</button>
    <button>Button 5</button>
  </div>
);
 
// Good 👍
const GoodScrollAreaExample = () => (
  <div style={{ height: '200px', overflowY: 'scroll' }}>
    <button style={{ marginBottom: '20px' }}>Button 1</button>
    <button style={{ marginBottom: '20px' }}>Button 2</button>
    <button style={{ marginBottom: '20px' }}>Button 3</button>
    <button style={{ marginBottom: '20px' }}>Button 4</button>
    <button>Button 5</button>
  </div>
);

Keep up to date with any latest changes or announcements by subscribing to the newsletter below.