Developing Accessible
Pattern Libraries

Follow along

https://git.io/vxWhu

Who am I?

Kasper Isager

Product Owner, Accessibility
@ Siteimprove

Primary focus

Lead the development of our accessibility testing engine

Before that...

Software Developer, Core
@ Siteimprove

Primary focus

Lead the development of our internal pattern library

What we will look at

What we've developed

Good API design

Useful abstractions

What we've developed

Our goal

Provide a suite of fully accessible baseline components

Vue.js

Vue.js

Vue.js allows developers to write plain HTML and JavaScript

Good API design

Interacting with any API is like filling out a questionnaire

HTML: Low level

What kind of element would you like?


    
...

How would you like to identify it?


    
...

Does it have any state you want to identify?


    
...

At this point, our tab only conveys structure and styling; we need semantics as well

ARIA: Still low level

What is the thing actually?


    
  

What state is the tab currently in?


    
  

Low level APIs quickly become tedious to work with and...

...developers will forget to fill out a question or ten

Let's improve the API!

As library developers, we control the questions posed

Components: High-ish level

What kind of thing do you want?


    ...
  

Should the tab be selected?


    <tab selected>...</tab>
  

Behind the scenes, we turn this...


    <tab selected>...</tab>
  

...into this


      
    

By posing the "right" questions, we can cover a lot of ground on the accessibility front...

...without having to worry about developers forgetting to add those pesky ARIA attributes

Useful abstractions

Relations

Not connecting parts of a UI that relate to one another is a leading cause of stress accessibility issues

Implicit relations

In this case, there's an implicit relation between the tab list and its associated tabs


    
      <tab selected>Tab 1</tab>
      Tab 2
    
  

Behind the scenes, we may want to automatically set the aria-activedescendant attribute


    

It could look like the following in Vue.js using some utility mixins


    const tabs = relation({ explicit: false })

    const TabList = {
      mixins: [hasMany(tabs, "tabs")],
      computed: {
        activeDescendant() {
          return this.tabs.find(tab => tab.selected)
        }
      }
    }

    const Tab = {
      mixins: [memberOf(tabs)]
    }
  

Explicit relations

In this case, there's an explicit relation between the button and the content it collapses


    
      
    
  

Behind the scenes, we may want to automatically set the aria-controls attribute


    
    <div id="collapsible" class="collapse-content" hidden>...</div>
  

Again, it could look like the following in Vue.js using some utility mixins


    const collapse = relation({ explicit: true })

    const Collapse = {
      mixins: [hasOne(collapse)],
    }

    const Button = {
      mixins: [memberOf(collapse, "collapse")],
      computed: {
        controls() {
          return this.collapse.id
        }
      }
    }
  

Implementations of the mixins used can be found at https://github.com/kasperisager/vue-relation

Interactions

Another common pitfall: When to use <a> and when to use <button>?

Find the "right" question to ask!

What kind of interaction do you want?

If you don't want interaction, this...


    ...
  

...turns into this


      
...

If you want interaction and have a link, this...


    ...
  

...turns into this


      ...
    

If you want interaction and have a click handler, this...


    ...
  

...turns into this


      
    

It could look like the following in Vue.js as a component template


    
  

Go forth and develop accessible pattern libraries!

Questions?

Thanks!