Accessibility in the Age of Components

Ayesha Mazumdar

UX Engineer @ Optimizely

     @AyeshaKMaz

Scaling

^

Hi, I'm Ayesha 👋

UX Engineer @ Optimizely

🐶

Dog-mom to Pepper

🎭

Proud Musical Theater Geek

Design Systems @ Salesforce

UX Engineer @ Optimizely

 

  • Component Library
  • Design System
  • Prototyping
  • Frontend UI/UX

Accessibility

📋 Today's Agenda

Just a placeholder

Label

Create...

Button Icon

Select Dropdown

Input

Visuals

Keyboard Navigation

Screen Readers

Component

Accessibility Win

What is Accessibility?

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

Component Libraries

Every change made to a component permeates throughout the entire ecosystem to all consumers

🎉

The Good

Component Libraries

Every change made to a component permeates throughout the entire ecosystem to all consumers

😳

With great power comes great responsibility...

The Bad

... and endless opportunity to make the web a more accessible place!

With great power comes great responsibility...

 Visuals 

Is the interface visually legible and understandable for all users?

🧩 Button Icons

Key Problem Areas:

 

  • Focus + Hover States
  • Contrast
  • Titles

 Visuals 

🧩 Button Icons

 Visuals 

Text

button {
  outline: none;
}

Focus + Hover States

Focus + Hover States

Neutral

Hover

Focus

Focus + Hover

🧩 Button Icons

.oui-button-icon:focus {
  box-shadow: 0px 0px 3px $brand-blue-dark;
}

 Visuals 

Contrast

<ButtonIcon iconName="ellipsis"/>
<ButtonIcon iconName="ellipsis" iconFill="#abcabc"/>

🧩 Button Icons

 Visuals 

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)),
  {...}
}

🧩 Button Icons

 Visuals 

Titles

return (
    <button
      {...}
      title={ props.title }>
      <Icon name={ props.iconName } size={ props.size } />
    </button>
);

🧩 Button Icons

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,
};

 Visuals 

Even Better - aria-label

return (
    <button
      {...}
      aria-label={ props.title }
      title={ props.title }>
      <Icon name={ props.iconName } size={ props.size } />
    </button>
);

🧩 Button Icons

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,
};

 Visuals 

* Bonus slide added after React Boston

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

Can users easily interact with the content on the screen without seeing it?

🧩 Inputs

Screen Readers

🧩 Inputs

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

🧩 Inputs

<Input type="text"/>

Screen Readers

🧩 Inputs

<Input type="text"/>
if (props.label) {
  return (
    <Label>{...}</Label>
    <input>{...}</input>
  );
}

/components/input/index.js

Screen Readers

🧩 Inputs

<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

🧩 Inputs

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

Keyboard Navigation

Anything you can do with a mouse, I can do with the keyboard

🧩 Dropdowns

Create...

aria-expanded="true|false"

role="combobox"

role="menuitem | placeholder"

tabindex="-1"

Keyboard Navigation

aria-haspopup="true"

🧩 Dropdowns

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:

That was a lot to cover

Let us review

Start with your most used components

🥡 Take Away # 1

Just a placeholder

Label

Create...

Make it easier to do the right thing

🥡 Take Away # 2

ButtonIcon.propTypes = {
  iconFillName: PropTypes.oneOf(Object.keys(FILL_COLOR_MAP)),
  title: PropTypes.string.isRequired,
  {...}
}

Some things shouldn't be flexible

🥡 Take Away # 3

Inputs != 🧘‍♀️

Good things take time and effort

🥡 Take Away # 4

Effort

Impact

  • Fable and Access Works

    • Usability testing with people with disabilities

  • The A11Y Project

    • Covers AA and AAA criterion

  • Other Design Systems and Component Libraries

    • Don't reinvent the wheel

📸 Resources

🤔 Want to chat more?

Thank
You

Scaling Accessibility in the Age of Components - ReactBoston 2019

By Ayesha

Scaling Accessibility in the Age of Components - ReactBoston 2019

We often build component libraries to improve consistency, collaboration, and customization for a given product. But what if a component library could also scale accessibility across the entire organization? Building accessible components can help distribute responsibility across all of design and engineering, without needing everyone to be an expert on the nitty-gritty details. This presentation looks at specific component examples and use cases to help you and your team contribute to a better, more inclusive web.

  • 2,942