A11Y Button
Improve the accessibility of an existing button or anchor link element.
Table of contents
Install
npm i @wanjapflueger/a11y-button
Usage
-
Require/Import module
import { Button } from "@wanjapflueger/a11y-button"; // or var Button = require("@wanjapflueger/a11y-button");
-
Create a button
/** @type {HTMLButtonElement | HTMLAnchorElement} Any button or anchorlink element */ let button; // Create a new button button = document.createElement('button'); // or use an existing button button = getElementById('foo');
-
Add stuff to button
// add text button.innerHTML = `I will not be read by a screen reader`; // or elements button.append = document.createElement('span');
-
Make the button accessible
const config = { el: button, title: 'Button label for screen reader', busyOnClick: true, lang: 'en', }; // with require Button.Button(config); // or with import Button(config);
In short:
import { Button } from "@wanjapflueger/a11y-button";
const button = document.createElement('button');
button.innerHTML = `I will not be read by a screen reader`;
Button({
el: button,
title: 'Button label for screen reader',
busyOnClick: true,
lang: 'en',
});
Options
See ButtonInterface
in src/index.ts.
Property | Required | Description | Example |
---|---|---|---|
el |
|
A button or an anchor link element. |
document.createElement('button') , document.createElement('a') , document.getElementById('my-button-id')
|
title |
|
Text for screen reader | Lorem Ipsum |
busyOnClick |
|
If true add attribute [aria-busy="true"] to element on click. The [aria-busy="true"] will always be added to element inside HTMLFormElement on form submit (see Busy state. |
true or false
|
lang |
|
ISO 639-1 Language Code. By default the button will have the same language as the document. Helps screen readers in multi-lang environments (see Multilanguage environments) |
en , de
|
Motivation
Busy state
Imagine buying a product at an online shop. You add your shipping address and payment method and then hit the „Place Order” button.
<button>Place Order</button>
The speed at which the page responds can be affected by many factors.
For example, if the user has a poor internet connection, it may take a while before visual feedback is displayed. He may wonder why nothing is happening, even though something is happening in the background.
Solution approach for busy state
By adding an attribute [aria-busy]
, once the user has clicked on the button, we can add visual feedback at that very moment.
For example we can add a CSS animation like this:
button[aria-busy='true'] {
animation: 1.5s shine linear infinite;
background: linear-gradient(110deg, rgba(#fff, 0) 50%, rgba(#fff, 0.2) 70%, rgba(#fff, 0) 90%);
background-size: 200% 100%;
}
@keyframes shine {
to {
background-position-x: -200%;
}
}
Multilanguage environments
Screen readers misinterprete the language of button elements in some cases.
In this example we create an English document with the language attribute en
. The system language of the users device is set to German.
<html lang="en">
...
<button id="buttonA" aria-label="I am a text">Button A</button>
<button id="buttonB" aria-label="I am a text" lang="en">Button B</button>
<button id="buttonC" aria-label="Ich bin ein Text" lang="de">Button C</button>
...
</html>
Screen readers prefer the system language over the document language for button elements. The given example code produces these results:
Button id | Expected language | Associated lang attribute |
Language read by screen reader | Correct language |
---|---|---|---|---|
buttonA |
|
<html> |
|
|
buttonB |
|
<button> |
|
|
buttonC |
|
<button> |
|
|
Solution approach for multilingual environments
Given the example of an English document with the language attribute en
, we can see that screen readers read the correct language of button elements if the language attribute lang
is being used in combination with aria-labelledby
.
<html lang="en">
...
<button id="buttonA" aria-labelledby="labelForButtonA" lang="en">
Button A
<span id="labelForButtonA" class="visuallyhidden">I am a text</span>
</button>
<button id="buttonB" aria-labelledby="labelForButtonB" lang="de">
Button B
<span id="labelForButtonB" class="visuallyhidden">Ich bin ein Text</span>
</button>
<button id="buttonB" aria-labelledby="labelForButtonC" lang="fr">
Button C
<span id="labelForButtonC" class="visuallyhidden">Je suis un texte</span>
</button>
...
</html>
The screen reader ignores system and document language. The given example code produces these results:
Button id | Expected language | Associated lang attribute |
Language read by screen reader | Correct language |
---|---|---|---|---|
buttonA |
|
<button> |
|
|
buttonB |
|
<button> |
|
|
buttonC |
|
<button> |
|
|
Visually hidden elements
You may add this code to your project to hide elements visually. Visually hidden elements are still accessible to screen readers.
/// Hide visually (still accessible by screen readers)
/// @link https://a11yproject.com/posts/how-to-hide-content/
@mixin hide-visually() {
position: absolute;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px, 1px, 1px, 1px);
white-space: nowrap;
}
.visuallyhidden {
@include hide-visually();
}
Notes
Keep in mind that using aria-label
or aria-labelledby
means, that screen readers cannot read any text on a button, that is not a label in this context.
<button aria-labelledby="buttonLabel">
Apple
<span id="buttonLabel">Banana</span>
</button>
As shown in the code example above, screen readers will always read Banana
but never Apple
.
Accessibility Compliance Report
Browser | Platform | Screen reader | Passed |
---|---|---|---|
Chrome 90.0.4430.212 | MacOS 10.15.7 | VoiceOver | |
Chrome 90.0.4430.210 | Android 10 | Talkback |
-
This information refers only to the technical aspects of the component, not to the design or the editorial handling of any content.
↩