-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initial version of a11y guide #13390
Changes from 7 commits
e893d6d
397d6d3
9dc78d8
0a4accb
11f13ec
75b3545
ff3cae4
9b98b37
ec43752
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
# Accessibility (A11Y) Guide | ||
|
||
This document provides some technical guidelines how to prevent several common | ||
accessibility issues. | ||
|
||
## Naming elements | ||
|
||
### `aria-label` and `aria-labelledby` | ||
|
||
Every element on a page will have a name, that is read out to an assistive technology | ||
like a screen reader. This will for most elements be the content of the element. | ||
For form elements it will be the content of the assosciated `<label>` (see below). | ||
|
||
You can overwrite that name, that is read out, by specifying a new name via the | ||
`aria-label` attribute. This must e.g. be done, if the element itself has no | ||
visual text representation (e.g. an icon button): | ||
|
||
```html | ||
<button aria-label="Add filter"><i class="fa fa-plus"></i></button> | ||
``` | ||
|
||
If the actual name for that element is already present in another element, | ||
you can use `aria-labelledby` to reference the id of that element: | ||
|
||
```html | ||
<div id="datepicker">Date Picker</div> | ||
<button aria-labelledby="datepicker"><i class="fa fa-calendar"></i></button> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here. |
||
``` | ||
|
||
### Label every form element | ||
|
||
You should add a label for every form element: | ||
|
||
```html | ||
<label for="interval">Interval</label> | ||
<select id="interval"><!-- ... --></select> | ||
``` | ||
|
||
If one label references multiple form elements, you can use the reverse logic | ||
and add `aria-labelledby` to all form elements: | ||
|
||
```html | ||
<label id="fromLabel">From</label> | ||
<input type="number" aria-labelledby="fromLabel"> | ||
<input type="number" aria-labelledby="fromLabel"> | ||
<input type="number" aria-labelledby="fromLabel"> | ||
``` | ||
|
||
You should always prefer the `<label for>` solution, since it also adds benefit | ||
for every user, by making the label clickable, to directly jump to the form | ||
element (or in case of checkboxes and radio buttons directly check them). | ||
|
||
### Don't use the `title` attribute | ||
|
||
**TL;DR** *Don't use the `title` attribute, use tooltips, `aria-label`, etc. instead.* | ||
|
||
The `title` has no clear role within the accessibility standards. | ||
[See the HTML5 spec](http://w3c.github.io/html/dom.html#the-title-attribute) for more information. | ||
|
||
To provide supplementary, descriptive information about a form control, button, link, or other element, | ||
that should also be visible to non vision impaired users, use a tooltip component instead. | ||
|
||
If you need a label only for screen readers use `aria-label`. | ||
|
||
**Additional reading:** | ||
|
||
* https://www.paciellogroup.com/blog/2010/11/using-the-html-title-attribute/ | ||
* https://www.paciellogroup.com/blog/2012/01/html5-accessibility-chops-title-attribute-use-and-abuse/ | ||
* https://www.deque.com/blog/text-links-practices-screen-readers/ | ||
|
||
## Interactive elements | ||
|
||
### Use `<button>` and `<a href>` | ||
|
||
**TL;DR** *Use `<button>` and `<a>` (with `href`) instead of click listeners on other elements | ||
and style it whatever way you need.* | ||
|
||
If you want to make an element clickable, use a `<button>` or `<a href>` element for it. | ||
Use a `<button>` whenever it causes an action on the current page, and an `<a href>` if it | ||
navigates to a different page. You can use click listeners just fine on these elements. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think we should advise using a click handler on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This information also duplicates https://github.com/elastic/kibana/blob/master/style_guides/html_style_guide.md#prefer-button-and-a-when-making-interactive-elements-keyboard-accessible, so we should probably remove it from the HTML style guide. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They trigger a click as long as the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh I see. I guess that raises the question, why would you want a click handler and an href? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't it non-semantic to use a link as a button? I think links should be used for navigating to different URLs, and buttons should be used for triggering event handlers. That way you know that you can open links in new windows or tabs, or just reference the URL it points to. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would totally agree with you in that. But I think there might be some cases, where the "navigation" is actually implemented in JS (like dynamically determine parts of the URL) in which this would be valid. That's why I left it in there. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree, but if I understand correctly, here are the scenarios we can have: <!-- Semantic -->
<a href="hardcoded/url">
<a href={dynamicUrl}>
<button onClick={doSomething}>
<button className="lookLikeALink" onClick={doSomething}>
<!-- Non-semantic -->
<a href onClick={doSomething}> The last example is what I'm advocating against because it just doesn't make sense... you should just use a |
||
|
||
An `<a>` element must have an `href` attribute, so that (a) it will be correctly perceived | ||
as a link by a screen reader and (b) that registered click listener will correctly | ||
trigger on pressing <kbd>Enter</kbd>. If you don't need it, make it `href="#"` | ||
and call `preventDefault()` on the click event. | ||
|
||
*Why not use other elements?* | ||
|
||
If you create e.g. a `<div>` with a click listener (like `<div ng-click="ctrl.doSomething()">...</div>`) | ||
you will create multiple accessibility issues: | ||
|
||
* The element is not *keyboard accessible*, meaning: | ||
* You cannot focus it by pressing <kbd>tab</kbd>. You can add this behavior by | ||
adding `tabindex="0"` to the element. | ||
* You cannot trigger the click by pressing <kbd>Enter</kbd> or <kbd>Space</kbd>. | ||
We have a `kbn-accessible-click` directive for AngularJS and a `KuiKeyboardAccessible` | ||
React component to add that behavior. | ||
* Even if you make it keyboard accessible, a user using a screen reader won't | ||
recognize, that the div is actually an interactive element, the screen reader | ||
will just announce something like: "Sort". You would need | ||
to add `role="button"` or `role="link"` to it, so that the screen reader would | ||
actually announce something like "Sort, button" and give the user the required | ||
information, that this element is interactive. | ||
|
||
You will need quite some work just to rebuild native logic of a button (and we haven't | ||
even touched disabled states, etc.). It is most of the time easier to just use | ||
a `button` or an `a` and style this, whatever way you want it to look (even if | ||
you don't want it to look like a button at all). | ||
|
||
### tabindex and id | ||
|
||
**TL;DR** *Only use `tabindex="0"` and `tabindex="-1"` and no values above 0. Always | ||
add an `id` to an element with `tabindex`.* | ||
|
||
If you want to make an element focusable, that doesn't offer this feature by default | ||
(i.e. isn't an form element or a link), you can add `tabindex` with a value >= 0 to it. | ||
|
||
*Why shouldn't you use values above 0?* | ||
|
||
Setting `tabindex="0"` will add the element to the tabbing order at the position | ||
it is in the DOM. Setting it to something above 0 will create an explicit order. | ||
Tabbing will always focus all elements with an tabindex > 0, starting from the smallest | ||
number. After all those elements has been tabbed, it will continue with all `tabindex="0"` | ||
or implicit tabable elements. These values are not scoped to a subtree of the | ||
DOM, but are global values. Maintaining a global order is nearly impossible | ||
and considered a [serious issue](https://dequeuniversity.com/rules/axe/1.1/tabindex) | ||
by automated accessibility testing frameworks. | ||
|
||
`tabindex="-1"` will remove an element from tab order, that would be focusable | ||
otherwise. You won't need this often. | ||
|
||
*Why should you add an `id`?* | ||
|
||
Due to some bugs in some common screen readers, you will always need to add an `id` | ||
to an element with `tabindex`, since they wouldn't pick up the `tabindex` correctly | ||
otherwise. | ||
|
||
### Tooltips | ||
|
||
**TL;DR** *Add `role="tooltip"` and `aria-describedby` to elements for accessible tooltips.* | ||
|
||
Elements which act as tooltips should have the `role="tooltip"` attribute and an ID to which the | ||
described element can point to with the `aria-describedby` attribute. For example: | ||
|
||
```html | ||
<div | ||
class="kuiTooltip" | ||
role="tooltip" | ||
id="visualizationsTooltip" | ||
> | ||
Visualizations help you make sense of your data. | ||
</div> | ||
|
||
<button aria-describedby="visualizationsTooltip"> | ||
Visualizations | ||
</button> | ||
``` | ||
|
||
### Don't create keyboard traps | ||
|
||
**TL;DR** *If you can't leave an element with <kbd>Tab</kbd> again, it needs a special interaction model.* | ||
|
||
If an interactive element consumes the <kbd>Tab</kbd> key (e.g. a code editor to | ||
create an actual tabular indentation) it will prevent a keyboard user to leave | ||
that element again. Also see [WCAG 2.1.2](https://www.w3.org/TR/WCAG20/#keyboard-operation-trapping). | ||
|
||
Those kind of elements, require a special interaction model. A [code editor](https://github.com/elastic/kibana/pull/13339) | ||
could require an <kbd>Enter</kbd> keypress before starting editing mode, and | ||
could leave that mode on <kbd>Escape</kbd> again. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we mention using |
||
|
||
Unfortunately there is no universal solution for this problem, so be aware when creating | ||
such elements, that would consume tabbing, to think about an accessible interaction | ||
model. | ||
|
||
*Hint:* If you create that kind of interactive elements `role="application"` might | ||
be a good `role` (also see below) for that element. It is meant for elements providing | ||
their own interaction schemes. | ||
|
||
## Roles | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we add There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure if we can and should make this a complete developer guideline or just pointing out most common pitfalls. I fear if we start documenting Which way do you and @cjcenizal think we should go with this document for now? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's a very ambiguous line between "complete guideline" and "common pitfalls" because it's so relative to the experience level of the developer. I think we might as well err on the side of comprehensiveness and describe those roles, as well as To elaborate on my line of reasoning: if we try to keep the guide limited to common pitfalls, then we'll run into this same question over and over, so it seems more maintainable to side-step the question entirely. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I see your point there, and will add an explanation tomorrow. |
||
|
||
Each DOM element has an implicit role in the accessibility tree (that assistive technologies | ||
use). The mapping of elements to default roles can be found in the | ||
[Accessibility API Mappings](https://www.w3.org/TR/html-aam-1.0/#html-element-role-mappings). | ||
You can overwrite this role via the `role` attribute on an element, and the | ||
assistive technology will now behave the same like any other element with that role | ||
(e.g. behave like it is a button when it has `role="button"`). | ||
|
||
### Landmark roles | ||
|
||
Some roles can be used to declare so called landmarks. These landmarks tag important | ||
parts of a web page. Screen readers offer a quick way to jump to these | ||
parts of the page (*landmark navigation*). | ||
|
||
#### role=main | ||
|
||
The `main` role (or equivalent using the `<main>` tag) declares the main part | ||
of a page. This can be used in the landmark navigation to quickly jump to the | ||
actual main area of the page (and skip all headers, navigations, etc.). | ||
|
||
#### `<section>` | ||
|
||
The `<section>` element, can be used to mark a region on the page, so that it | ||
appears in the landmark navigation. The section element therefor needs to have | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typo: therefor -> therefore There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just wanted to sound archaic - no kidding, will fix it. |
||
an *accessible name*, i.e. you should add an `aria-label`, that gives a short | ||
title to that section of the page. | ||
|
||
### role=search | ||
|
||
**TL;DR** *Place `role="search"` neither on the `<input>` nor the `<form>`, but | ||
some `div` in between.* | ||
|
||
Role search can be used to mark a region as used for searching. This can be used | ||
by assistive technologies to quickly find and navigate to this section. | ||
|
||
If you place it on the `input` you will overwrite the implicit `textbox` or `searchbox` | ||
role, and as such confuse the user, since it loses it meaning as in input element. | ||
If you place it on the `form` element you will also overwrite its role and | ||
remove it from a quick jump navigation to all forms. | ||
|
||
That's why it should be placed to an `div` (or any other container) between the | ||
`form` and the `input`. In most cases we already have a div there that you can | ||
easily put this role to. | ||
|
||
**Related Links:** | ||
|
||
* [Where to put your search role?](http://adrianroselli.com/2015/08/where-to-put-your-search-role.html) | ||
* Discussions about making `search` role inherit the `form` role: | ||
[wcag/113](https://github.com/w3c/wcag/issues/113), | ||
[html-aria/118](https://github.com/w3c/html-aria/issues/18), | ||
[aria/85](https://github.com/w3c/aria/issues/85) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we change
<i>
to<span>
, which is more semantic?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does font awesome not use
<i>
tags anymore?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Font awesome will work on any inline tag, and possibly any block-level tag (haven't tested).