Drupal Speaks

Aural user interfaces and how we're making Drupal 8 the most accessible CMS in the world

Jesse Beach
Senior Front End Developer

Wim Leers
Senior Software Engineer

Isn't Drupal accessible already?

As an inclusive community, we are committed to making sure that Drupal is an accessible tool for building websites that can also be accessed by people with disabilities.

We have an accessibility checklist

  • All non-text content has alternative text
  • All data tables have headings
  • Users can complete and submit all forms
  • Links make sense out of context
  • Media has captions and/or transcripts
  • Non-HTML content is accessible
  • Users can skip repetitive elements on the page
  • Meaning is not conveyed through color alone
  • Content is clearly written and easy to read
  • JavaScript is accessible
  • The site complies to standards

We have accessibility tags for issues

+needs accessibility review

+needs color accessibility review


Accessibility pledge



I pledge to make this [module or theme] as accessible as it can be. If you find any flaws, please submit an issue [link to issue queue]. Help me fix them if you can.

Interfaces give us access to information

Interfaces are sensual

  • Visual
  • Aural
  • Tactile
  • Every sense but taste and smell (so far)

Interface !== Visual

Oh crap! Screen readers! Put in alt attributes!

Visual is not primary

What is accessibility?

Accessibility is the foundation of usability

Three levels of doing accessibility

  • The markup - automated testing
  • The ux - behavioral testing
  • The content - real-time feedback


Accessibility Information Library

Unleash the robots

Automated testing

Codify best practice

Accessibility module

Behavioral testing

...or, check your ego.

DrupalCamp Twin Cities testing sessions

Study findings

  • “Fairly accessible for speech/ keyboard for content consumption.”
  • “I was able to complete tasks using only my keyboard”
  • “That was truly a pain free testing experience.” using Dragon NaturallySpeaking11 Professional

Session recording highlights

Testing content creation in Drupal

  • 01.00.00
  • 01.01.00

Issues about form validation that we discovered during the testing

Aural user interfaces

Drupal Speaks?

You betcha!

Let’s have a listen

Why consider speech output?

Isn't semantic HTML enough?

Tour module tooltip markup

<div data-index="0">
  <div role="dialog" aria-labelledby="tour-tip-views-ui-displays-label" aria-describedby="tour-tip-views-ui-displays-contents">
    <h2 id="tour-tip-views-ui-displays-label">Displays</h2>
    <p id="tour-tip-views-ui-displays-contents">A view can consist of multiple displays.</p>
    <div class="tour-progress">1 of 10</div>
    <a href="#" class="joyride-next-tip">Next</a>
    <a href="#close" class="joyride-close-tip">X</a>

The accessible-to-usable scale

Accessibility for everyone

dynamic applications undergo state and property changes.

those changes must be expressed

we build interfaces

for everyone

so how do we do it?

show and announce the changes

Rich experience for everyone


Accessible Rich Internet Applications

States like

  • active
  • disabled
  • pressed

Properties like

  • label
  • form
  • dialog
Unable to display content. Adobe Flash is required.


Describes an HTML element that reads its contents when those contents are altered.

<div aria-live="polite" class="element-hidden">
  some text to speak

Express changes aurally


  // Pass a string.
  Drupal.announce('The application has been updated.');

  // Make sure the string is translatable.
    Drupal.t('The application has been updated.')

  <div id="drupal-live-announce" class="element-invisible" aria-live="polite" aria-busy="false">The application has been updated.</div>

Quick successive calls

// Quick successive calls will concatenate.
Drupal.announce(Drupal.t('Freegan before they sold out polaroid.'));
Drupal.announce(Drupal.t('Pitchfork letterpress polaroid four.'));
Drupal.announce(Drupal.t('Cardigan cosby sweater fanny pack.'));

<div id="drupal-live-announce" class="element-invisible" aria-live="polite" aria-busy="false">
Freegan before they sold out polaroid.
Pitchfork letterpress polaroid four.
Cardigan cosby sweater fanny pack.

Wait, doesn't this accessibility feature depend on JavaScript?

JavaScript enabled Respondents
Yes 98.6%
No 1.4%

Guide the flow


Why Drupal.TabbingManager?

Depending upon the action to be performed by the dialog, the object with focus before the dialog box is opened should be saved. This will allow restoring focus to this element when the dialog box is closed.

Managing tabbing is difficult


Constrain tabbable elements to just those necessary to complete a task.

Overlay module

Screenshot of Overlay module's constrained tabbing — visualized by devel_a11y.

How-to: tabbing management

Using Drupal.TabbingManager.

Apply tabbing constraint

Using a core API rather than reinventing.

