Ayesha Mazumdar
Senior UX Engineer @ Optimizely
@AyeshaKMaz
Accessibility
📋 Today's Agenda
Just a placeholder
Label
Create...
Button Icon
Select Dropdown
Input
Visuals
Keyboard Navigation
Screen Readers
Component
Accessibility Win
ac·ces·si·bil·i·ty
alias: A11Y
Ensuring everyone,
no matter their ability,
can "perceive, understand, navigate, contribute to, and interact with" an application
noun
Source: Margaret P.
Every change made to a component permeates throughout the entire ecosystem to all consumers
🎉
The Good
Every change made to a component permeates throughout the entire ecosystem to all consumers
😳
With great power comes great responsibility...
The Bad
With great power comes great responsibility...
Key Problem Areas:
button {
outline: none;
}
Focus + Hover States
Focus + Hover States
Neutral
Hover
Focus
Focus + Hover
.oui-button-icon:focus {
box-shadow: 0px 0px 3px $brand-blue-dark;
}
Contrast
<ButtonIcon iconName="ellipsis"/>
<ButtonIcon iconName="ellipsis" iconFill="#abcabc"/>
Contrast
import {
amberBase,
aquaBase,
brandBlueDark,
greenBase,
orangeBase,
pinkBase,
redBase,
magentaBase,
greyBase,
} from '../../tokens/forimport/index.es';
// map fillColorName prop values to OUI color tokens
export const FILL_COLOR_MAP = {
aqua: aquaBase,
amber: amberBase,
default: brandBlueDark,
green: greenBase,
orange: orangeBase,
pink: pinkBase,
red: redBase,
magenta: magentaBase,
grey: greyBase,
};
export default {
FILL_COLOR_MAP,
};
ButtonIcon.propTypes = {
/** Custom color for icon inside button **/
iconFillName: PropTypes.oneOf(Object.keys(FILL_COLOR_MAP)),
{...}
}
Titles
return (
<button
{...}
title={ props.title }>
<Icon name={ props.iconName } size={ props.size } />
</button>
);
ButtonIcon.propTypes = {
// Name of the icon to use
iconName: PropTypes.string.isRequired,
{...}
/** Title of the button used for hover and screen readers
* Describe the button's action, not the icon itself
*/
title: PropTypes.string.isRequired,
};
Even better - aria-label
return (
<button
{...}
aria-label={ props.title }
title={ props.title }>
<Icon name={ props.iconName } size={ props.size } />
</button>
);
ButtonIcon.propTypes = {
// Name of the icon to use
iconName: PropTypes.string.isRequired,
{...}
/** Title of the button used for hover and screen readers
* Describe the button's action, not the icon itself
*/
title: PropTypes.string.isRequired,
};
Given unpredictable support for the title attribute, adding aria-label can help provide consistent support for screen readers. There is no single perfect solution though - Sara Soueidan has a great article on the subject
Screen Readers
<li>
<Label>API Call</Label>
<p>
To track this event, add the following code to your page.
<Link href={url}>
Learn more
</Link>
</p>
<Attention>
{ this.renderApiCallText() }
</Attention>
</li>
Label is used for its style, not its semantic meaning
Screen Readers
But, where's the input?
<Input type="text"/>
Screen Readers
<Input type="text"/>
if (props.label) {
return (
<Label>{...}</Label>
<input>{...}</input>
);
}
/components/input/index.js
Screen Readers
<Input
id="input-01"
label="Experiment Name"
type="text"
/>
<div>
<Label inputId={ id }>
{ label }
</Label>
{ this.renderInput() }
</div>
/components/input/index.js
*Required Prop
*
*
Screen Readers
<label for={ inputId }>
{ label }
</label>
<input id={ id } type={ type }/>
A Testing Trick!
Screen Readers
Just a placeholder
Label
export { default as Input } from './components/Input';
export { default as Label } from './components/Label';
Plus...
becomes...
export { default as Input } from './components/Input';
Screen Readers
Create...
aria-expanded="true|false"
role="combobox"
role="menuitem | placeholder"
tabindex="-1"
Keyboard Navigation
aria-haspopup="true"
Keyboard Navigation
let currentFocusedIndex = this.state.currentFocusedIndex;
if (keyCode === ESC) {
toggleOpen()
} else if (keyCode === ENTER || keyCode === SPACE) {
handleItemSelect(currentFocusedIndex)
} else if(keyCode === DOWN) {
currentFocusedIndex ++;
} else if(keyCode === UP) {
currentFocusedIndex --;
}
// reset state
{...}
Example Psuedocode:
Just a placeholder
Label
Create...
ButtonIcon.propTypes = {
iconFillName: PropTypes.oneOf(Object.keys(FILL_COLOR_MAP)),
title: PropTypes.string.isRequired,
{...}
}
Effort
Impact
Fable and Access Works
Usability testing with people with disabilities
Covers AA and AAA criterion
Other Design Systems and Component Libraries
Don't reinvent the wheel!
Come say hi 👋 during the breaks
Reach out on Twitter: @AyeshaKMaz
Connect on LinkedIn: /ayeshakmaz