Skip to content
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

Merged
merged 9 commits into from
Aug 14, 2017
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions STYLEGUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ recommended for the development of all Kibana plugins.
- [HTML](style_guides/html_style_guide.md)
- [API](style_guides/api_style_guide.md)
- [Architecture](style_guides/architecture_style_guide.md)
- [Accessibility](style_guides/acecssibility_guide.md)

## Filenames

Expand Down
230 changes: 230 additions & 0 deletions style_guides/accessibility_guide.md
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>
Copy link
Contributor

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?

Copy link
Contributor

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?

Copy link
Contributor

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).

```

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>
Copy link
Contributor

Choose a reason for hiding this comment

The 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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use click listeners just fine on these elements.

I think we should advise using a click handler on button only, and an href with the anchor tag. If we use an anchor with a click handler, then we'll need to apply kbn-accessible-click or KuiKeyboardAccessible, because browsers won't trigger the click handler when you hit ENTER.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They trigger a click as long as the a has an href. So I guess this issue will collapse with the one above, about mentioning usage of href. But I will try to make this more clear.

Copy link
Contributor

Choose a reason for hiding this comment

The 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?

Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.

Copy link
Contributor

@cjcenizal cjcenizal Aug 9, 2017

Choose a reason for hiding this comment

The 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 <button> element styled to look like a link. The href is supposed to provide a hint of where you're going, and the anchor tag should allow you to open the href in a new tab, but none of that functionality is being used.


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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we mention using role="application" here, and some reasons why?


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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add role=main, section tags and revisit role=application here as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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 role=main, section, etc. we would also need to start explaining aria-label/aria-labelledby and aria-describedby, etc.?

Which way do you and @cjcenizal think we should go with this document for now?

Copy link
Contributor

@cjcenizal cjcenizal Aug 9, 2017

Choose a reason for hiding this comment

The 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 aria-label/aria-labelledby and aria-describedby. We can use #11837 (comment) for the latter.

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: therefor -> therefore

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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)
50 changes: 0 additions & 50 deletions style_guides/html_style_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,53 +82,3 @@ similar elements which appear sequentially in the markup.
```html
<div><span>hi</span></div>
```

## Accessibility

### Don't use the `title` attribute

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, use
a tooltip component instead.

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/

### 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>
```

### Prefer `button` and `a` when making interactive elements keyboard-accessible

If something is meant to be clickable, favor using a `button` or `a` tag before over the `kui-accessible-click` directive or `KuiKeyboardAccessible` component. These tools are useful as they augment non-clickable elements with `onkeypress` and `onkeyup` handlers, allowing non-clickable elements to become keyboard accessible. However, `button` and `a` tags provide this functionality natively.

### Use `tabindex` to make elements tabbable

When added to the tab order, elements become focusable via non-sticky-mode keyboard navigation.
To add an element to the tab order, you must add an `id` attribute as well as a `tabindex` attribute.

You should only use `tabindex="0"` to add an element to the tab flow or `tabindex="-1"` to remove an
otherwise focusable element from the focus flow (use with care).
You should never use a value greater than 0, since tabindex is a global counter for the whole
webpage and not scoped to parent elements, so you would need to manage a globally meaningful order
across all elements in the whole source code.