manageTabbing: function () {
  var tabbingContext = this.model.get('tabbingContext');

  // Always release an existing tabbing context.
  if (tabbingContext) {

  // Create a new tabbing context when edit mode is enabled.
  if (!this.model.get('isViewing')) {
    tabbingContext = Drupal.tabbingManager.constrain($('.contextual-toolbar-tab, .contextual'));
    this.model.set('tabbingContext', tabbingContext);

Example from contextual.module.

Announce the details of the constraint

Using Drupal.announce.

announceTabbingConstraint: function () {
  var args =  {
    '@contextualsCount': Drupal.formatPlural(Drupal.contextual.collection.length, '@count contextual link', '@count contextual links')

  Drupal.announce(Drupal.t('Tabbing is constrained to a set of @contextualsCount and the edit mode toggle.', args));
  Drupal.announce(Drupal.t('Press the esc key to exit.'));

Example from contextual.module.

Complex tabbing flows

Tabbing contexts can be stacked and popped as a user moves between task flows, even between modules.

Embrace modalities


The essential premise at the heart of Backbone has always been to try and discover the minimal set of data-structuring (Models and Collections) and user interface (Views and URLs) primitives that are useful when building web applications with JavaScript.

Framework doesn't matter

Separation of concerns matters

Before Drupal 8

event handlers ⟶ rendering ⟶ debug hell

Now in Drupal 8

events ⟶ state

state ⟶ rendering

developer ⟶ happy

A schema explaining the different components of Backbone. Relevant parts to us: data & state are modeled in Backbone Models. Backbone Models of the same type may be grouped in Backbone Collections if that is useful for the use case. Backbone Models are connected to Backbone Views via model event bindings, so that Backbone Views are able to update themselves whenever the data/state changes. A View is typically bound to a DOM element, within which it renders itself.

Source: http://kiranb.scripts.mit.edu/backbone-slides/


function toggleEditMode (event, data) {
  for (var i = contextuals.length - 1; i >= 0; i--) {
    contextuals[i][(data.status) ? 'detachHighlightBehaviors' : 'attachHighlightBehaviors']();
    contextuals[i].$region.toggleClass('contextual-region-active', data.status);


_toggleEditMode: function (event, data) {
  Drupal.contextual.collection.each(function (model) {
    model.set('isLocked', data.status);

How-to: Backbone.js for better accessibility

one model,
many views

Step 1: create a model

Contains state, is stupid. Views update it.

Drupal.contextualToolbar.Model = Backbone.Model.extend({
  defaults: {
    // Whether the toggle is currently in "view" or "edit" mode.
    isViewing: true,
    // Whether the toggle should be visible or hidden.
    isVisible: false,
    // A TabbingContext object as returned by Drupal.TabbingManager:
    // the set of tabbable elements when edit mode is enabled.
    tabbingContext: null

Example from contextual.module.

Step 2: create a visual view

99.9% of sites need visual output & mouse + touch input.

Drupal.contextualToolbar.VisualView = Backbone.View.extend({
  events: {
    'click': function () {
      this.model.set('isViewing', !this.model.get('isViewing')),
  initialize: function () {
    this.model.on('change', this.render, this);
  render: function () {
    var $el = this.el;
    var m = this.model;
    $el.toggleClass('element-hidden', !m.get('isVisible'));
    $el.find('button').toggleClass('active', !m.get('isViewing'));
    return this;

Example from contextual.module.

Step 3: create a keyboard view

For screen reader users & power users: keyboard input.

Drupal.contextualToolbar.KeyboardView = Backbone.View.extend({
  // …

Unnecessary here: the click event in the VisualView already works with keyboard input. We don't need focus or blur.

Step 4: create an aural view

For screen reader users: aural output.

Drupal.contextualToolbar.AuralView = Backbone.View.extend({
  initialize: function () {
    this.model.on('change', this.render, this);
    this.model.on('change:isViewing', this.manageTabbing, this);
  render: function () {
    var $el = this.el;
    var m = this.model;
    $el.find('button').attr('aria-pressed', !m.get('isViewing'));
    return this;
  manageTabbing: function () {
    // See TabbingManager example.
    Drupal.announce('<a relevant, explanatory message>');

Example from contextual.module.

All done!

Everything remains in sync!

Just like that.

Proposed best practice

Evolving as we develop more complex interactions

Devel accessibility

Not familiar with using a screen reader?

console.log announcements

Screenshot of devel_a11y's ability to log announcements via Drupal.announce to the browser's console.

Visualize tabbing constraints

Screenshot of devel_a11y's ability to visualize tabbing constraints defined by the TabbingManager.


a UI is sight, and sound and touch

Drupal 8 will communicate with each sense

using shared tools and a structured framework

and your module?


  1. Screen Reader User Survey #4. 2013. http://webaim.org/projects/screenreadersurvey4/
  2. http://kiranb.scripts.mit.edu/backbone-slides/#compenents-diagram


  1. http://previousnext.com.au/blog/so-you-want-be-accessible
  2. http://squizlabs.github.io/HTML_CodeSniffer/
  3. http://webaim.org/projects/screenreadersurvey4/
  4. Pillow fight

How do we make aural UIs informative without being annoying?

Listening to long instruction sentences gets annoying if they don't introduce more info and just repeat the same thing on every page. Eventually, one might want to turn off these audio announcements.


Help us improve Acquia products by taking part in UX research. Get a sneak peak at new Acquia offerings, try things out and give us your feedback.

And, we compensate our participants!

Studies are conducted remotely; no travel required.

Interested? Sign up at usability.acquia.com, contact us at uxresearch@acquia.com or find speak to Lisa Rex.