Skip to main content

Accessibility guidelines


This page provides some general accessibility guidelines. For component specific recommendations, be sure to check out their individual documentation pages.

EUI provides a strong start to building accessibility into your apps. The components provided strive to meet WCAG 2.1 guidelines on semantics, keyboard functionality, color contrast, and so on. How you stitch together these components in the overall page structure also plays a large role in meeting accessibility goals. Headings, landmarks, page titles, focus management, and accessible names all work together to create accessible apps.

Building accessibility into your app is as important as code quality, visual design, and performance, and it’s also important that you test as you go. You can approach accessibility testing from three dimensions: automated, manual, and empathetic thinking. Use automated tests to quickly cover as much ground as possible, manual tests to address more complicated scenarios, and empathy to fill in the gaps.

For a technical intro to accessibility and how EUI tackles it

Headings and landmarks

You can aid navigation and make pages more accessible for screen reader users by using solid headings and landmarks. Headings are the simplest way for screen readers to navigate pages. A good heading hierarchy:

  • Uses only one <h1> on each page
  • Doesn't skip levels <h1> → <h6>
  • Doesn't duplicate content

Heading examples

tsx code block:
<EuiText> <h1>Discover your data</h1> <p>Some content</p> <h2>Drill into site metrics</h2> <h3>An important site metric</h3> <h3>Another important site metric</h3> </EuiText>

Do: Descend through headings as you work your way through the document.

tsx code block:
<EuiText> <EuiScreenReaderOnly> <h1>Discover your data</h1> </EuiScreenReaderOnly> <p>Some content</p> <h2>Discover your data</h2> <!-- Missing h3 header --> <h4 className="myLargeTitle"> An important site metric </h4> </EuiText>

Don't: This heading hierarchy is confusing. Also EuiText is not a good solution when you need to change heading presentation.

tsx code block:
<EuiTitle size="s"><h1>Discover your data</h1></EuiTitle> <EuiScreenReaderOnly><h2>Drill into site metrics</h2></EuiScreenReaderOnly> <EuiTitle size="l"><h3>An important site metric</h3></EuiTitle> <EuiTitle size="m"><h3>Another important site metric</h3></EuiTitle>

Do: This is a good heading hierarchy. Though visible headings are certainly better, sometimes that is difficult to accommodate so hidden headings can give additional context.

tip

EuiTitle gives you a way to separate your presentation from your semantic markup.

Landmarks are another way for screen readers to navigate pages. A benefit of landmarks is that they offer more context on the type of content to expect than a heading. This is useful for tech that offers reader modes (e.g., Firefox, Safari, and apps like Pocket) and new form factors (e.g., smartwatches). Many landmarks are mapped to HTML elements, such as <main>, <aside>, <article>; others are exposed through the role attribute.

You can implement named landmarks with aria-label or aria-labelledby. However, having a heading inside of the landmark (even if it is visually hidden) and referenced by aria-labelledby is preferred.

Landmarks example

html code block:
<body> <header className="appHeader"> <!-- content --> </header> <main><!-- content --></main> <footer className="appFooter"> <!-- content --> </footer> </body>

Do: Use HTML5 elements which convey semantic meaning about their purpose. Notice that all of the content is inside of semantic elements.

html code block:
<body> <div className="appHeader"></div> <discover-app></discover-app> <ul className="appFooter"></ul> </body>

Don't: Classes provide no semantic meaning and not all elements provide semantic meaning either.

Headings and named landmarks example

html code block:
<header aria-labelledby="pageHeading"> <h1 id="pageHeading">Discover your data</h1> <form role="search" aria-label="Site search"> <!-- input + label go in here --> </form> <header> <main aria-labelledby="contentHeading"> <h2 id="contentHeading">Drill into site metrics</h2> <form role="search" aria-label="Search your data"> <!-- input + label go in here --> </form> </main>

Do: Use landmarks and headings together to build complex pages.

Further reading


Page titles

Each page requires a unique, informative title that accurately reflects what the page does. The best page titles put the unique content first. Effectively, they're reverse-order breadcrumbs.

Use this format: {Unique page title} - {Site title}

Discover - Kibana

Rollup Jobs - Management - Kibana

Do: These are good example of page titles.

Watchers

Don't: Though unique, this does not provide enough context.

Do: Watchers - Management - Kibana

Elastic Kibana - Spaces

Don't: Although it provides all the context, putting the most important bit at the end is hard to find.

Do: Spaces - Management - Kibana

This is the Reporting page of the Management section of Kibana.

Don't: Although this provides all the context and in a good order, a title is not the place for any extra words.

Do: Reporting - Management - Kibana

Further reading


Focus management

Where is the focus state right now?

Focus states are an important part of design because they let keyboard users know where focus is currently at. All browsers ship with focus states for interactive elements, and most of the time you shouldn’t need to alter these. EUI goes further to customize focus states to match the Elastic brand and provide better visual states, including color contrast.

Where is the focus state going?

Given that a keyboard user primarily navigates pages in one direction (either forward or backward), it’s important to have an intuitive focus order. Focus order should follow the flow of the page to make it easy to follow. If you’ve made a normally non-interactive element like a <div> interactive via JavaScript, you can enable a tab stop using tabIndex=0. If you want something that is only focusable programmatically, you can use tabIndex=-1.

