Making Accessible Ember Components

I’m Josh

I work at Uniiverse

@jdjkelly && robotfuture.net

What are components?

Web Components

Templates, Custom elements, Imports, Shadow DOM

Ember Components

Templates/Layouts, Ember.Component, Resolver, ???

An Ember.Component might look like this:

App.NameTagComponent = Ember.Component.extend({
    actions: {
        hello: function(name) {
            alert(name);
        }
    }
});

Its template might look like this:

<div {{action 'hello' person.name}}>
    Hi, my name is {{person.name}}
</button>

It might be instantiated like this:

{{name-tag person=current_user}}

What is accessibility?

“Screen readers don’t work with Javascript”

Wrong, wrong, wrong. Screen readers only care about DOM.

⌘-F5

WAI-ARIA

Accessible Rich Internet Applications

Sounds like Ember, right?

Architecture

Accessible elements

Roles & States/Properties

Roles

allows the author to annotate host languages with machine-extractable semantic information about the purpose of an element

Or: the purpose of an element in the DOM

Simplest possible example:

<div role="button">
   Click me
</div>

Basic

alert alertdialog button checkbox dialog gridcell link log marquee menuitem menuitemcheckbox menuitemradio option progressbar radio scrollbar slider spinbutton status tab tabpanel textbox timer tooltip treeitem

Complex

combobox grid listbox menu menubar radiogroup tablist tree treegrid

An example of a component with parent and child roles is a menu:

<awesome-nav role="menu">
   <div role="menuitem">Item one</div>
   <div role="menuitem">Item two</div>
</awesome-nav>

States/Properties

What meaningful properties does this object have at this time?

<awesome-nav role="menu" aria-expanded="true">
   <div role="menuitem">Item one</div>
   <div role="menuitem">Item two</div>
</awesome-nav>

Roles communicate the purpose of the component to the assistive software.

States communicate the state of the current state of the component.

Applying ARIA to Ember

Role support is built-in

Taco Button Component example

taco-button-component.js

App.TacoButtonComponent = Ember.Component.extend({
  tagName: 'taco-button',
  nameBinding: 'taco.name',
  attributeBindings: ['label:aria-label', 'tabindex'],
  answer: false,

  label: function() {
    return "Are " + this.get('name') + " tacos tasty?";
  }.property('name'),

  tabindex: -1,
  ariaRole: 'button',

  click: function(event) {
    alert('Yes');
  },

  keyDown: function(event) {
    if (event.keyCode == 13 || event.keyCode == 32) {
      this.click(event);
    }
  }                                       
});

components/taco-button.hbs

{{label}}

index.hbs

{{taco-button taco=model}}

Rendered DOM

<taco-button aria-label="Are spicy tacos tasty?" tabindex="1" role="button">
    Are spicy tacos tasty?
</taco-button>

What about something more complex?

Buffer slide

  • A note about future proofing
  • What’s the shadow DOM?
  • Is the Shadow DOM keyboard navigable?
  • So when Ember Components implement Shadow DOM will they still be accessible?

Thanks!

Come see an expanded version of this talk at Toronto Javascript on June 23

notes && slides