Scaling Accessibility

in the

Age of Components

Ayesha Mazumdar

Senior UX Engineer @ Optimizely

聽 聽 聽@AyeshaKMaz

Hi, I'm Ayesha 馃憢

Senior UX Engineer @ Optimizely

馃惗

Dog-mom to Pepper

馃幁

Proud Musical Theater Geek

Design Systems @ Salesforce

UX Engineering @ 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

Who benefits?

A11Y is for Everyone

Source: Margaret P.

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聽

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

  <label for={ inputId }>
      { label }
  </label>
  <input id={ id } type={ type }/>

馃З Inputs

A Testing Trick!

Screen Readers

Just a placeholder

Label

馃З 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="menu"

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:

So, what's the big picture here?

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

ProductWorld: Scaling Accessibility in the Age of Components

By Ayesha

ProductWorld: Scaling Accessibility in the Age of Components

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.

  • 30

More from Ayesha