danger

Using tabIndex values greater than 0 is problematic and should be avoided.

How do I get back to where I was?

Navigating complex sites sometimes means your focus state will jump around (e.g., skip links, modals, typeahead, and so on). If you remove an element that currently has focus without setting focus anywhere else, users start over at the beginning of the page. Unless there’s a strong reason to do otherwise, focus state should always return to where it was previously if the currently focused element disappears. For example, closing a modal might mean your focus is on a close button; when the modal closes, you should return focus to the button that opened the modal.

Further reading


Disabled elements

Browsers remove disabled elements from the tab order. This means keyboard users cannot tab to those elements or activate nested focus behaviors.

If you disable a button or form element, you need to provide clear instructions to users how to correct errors or remove the disabled state.

When you add the HTML disabled attribute to an element, you must remove tooltips or other focus interactions. You must also remove focus interactions if you pass the isDisabled prop to components like EuiAccordion.

Further reading


Naming

An accessible name is the name of an HTML element as it’s exposed to assistive technology. An accessible name can then be read by a screen reader or can be targeted for an action.

Most elements

For most content, the accessible name comes from the element’s inner text, such as: <a href="https://elastic.co">Elastic.co</a>. A screen reader can now read it out something like “Elastic.co, link” or, using voice commands, it can be controlled with “Click Elastic.co link”.

Images and other elements

Some content might require special attributes to give an element an accessible name. For images, you can use alt attributes, such as:

text code block:
<img src="image1.jpg" alt="An apple lays on a table">

Buttons without inner text

For buttons without descriptive text content, you can rely on ARIA to bring meaning back:

text code block:
<button aria-label="Close modal">X</button>

Forms and more complex patterns

Some HTML elements have associated elements that provide accessible names. Form elements are the most ubiquitous example: a checkbox doesn’t have a name by itself, but when it is associated with a label, assistive technologies can make the connection:

text code block:
<input type="checkbox" id="subscribe"> <label for="subscribe">Subscribe to Elastic news</label>

Of note: Repeated calls to action

Having only an accessible name, however, doesn’t always lead to the best UX. Take a list of available fields that someone might want to add to their filter (say, on the discovery page of a popular open source project):

text code block:
<h3>Available fields</h3> <ul> <li> @timestamp <button>add</button> </li> <li> _id <button>add</button> </li> <li> _index <button>add</button> </li> </ul>

Here, the 3 buttons have the same accessible name. There are a few different patterns you can use to differentiate between repeated items. For example, each button below shows a possible pattern you can use (in order of recommended best practice):

text code block:
<h3 id="available">Available fields</h3> <ul aria-labelledby="available"> <li> _id <button aria-label="add _id field to your current filter"> add </button> </li> <!-- The next two options are hardest to make work with Elastic’s i18n framework --> <li> @timestamp <button> add <EuiScreenReaderOnly> @timestamp field to your current filter </EuiScreenReaderOnly> </button> </li> <li> <!-- This isn’t recommended but will work in a pinch --> <span id="filed3">_index</span> <button id="button3" aria-labelledby="button3 field3">add</button> </li> </ul>
Give lists an accessible label to improve their discoverability!
text code block:
<!-- Can be any heading level or even a paragraph --> <h1 id="a1b2c3">My favorite fruit</h1> <ul aria-labelledby="a1b2c3"><!-- ... --></ul> <!-- You can still provide an accessible title even if there's no visual label --> <ul aria-label="My favorite vegetables">...</ul>

Further reading


Testing considerations

There are a lot of aspects to accessibility, and covering all the bases can be a lot to keep in mind. By relying on standards, you can minimize the amount of special casing you have to do in code, but you should still be cognizant of the many modalities in which users might use your products.

Low-vision

While low-vision users may use many assistive technologies in tandem, this section focuses on zooming. Two ways that users can zoom the page are by increasing the base font-size with browser tools or by using a 3rd-party magnifier (sometimes, a physical magnifier) to better see the screen.

WCAG 1.4.4 defines 200% browser zoom should continue to work with no further action from the user as a Level AA criteria.

ZoomText is the most popular 3rd-party magnifier that gives users a window they can drag over content to magnify and read it out loud. Testing for the best experiences here is exceptionally difficult because you must make visual judgement calls. Specifically, related pieces of information should be close enough together for a low-vision user to efficiently interact with the UI.

Low-vision/blind (screen readers)

Blind and low-vision users often rely on tools, such as screen readers and braille readers, to navigate the web. Screen and braille readers read the page from top to bottom. Building a page with a good structure, will make it quick and easy to navigate. Braille readers are a textual representation of what a screen reader would say so we can focus on screen reader compatibility.

The 3 most common, desktop, screen readers, and their most common browser pairings are:

  • JAWS with Chrome
  • NVDA with Firefox
  • VoiceOver with Safari

Mobile is a little simpler:

Learning resources

Practical examples

For many things, there’s no need to reinvent the wheel. If your component is featured in one of these two sources, feel free to borrow heavily!

Tooling

Spec docs