Jesse Beach
Senior Front End Developer
@jessebeach
Wim Leers
Senior Software Engineer
@wimleers
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.
+needs accessibility review
+needs color accessibility review
+a11y
#D7AX
#D8AX
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.
Developed primarily by Kevin Miller with input from Dan Mouyard and Nick Schonning.
Testing content creation in Drupal
Our work regarding Drupal 8 accessibility has focused primarily on enabling aural user interfaces.
Aural refers to the sense of hearing, just as visual refers to the sense of sight.
Yes, Drupal speaks!
Well, to be more precise, screen reading software speaks.
In this example, we'll see me using the tab key to navigate several interfaces within a typical Drupal page.
This video is meant to provide a stark example of what a complex interface interaction feels like when we remove the visual component.
NOTE: We're picking on the Tour module. It's meant to prove a point. Lots of aural UI in Drupal needs work and I had to pick something to illustrate this.
Now that example was intentionally long and intentionally without visuals to provide a really stark illustration of how quickly one can become lost in an interface; lose a sense of what the original intent was and where the interaction ends.
<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>
</div>
</div>
In trying to determine the scale of the effort to bring the aural experience of Drupal's interface up to the same level of polish of the visual UI, we wanted to understand just how far apart they might be.
So we put together this chart that plots the visual and aural UI experiences from a lower bound of inaccessible (a value of 0) to an upper bound of delightful interaction (a value of 4).
We might say that the visual content consumption experience of Drupal ranges from 2.5 to 3.5.
The visual site administration experience ranges from a 2 to 3.5
The aural content consumption experience ranges from 1.5 to 3
The aural site administration experience ranges from 0 to 3
We propose that the aural site administration experience -- managing content, managing views, configuring a site -- has the most variability in experience within Drupal.
As developers, we have tended to focus our accessibility improvements on static markup which is important, but it's only half the story.
We want to pull the lower bound of the aural UI experience at least up to accessible for all administrative pages and push the upper bound closer to delightful.
We are no longer just talking about baseline accessibility. Only after we have made an interface accessible, can we start to improve usability.
I think the bias of sighted people is to assume that visual interfaces are accessible so much so that we rarely talk about the accessibility of visual interfaces.
Accessibility is wrongly used as a synonym for non-visual UIs; for the extra markup; for the development we might get around to if there is enough time in the project.
We must stop thinking like this.
Our jobs, our passion, is making information accessible, to everyone.
Semantic markup is mostly sufficient to render a content consumption, such as reading an article, accessible.
As we build better site maintenance and content management tools into Drupal, semantic markup is no longer sufficient.
[Jesse] Take for example infinite scrolling. Page content is updated dynamically as the user scrolls a page. Ideally, this would not only be visually indicated. It would be indicated through aural updates as well.
This video will demonstrate how tabbing constraints and audio announcements are used in Drupal 8.
The will move from a context of a basic page, to edit mode being enabled. In edit mode, only the contextual links on entities are a available via tabbing.
The user will then edit an entity. The tabbing context will shift to that of elements in the overlay and toolbar.
Closing the overlay will return the tabbing context to the contextual links. Disabling edit mode will return the tabbing context to that of the full page.
Rather than accessibility for some, we must talk about rich experiences for everyone. This phrase comes from Everett Zufelt.
We have teams of UX professionals thinking about visual presentation patterns. We fret over color and placement, animation and typography.
I want to see us put the same effort into designing audio prompts, keyboard shortcut bindings and voice interations because these are interfaces, too.
So let's move from our brief introduction to accessibility in Drupal to the work currently underway to make it even better.
[Wim] Modern UIs are complex. As we introduce just-in-time information and dynamically updating pages, we also introduce new design patterns to express to the user that their page contents have changed: spinner icons, quick flashes of color, shifting boxes, fade in effects. Almost exclusively, these indicators are visual. While we were helping to improve authoring experience tools for Drupal 8, we realized that Drupal is, too, evolving into a complex and dynamic front end application. That we need to express to the user when and how their interface has updated. And that we need to do this within Drupal's guidelines. We can't only express updates visually because for users who are blind, our UIs are frustratingly silent about what's going on.
We use ARIA attributes to represent through markup what you are accustomed to representing through visual language.
In this video we show the following snippet of code change when the user clicks the Menu button in the Drupal 8 toolbar.
<a href="/admin" title="Admin menu" class="icon icon-menu toolbar-tab" id="toolbar-item--2" data-toolbar-tray="toolbar-item--2-tray" aria-owns="toolbar-item--2" role="button" aria-pressed="false">Menu</a>
The aria-pressed attribute toggles between false and true as the button is toggled off and on.
Describes an HTML element that reads its contents when those contents are altered.
<div aria-live="polite" class="element-hidden">
some text to speak
</div>
Describes an HTML element that reads its contents when those contents are altered.
In Drupal 8, we provide a simple way to turn text into speech.
The Drupal.announce function was first brought up at the Drupal Camp Toronto accessibility sprint in the fall of 2012.
We need a way to express changes in the state of the toolbar that could not be described with existing aria state markup.
An example is whether or not a toolbar tray is open when a tab in the toolbar is toggled.
It quickly became obvious that many modules in Drupal would benefit from a system that could read arbitrary strings through the screen reader.
It wasn't until the later winter of 2013 that we put together the first prototype of the Drupal.announce function.
// Pass a string.
Drupal.announce('The application has been updated.');
// Make sure the string is translatable.
Drupal.announce(
Drupal.t('The application has been updated.')
);
We invoke the Drupal.announce function by passing it a string that will be read by the screen reader.
<body>
...
<div id="drupal-live-announce" class="element-invisible" aria-live="polite" aria-busy="false">The application has been updated.</div>
</body>
Drupal.announce manages a hidden div on the body tag. When the text of this tag is changed, the screen reader reads the new text.
<script>
// 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.'));
</script>
<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.
</div>
Not really. Drupal.announce is only necessary for dynamic applications and those will already be dependent on Javascript.
More importantly, JavaScript use among people who also use a screen reader is nearly 100%.
JavaScript enabled | Respondents |
---|---|
Yes | 98.6% |
No | 1.4% |
The rate of users that employ a screen reader and who have JavaScript enabled is about the same as those who don't a screen reader.
http://developer.yahoo.com/blogs/ydn/many-users-javascript-disabled-14121.html
[Wim] Jesse explained an important component for accessible dynamic pages in Drupal 8 that she got committed: Drupal dot announce.
That was about aural updates, about communicating change. What's also important, is to assist in keyboard navigation, to guide the flow of the user throughout your application, throughout Drupal.
This benefits both screen reader users and power users that prefer keyboard navigation
That brings us to the other big accessibility component: the tabbing manager. Both of us worked to get this in Drupal core.
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.
[Wim] This is quote from the spec. What it basically says is "x and y should happen". But it does not specify how that should happen.
So, basically the spec says: "figure it out yourself"…
This might lead you think that this is rather messy … and you would be absolutely right!
[Wim] What we want to avoid is putting the responsibility of tabbing management on every individual contributed module that might need it. (We had to solve it for in-place editing and the Drupal 8 toolbar.)
The overlay module already had code to deal with this. What we did, was pull it out of there, and make it possible to use it for more advanced use cases.
Constrain tabbable elements to just those necessary to complete a task.
[Wim] The essence of the tabbing manager is just this: constrain tabbing (i.e. keyboard navigation) to potentially relevant elements only.
[Wim] Here we see a visualization of which elements are reachable via tabbing in the overlay. It is only possible to tab to either the toolbar buttons or any of the elements within the overlay. It is impossible to reach elements on the underlying page — elements that were reachable before opening the overlay.
Using Drupal.TabbingManager
.
Using a core API rather than reinventing.
manageTabbing: function () {
var tabbingContext = this.model.get('tabbingContext');
// Always release an existing tabbing context.
if (tabbingContext) {
tabbingContext.release();
}
// 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
.
[Wim] Let's first look at the code within the if-test at the bottom. That's where a tabbing constraint gets applied. We call Drupal dot tabbingManager dot constrain, with a selector for the elements that should be tabbable.
The constrain method returns a "tabbing context". Tabbing contexts are the building blocks that the tabbing manager uses to keep track of things. We store a reference to it.
Now let's look at the rest of the function. Let's start at the top. First, a reference to the existing tabbing context (if any) is retrieved. Then, if there indeed is a tabbing context, we release it. Finally, if the "is viewing" variable equals false, we apply a new tabbing constraint.
Looking at the whole, this method gets called whenever the "is viewing" variable's value changes (from true to false or vice versa). The existing tabbing constraint is always released. A new tabbing constraint is applied if the value for "is viewing" equals false, which means that the pencil icon (also known as "edit mode") is toggled in the toolbar.
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
.
[Wim] Now, it's important to not forget to announce tabbing constraints. We can do that with the component that Jesse explained just now: Drupal dot announce.
We announce: 1) that tabbing is constrained, 2) to which elements it is constrained, 3) a keyboard shortcut that is highly relevant to the user: one to cancel the tabbing constraint.
Tabbing contexts can be stacked and popped as a user moves between task flows, even between modules.
[Wim] Something that Jesse and I found very important, is to ensure that multiple modules can each have tabbing constraints simultaneously, without running into problems. Otherwise, Drupal contrib would not be able to use the tabbing manager in practice.
For example: the user can navigate to the edit mode toggle in the toolbar, toggle it and then tabbing is constrained to contextual links. The user can then tab from one contextual link to the next, and open them. If the user were to start in-place editing from one of those contextual links, then the in-place editing functionality would apply a new tabbing constraint (that gets stacked on top of the previous one). When stopping in-place editing, the contextual links tabbing constraint will be applied again. (The in-place editing tabbing constraint was popped.)
[Wim] Let's have another look at how tabbing is constrained when the user enables edit mode and then opens the overlay. Note how the edit mode tabbing constraint is restored after the overlay tabbing constraint context is removed.
Here is a more complex flow that we recently developed for in-place editing.
[Wim] We want all Drupal developers to embrace modalities.
A modality refers to the medium that we use to access information. That medium may be visual, it may be aural; it might be through touch as well.
We can account for modalities in our code. That's we've begun to do in Drupal 8 core.
To do that, we're leveraging Backbone.js
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.
[Wim] Why Backbone?
No special reason.
We don't want to impose a certain highly restrictive structure on Drupal JavaScript, nor does Backbone.
event handlers ⟶ rendering ⟶ debug hell
[Wim] DOM event handlers directly performed rendering (i.e. DOM modifications). This made debugging very hard, painful, frustrating …
events ⟶ state
state ⟶ rendering
developer ⟶ happy
Source: http://kiranb.scripts.mit.edu/backbone-slides/
Note that in Backbone.js, the View is a combination of a View and Controller in the MVC pattern. The Models are intentionally kept stupid, to make it easy to link them with more Views. Since the DOM also combines rendering (HTML elements rendered in a certain way) and event handling (DOM events), and the View is the connection to the DOM, this feels natural.
However, if you prefer a full separation, then it's entirely possible to have Views that are solely listening for events and updating the bound Model, and separate Views that are responsible for rendering.
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);
}
}
versus
_toggleEditMode: function (event, data) {
Drupal.contextual.collection.each(function (model) {
model.set('isLocked', data.status);
});
}
[Wim] We are using Backbone to formalize modal interactions: one model to track state, and a view for each modality.
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
.
[We might want to add a transcript here too.]
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
.
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
.
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
.
As a general rule: a Backbone View must receive two things: a Backbone Model and its "root" DOM element.
This is our current advised best-practice: create a Visual View that covers the needs of >90% of the users — being visual output and mouse + touch handling. Then the developer may add a Keyboard View to support keyboard navigation. And finally, the developer may add an Aural View, to provide auditive feedback via Drupal.announce and set the appropriate ARIA attributes.
In other words: we don't want to force every developer to create an Aural View. We just want to make it as easy as possible to support it.
Everything remains in sync!
Just like that.
Evolving as we develop more complex interactions
[Wim] As Drupal developers, we are all responsible for best practices.
Since these types of UIs are new (to us at least) we're going to have numerous breakthroughs that replace our previous best practices.
console.log
announcements[Wim] It supports logging Drupal.announce
ments.
[Wim] And it supports both logging and visualizing Drupal.TabbingManager
tabbing constraints. A CSS animation occurs whenever the tabbing constraints change, tabbable elements have a red box shadow, the currently focused tabbable element has a green box shadow.
So to conclude...
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.