6/28/2014 - 3:36 PM

Making Accessible Ember Components

Making Accessible Ember Components

Making Accessible Ember Components

Making the web accessible is important. We have ethical and, in some cases, legal obligations to ensuring access to all of users.

Luckily for us, it's easy to make an accessible Ember Component.

What are components?

To understand the accessibility story around Ember Components, we have to start by talking about Web Components. Ember Components are designed to be interoperable with the final Web Components API.

Web ComponentsEmber Components
Custom elementsEmber.Component
Shadow DOM???

An Ember.Component might look like this:

App.NameTagComponent = Ember.Component.extend({
    tagName: 'name-tag',
    actions: {
        hello: function(name) {

This is a simple button. It has an action that pops open an alert that displays the string it's passed as the arg name. Its template might look like this:

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

It might be instantiated like this:

{{name-tag person=model}}

Here's a demo: JSBin

We don't need to worry about how the Resolver works - that's an implementation detail solved by the framework. But keep the Shadow DOM in mind - I'll mention it under Future Proofing.

What is accessibility?

The ability to use an application. When a user has diminished control over an input device (keyboard, mouse), assitive technologies fill the gap. Screen readers are an extremely common example.

If you're using OS X you've got a screen reader built into your operating system. It can be opened by hitting ⌘-F5.

The W3C maintains a number of accessibility specs. The most important of these, for the Ember developer, is WAI-ARIA - or Web Accessibility Initiative - Accessible Rich Internet Applications.

ARIA Architecture

At a high level, the ARIA API is a way of annotating HTML to define the behaviour of an arbitary element. This diagram from the accessibility gives us a good idea of how ARIA fits into what we're building.

So it turns out - and this might surprise some experienced developers - that Javascript ain't no thing for screen readers - as long as that Javascript is building DOM. Which is exactly what Ember does (this will be truer still with HTMLBars).

ARIA gives us two primitives to mark up our HTML with: Roles and States/Properties. In using both in combination we can produce DOM that is semantically meaningful to a screen reader.


The role attribute, borrowed from the, Role Attribute [ROLE], allows the author to annotate host languages with machine-extractable semantic information about the purpose of an element. It is targeted for accessibility, device adaptation, server-side processing, and complex data description.

Put another way, a role is the purpose of an element in the DOM.

Simplest possible example:

<div role="button">
   Click me

ARIA gives us lots of roles to work with. The most basic roles can "act as standalone user interface widgets or as part of larger, composite widgets":

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

We also have access to complex roles, with child-role relationships:

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>

States and Properties

What meaningful properties does this object have at this time?

We also need a way to be able to describe various properties and states a component might have.

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

When a user tabs to the awesome-nav element, we need to be able to communicate the state of the menu to the screen reader. Otherwise, there would be no way to determine if the next tab action should highlight

or move on to a sibling element.

It should be obvious that the element's role will dictate which states/properties are relevant. These states/properties can be required, supported, or inherited. For our purposes this distinction isn't important. All we need to know right now is that a given role has a given list of states/properties which can be meaningful.

The menu role, for example, allows use to specifiy any of the following states/properties:

aria-activedescendant aria-atomic aria-busy aria-controls aria-describedby aria-disabled aria-dropeffect aria-expanded aria-flowto aria-grabbed aria-haspopup aria-hidden aria-invalid aria-label aria-labelledby aria-live aria-owns aria-relevant


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

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

Accessible Ember Component Examples

Built-in support?

Ember.Component - by way of Ember.View - actually gives us built-in support for a property called ariaRole. We can pass this property a string, and it will automatically fill in the role attribute in the generated DOM. Otherwise, we need to do a little bit of work ourselves.

Button Example


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?";
  tabindex: -1,
  ariaRole: 'button',
  click: function(event) {
  keyDown: function(event) {
    if (event.keyCode == 13 || event.keyCode == 32) {

Parts worth mentioning: tabindex, ariaRole, aria-label, keyDown

tabindex: makes the content accessible via tabbing

ariaRole: outputs a role attribute to the DOM

aria-label: can be semantically important (for our demo this won't matter)

keyDown: captures enter/space keyboard presses which emaulate the behaviour of a click




{{taco-button taco=model}}

Rendered DOM

Note: I've omitted the metamorphs and ember-view ID attributes

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

JS Demo: Accessible Example

JS Demo: Inaccessible Example

Pop open VoiceOver and check out the difference for yourself.

Menu Example


A note about future proofing

Recalling the difference between Web Components and Ember Components:

Web ComponentsEmber Components
Custom elementsEmber.Component
Shadow DOM???

What's the Shadow DOM?

[T]here is a fundamental problem that makes widgets built out of HTML and JavaScript hard to use: The DOM tree inside a widget isn’t encapsulated from the rest of the page. This lack of encapsulation means your document stylesheet might accidentally apply to parts inside the widget; your JavaScript might accidentally modify parts inside the widget; your IDs might overlap with IDs inside the widget; and so on. Shadow DOM addresses the DOM tree encapsulation problem.

HTML5 Rocks

Is the Shadow DOM keyboard navigable?


-Marcy Sutton

So when Ember Components implement Shadow DOM will they still be accessible?


Call for help

If all this sounds great, please send a PR adding accessibility to the Ember UI project. I've added support for a few in my free time. It would be great to see other people at this meetup contribute.


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