Accessibility: The Missing Ingredient

Once upon a time, I treated web accessibility as something of an extra. Sure, my images had alt attributes. Yes, my anchors contained titles. I honored 508 compliance, too, but it was usually the last thing I did. The way I saw it, the cake was ready to eat; accessibility was the decorative icing to slather on at the end.

Article Continues Below

Sadly, I wasn’t alone. Many developers I’ve recently spoken with seem to place accessibility last, if they address it at all. Why is accessibility often treated as an afterthought? Key factors include lack of tools and specifications, weak industry demand, and developer laziness.

Back in the late ’90s, web accessibility was primitive at best. JAWS was still in its infancy, and WAI-ARIA had yet to be conceived. All that seemed to matter was how quickly the user could be visually impressed. DHTML and Flash wizards ruled the land. Flannels looked best worn around the waist.

As accessibility began to gather steam in 2004–2005, many developers and companies still paid little attention to the subject. Web developers were lost in CSS black magic, PHP tutorials, and JavaScript books. As a journeyman freelancer, I had never heard accessibility mentioned as a project requirement.

Despite the now well-publicized and supported WAI-ARIA, a specification engineered specifically to help impaired users, we developers often do not implement it. Perhaps we’re waiting for an accessibility-minded client to catalyze the learning process. Perhaps we feel overwhelmed at where to start. Whatever the excuse, it’s no longer valid given the resources and technology currently available.

A few months into my employment at IBM, the web application my colleagues and I had been devoting our weekly hours to had to satisfy a stringent accessibility checklist. We needed to thoroughly test the entire web application against WAI-ARIA and WCAG compliance. I was suddenly tasked with creating a comprehensive plan detailing the open issues, as well as estimating how long it would take for the team to address them. Placing accessibility last for so many years left me thinly educated and unprepared.

Through trial and error, the help of my knowledgeable colleagues, and much reading on the subject, I’ve emerged as a changed developer. Coding accessibly is not an extra thing to consider at the end of a project, but simply another thing to consider from the beginning. Let’s walk through a homegrown example that showcases some useful WAI-ARIA roles, states, and properties.

Before we begin#section2

Screen readers, like browsers, vary among vendors. My intention is not to provide an exhaustive tutorial on this broad topic. I chose ChromeVox for this tutorial because it’s a free screen reader plugin that runs on both Mac and Windows. However, many other screen readers exist. For web development involving WAI-ARIA, here are some popular choices.

Windows users#section3

Mac users#section4

Linux users#section5

As mentioned above, each reader is unique. If you’re following this demo with a reader other than ChromeVox, you’ll notice differences in how the assistive technology parses ARIA.

What are we trying to accomplish?#section6

Our client would like a shopping cart for his robot parts business, Robot Shopper. The cart must meet WAI-ARIA guidelines and work smoothly from a keyboard. To test our cart on a screen reader, we’re going to use ChromeVox, which will allow us to aurally experience our web page. After we’re through, you may wish to disable the plugin. To do that, visit chrome://extensions and untick the box beside the ChromeVox plugin.

To manage items in the cart, we’ll use HTML5 storage, and we’ll employ some JavaScript templating using Handlebars to display our inventory markup. As for the design, here’s what we’re aiming for:

Initial display with the cart closed.
Initial display with the cart closed.
The cart is opened and empty.
The cart is opened and empty.
The cart is opened with items.
The cart is opened with items.

Beginning markup#section7

first_pass/index.html#section8

<body role="application">

	<div id="container">
		<header>
			<h1>Robot Shopper</h1>

			<button
				title="Cart count"
				id="shopping_cart">
			</button>
		</header>

		<!-- dynamically loaded from our template -->

		<div
			id="main"
			tabindex="-1">
		</div>

		<footer>
			<p>Robot Shopper © 2014</p>
		</footer>
	</div>

	<div
		id="my_cart"
		tabindex="0">

		<div id="my_cart_contents"></div>

		<button
			title="Close dialog"
			id="close_cart">
			x
		</button>
	</div>

	<div id="page_mask"></div>
	
</body>

The application role#section9

Although I didn’t plan on diving into ARIA roles in our first pass of the code, adding the application role to the body is mandatory for the experience we wish to create. This small passage from the Yahoo! Accessibility blog really captures the gist of this role:

Your average web page is a document. The user expects to read content and it may feature some interactive behaviors. Applications are more like a desktop application. The user expects tool sets, instant changes, dynamic interactions.

One must-have feature of our Robot Shopper application is for users to navigate quickly between product sections using the up and down arrow keys. For certain screen readers, such as NVDA, removing the application role will prevent our custom JavaScript from overriding these keyboard events. Without the application role, pressing the up and down arrows will instead move the focus between every element on the page—not what we want. Using this role, we’re informing the browser, “I know what I’m doing. Don’t override my defined behaviors.” Keep in mind that any region we declare as an application requires us, the developers, to implement custom keyboard navigation.

Our template#section10

You may have noticed above that our main div designated for storing all of our products is empty. That’s because our Handlebars template immediately loads our JSON product data into this element.

first_pass/index.html#section11

<script id="all_products" type="text/x-handlebars-template">
	<div id="product_sections">
		{{#products}}
			<section tabindex="-1">
				<div class="product_information">
					<h2 class="product_title">{{title}}</h2>
					<span class="product_price">${{price}}</span>
				</div>
				<div class="product_details">
					<a href="#" title="{{title}} details">
						<img class="product_thumb" src="{{img_uri}}" alt="{{title}}">
					</a>
					<p class="product_description">{{description}}</p>
				</div>
				<button
					title="Add to Cart"
					class="button add_to_cart"
					tab-index="0"
					data-pid="{{pid}}">
					Add to Cart
				</button>
			</section>
		{{/products}}
	</div>
</script>

first_pass/products.js#section12

data = { products: [
	{
		title: "Jet Engines",
		price: "500.00",
		img_uri:	"http://goo.gl/riaO3q",
		description: "It's only a jetpack--a simple, high-thrust, fuel-thirsty jetpack. I would add this puppy to the cart. What could go wrong?",
		pid: "product_093A14"
	},
	
	…

]};
document.getElementById("main").innerHTML = template(data);

ChromeVox time (Demo)#section13

Let’s check out our Robot Shopper in action. In the demo, click “Add to Cart” and see the cart count in the blue robot’s chest increase. Simple, right? But how does this experience translate within the context of a screen reader?

First, let’s talk page navigation. Although we can tab to product detail anchors and “Add to Cart” buttons, it takes users two tab presses to traverse each item—a chore if a disabled user wishes to reach an item far down on the list using a keyboard. We’ve solved this issue by adding keyboard arrow navigation.

first_pass/app.js#section14

$(document.documentElement).keyup(function (event) {

	…

	if (key_pressed === UP) {
		direction = "prev";
	} else if (key_pressed === DOWN) {
		direction = "next";
	}

	…

$(".selected")[direction]()
	.addClass("selected")
	.focus()
	.siblings()
	.removeClass("selected");
	
	…
		
});

One interesting thing about this code is that we’ve associated the arrow key pressed with the direction we’d like to move in the product list. The directional string ("prev" and "next") becomes an interpolated jQuery function that does the moving for us. Using the up and down arrow keys, we can move between items more quickly and efficiently. After arrowing (or clicking) to the product section we’re interested in, we can then tab to each product’s anchored thumbnail or “Add to Cart” button.

A lackluster cart experience#section15

From an empty cart, we click an “Add to Cart” button. Sighted users can see the number on the blue robot’s chest increment. Non-sighted users only hear, “Add to Cart button.”

Clicking the blue robot launches the cart modal. Of course, a user wouldn’t know that unless she could see it happening. Users dependent on screen readers will hear, “One button,” or the number of items in the cart.

Once we’re in the visible modal, we can increase, decrease, and remove all items from the cart. In our first pass, however, accessing those buttons leads to a rather poor UX for visually disabled users. “Plus button” isn’t a good enough response from our screen reader when we hit the “+” button.

Similarly, when closing the cart modal using our “x” button, our reader tells us, “x button.”

It’s not the fault of the screen reader, either. Our reader doesn’t have what it needs to offer a rich experience to users dependent on it. To provide a rich experience for non-sighted users, we must improve our semantics using WAI-ARIA roles, states and properties.

First pass fixes#section16

We’ll approach our accessibility enhancements using a general-to-specific approach.

1. Landmark roles#section17

Although somewhat semantic, our first pass markup contains no landmark roles. Why use landmark roles? According to Steve Faulkner, accessibility guru and advocate:

Adding ARIA landmarks to your existing site, or to a site you are developing, provides useful global navigation features and aids understanding of content structure for users.

Landmark roles allow users of a screen reader to navigate to major sections of your website with a single keystroke. Our markup also lacks ARIA states and properties. These states and properties are terms, which, according to the W3C, “provide specific information about an object, and both form part of the definition of the nature of roles.”

second_pass/index.html#section18

<!-- Header -->
<header role="banner">

<!-- Main content element replaces original div element-->
<main
	id="main"
	tabindex="-1">
</main>

If we’d chosen to remain with our div in parenting our main content, adding role="main" to it would be acceptable for accessibility purposes. However, we’re going to forgo the role in lieu of the straightforward main tag.

Cart dialog:#section19

second_pass/index.html#section20
<div
	id="my_cart"
	tabindex="0"
	role="dialog"
	aria-hidden="true"
	aria-labelledby="my_cart_title"></div>

When items are first added to the cart, markup is dynamically created using code in the updateCartDialog function. Sighted users will notice that the cart is not yet visible. For non-sighted users, we need to ensure the cart is not announced by our screen reader. To do this, we’ve added the aria-hidden attribute which, according to the specification, sets a state on a given element “indicating that the element and all of its descendants are not visible or perceivable to any user as implemented by the author.” It goes on to say, “if an element is only visible after some user action, authors MUST set the aria-hidden attribute to true.” The cart dialog is labeled by this table’s caption element.

second_pass/app.js#section21
$cart_contents
	.html("")
	.append("\
	<table id='cart_summary'>\
		<caption id='my_cart_title'>Cart Contents</caption>\
	…
	
	</table>\

2. Adding/removing items from the cart should notify the user#section22

Clicking the “Add to Cart” button triggers ChromeVox to say, “Add to Cart button,” which isn’t very helpful to a user dependent on aural cues. Users should know what exactly is being added to the cart. Also, the action of adding and removing items from within the cart modal should audibly inform the user how many items are in the cart the moment the cart count changes.

To make this happen, we first modify the “Add to Cart” button markup in our Handlebars template.

second_pass/index.html#section23
<script id="all_products" type="text/x-handlebars-template">
	
	…
	
	<button
		class="button add_to_cart"
		tabindex="0"
		aria-label="Add {{title}} to the cart"
		aria-controls="shopping_cart cart_count"
		data-pid="{{pid}}">
		Add to Cart
	</button>
	
	…

</script>

The action associated with this button controls the shopping_cart and cart_count elements by incrementing the total number of items in the cart.

<script id="all_products" type="text/x-handlebars-template">
	
	…
	
	<button
		title="Cart Count"
		id="shopping_cart"
		aria-owns="cart_contents"
		aria-label="Cart count">
	</button>
	
	…
	
</script>

The shopping cart button (the blue robot) stores the current number of items in the cart. However, to audibly notify the user of changes as they occur, we’ll take a new approach.

<div
	class="aria_counter"
	id="cart_count"
	aria-live="polite">
</div>

As the user adds an item to the cart, we update our aria counter which contains an aria-live attribute. An element using this attribute will announce its contents when something changes.

3. Cart dialog buttons need ARIA properties#section24

Cart dialog buttons.
Cart dialog buttons.

3a. “x” button (remove all)#section25

As stated earlier, clicking “x” currently prompts ChromeVox to say, “x button.”

first_pass/app.js#section26
<button class='button row_button remove_row_items' title='Remove all items of this type' data-pid='" + element.pid + "'>x</button>

Our improved “x” button related code lives in the updateCartDialog function.

second_pass/app.js#section27
<button aria-controls='row_" + index  +  " cart_count item_count' class='button row_button remove_row_items' aria-label='Remove all " + element.title + "s from the cart?' data-pid='" + element.pid + "'>x</button>\

Introducing the aria-controls attribute to our button provides more semantic meaning. We’re informing screen readers that a given row in our cart is controlled by the “x” button within each row, and that we can remove the entire row by clicking the button and confirming the removal. In addition, the “x” button is now labeled, so screen reader users will be told which exact item is to be removed from the cart.

3b. “−” button (decrease quantity of specific item)#section28

When clicked, ChromeVox reads aloud, “dash button.” We store the id of the target quantity field the button changes within a non-semantic data attribute.

first_pass/app.js#section29
<button class='button row_button decrement_row_item'  title='Decrease quantity by one' data-pid='" + element.pid + "' data-product-quantity='product_quantity_" + index  + "'>-</button>

…

$(document).on("click", ".decrement_row_item", function() {
	app.decrementItemQuantity(this.dataset.pid, this.dataset.productQuantity);
	
	…

});

Let’s do away with the non-semantic data attribute in place of a semantic ARIA attribute. Here’s our improved version:

second_pass/app.js#section30
<button aria-controls='product_quantity_" + index  + " cart_count item_count' class='button row_button decrement_row_item' aria-label='Decrease " + item_title + " quantity' data-pid='" + element.pid + "'>-</button>

$(document).on("click", ".decrement_row_item", function() {
	var aria_controls = $(this).attr("aria-controls").split(" ")[0];
	app.decrementItemQuantity(this.dataset.pid, aria_controls);
	
	…
  
});

The “-” button is now labeled. For a better UX, our label now includes the product title. Adding aria-controls to the mix, we declare the elements in the DOM that are controlled by this button:

  1. Product quantity: the td containing the current product quantity
  2. Cart count: the off-screen cart counter whose value is announced when the number of items in the cart changes
  3. Item count: another off-screen counter whose value is announced when the quantity of each item in the cart changes

Here is the JS handling the button click:

$(document).on("click", ".decrement_row_item", function() {
	// get the product quantity id of the row
	var aria_controls = $(this).attr("aria-controls").split(" ")[0];
	app.decrementItemQuantity(this.dataset.pid, aria_controls);
	
	…
	
});

Improving our markup has the added benefit of making our JS more specific, too. Our second argument to decrementItemQuantity becomes part of the value of the aria-controls attribute. So, not only is the button more accessible, our code has become more readable.

4. Cart modal needs ARIA state#section31

Currently, when we launch the cart modal, ChromeVox doesn’t indicate this detail to the user. This is because we only add and remove a semantically weak class name on the body to get this interaction rolling.

first_pass/app.js#section32
showModal: function() {
	$("body").addClass("show_cart");
},

removeModal: function() {
	$("body").removeClass("show_cart");
}

To make our reader perform better, we’ll feed it better semantics. When the cart is open, we’d like to conceal our container (everything beneath the cart modal) from assistive technologies. Conversely, our cart now comes out of hiding and is clearly revealed to assistive technologies.

second_pass/app.js#section33
showModal: function() {
	if (app.elements.$my_cart.attr("aria-hidden") === "true") {
		$("body").addClass("show_cart");
		
		…
		
		app.elements.$container.attr("aria-hidden", "true");
		app.elements.$my_cart.attr("aria-hidden", "false");
	}
	
	…
	
}

5. Selected product sections need ARIA state#section34

When users press the up and down arrow keys, a “selected” class is added to represent the currently selected product section. Let’s trade that class for aria-selected.

second_pass/app.js#section35
$(document.documentElement).keyup(function(event) {

	…

	if (key_pressed === UP) {
		direction = "prev";
	} else if (key_pressed === DOWN) {
		direction = "next";
	}
	
	…
	
	$selected[direction]()
		.attr("aria-selected", "true")
		.focus()
		.siblings()
		.attr("aria-selected", "false");
		
	…
		
});

Round 2 (Demo)#section36

Follow along and notice how we’ve improved the UX for disabled users through better semantics.

  1. From an empty cart, add any item to the cart.
    • Before: “Add to cart button”
    • After: “Add {{title of product}} to the cart button. Cart count one”
  2. Tab to focus on the robot.
    • Before: “One button”
    • After: “Banner, cart count one”
  3. Click the robot to open the cart.
    • Before: (contents of the cart)
    • After: “Enter dialog” (contents of the cart)
  4. Click the “+” button in the opened cart containing one item.
    • Before: “Plus button”
    • After: “Increase {{title of product }} quantity button. Item quantity two. Cart count two”
  5. Click the “x” to remove all items of a certain type.
    • Before: “X button”
    • After: “Remove all {{title of product}}s from the cart button”
  6. Cancel out of the JavaScript confirmation pop-up and exit the modal dialog using the escape key or “x” button.
    • Before: “”
    • After: “Exited dialog”

Our focus then returns to the last selected product before the cart was opened. If no product section had been previously selected, focus returns to the first product section in the list.

Wrapping up#section37

I’d be a liar if I told you writing accessible code takes no extra work. When prototyping and sketching with our product designer, we scrutinize our decisions even more heavily than before. Each design must lead to an accessible UX. Similarly, developers unfamiliar with accessibility practices may spend more time revising their commits before seeing their code merged.

Good product managers realize that new challenges beget greater time requirements from designers and developers. Encouragingly, we are highly adaptable creatures; these up-front labor costs will diminish over time. Most important, this hard work will yield a more usable product for all customers and a more readable codebase for developers. Let’s agree not to treat accessibility as the icing on the cake, but rather as an essential part of the mix.

About the Author

Andy Hoffman

Andrew Hoffman is a front-end developer at IBM. He loves cycling, awkward humor, and complex analogies. He tweets about code and obscure life observations at @antibland.

18 Reader Comments

  1. You seem to be conflating the need for accessible websites with the use of ARIA roles.

    It’s important to make clear that you do not need to use ARIA roles in order for a site to be accessible.

    For HTML markup use a visually hidden “skip to content link” at the top of the page and then make sure, where possible, to use ordered headers (On some pages, like the homepage, it may not make sense to use ordered headers from h1 -> h6). There’s more, of course, but these two are a good starter.

    Having been a contractor in London for a number of years I used to ask a preliminary question, Do you aim to create accessible websites?

    This has been answered no so many times I don’t even bother asking it now. These are major multinational companies including one in the eduction sector.

    It’s just not on their radar. I suppose if there were was a well-publicised court case then this state of affairs may change.

  2. Agree with DivisiveCotton above. In general webdev workflow, ARIA should be the last task. Your article is not really about accessibility but screen reader accessibility/ARIA. Also, know that ChromeVox is the least used screen reader (and results between screen readers can vary a lot).

  3. Hi Andrew,

    Props to you for demonstrating hands-on effects of making a common use of the web more accessible. I enjoyed reading this. Given what you’ve learned, I’d like to see you write something similar on responsive web design specifically for people with low-vision. I’m working on a product called sitecues (http://sitecues.com/) to make any website easier to read and use – so I’m particularly interested in font styles, what does/doesn’t work on mobile, etc.

    ~Seth

  4. Couldn’t agree more that accessibility needs to be considered as just another part of any web design/development project. ARIA can be especially tricky, but it is very important, so it’s always great to see these types of articles on ALA.

    I do have some reservations about your particular ARIA example.

    First, I think there’s a problem with relying on Chromevox with this example because Chromevox has a unique relationship with Chrome and the way it makes the DOM, page content and functionality accessible, as compared with the two most popular screen readers in use, JAWS and NVDA. This is not always a problem with demos like these, but it becomes one when we look at the effect of role=”application”.

    In the first pass of the Robot Shopper demo, things actually work pretty well already with Chromevox and Chrome. Arrowing from product to product, Chromevox reads all the content in each product SECTION element, that is, the product’s name, price and details. With NVDA in Firefox, on the other hand, the use of role=”application” on the BODY element forces NVDA into application mode, and so the name, price and details text content associated with each product is not available unless the user manually changes to browse mode. With JAWS in IE, things are even weirder: everytime you arrow to another product, JAWS starts reading all the content from the beginning of the page. These are known pitfalls of role=”application”, but they get no real attention in the article.

    In the second pass of the demo, things are much improved for JAWS in IE, and especially for NVDA in Firefox: access to the product name, price, and details is much more straightforward. In fact, when arrowing from product to product, NVDA in Firefox announces all the content for each product. This doesn’t appear to be a result of any of the ARIA or other fixes discussed in the article. Instead, from what I can tell, it’s because role=”list” has been added to the DIV wrapping the four product SECTIONs, and role=”listitem” to each of the SECTIONs themselves. As such, Firefox identifies the DIV as a list in the accessibility API through which NVDA interprets the page. Similarly, each SECTION is considered a list item, and as such each SECTION’s entire contents comprise the accessible name for that SECTION as represented in the accessibility API. That is how Firefox and the accessibility API deal with list items. In turn, for each product SECTION cum list item, NVDA reads out all of its content to provide a really helpful user experience.

    Some discussion of why role=”list” and role=”listitem” are used and what effect they have in the context of role=”application”, or of other ways to make the non-interactive text content associated with each product easily accessible for a screen reader when it’s in application mode, would have been useful. As it is, I think there’s a danger that readers might walk away from this article thinking that all they need to do to make a widget accessible to screen readers is apply role=”application” and some custom keyboard actions.

  5. @Jason Kiss

    Thank you for the excellent breakdown of my example’s peculiarities. As you stated (and as I mentioned in the article), not all screen readers behave identically. What worked extremely well in ChromeVox was, at times, completely broken in JAWS and/or NVDA. For my Robot Shopper example, adding the application role seemed to level the playing field between screen readers. The sole reason for my use of it on the body was to allow arrow navigation directly from page load. However, my intention was not to decree the automatic insertion of this role in the body element for all new web projects. My explanation of this role could have been better.

    As for the inclusion (and lack of explanation) for role=”list” and role=”listitem”, this was an unfortunate oversight. Having experienced the same issues you encountered in the first pass, I was experimenting with various fixes. Added these roles to the markup, as it turned out, was the fix I was after—unfortunately, I thought the fix had come from a different change and the new roles were left undocumented.

    Thank you for pointing these things out to everyone.

  6. @Web Axe

    In general webdev workflow, ARIA should be the last task.

    For complex web apps where the initial ARIA state is being set in the HTML, changed through JavaScript, and used by CSS for crucial display transitions/animations, I don’t see how adding ARIA last makes much sense. Adding ARIA last would mean purposely making all these code adjustments after things are up and running and non-semantic. Why not just do it right the first time?

  7. I agree with the sentiment from WebAXE, that ARIA is “last”, but only because the idea is you use the correct HTML element in the first place (which should then get you the needed roles without doing it by hand) and only *then* diving into ARIA.

    However, working in e-commerce, it’s simply a fact that any reactive e-commerce setup is going to have lots of thingies that DoStuff which have no associated HTML semantics, and lots and lots of Buttons that DoStuff depending on who they’re sitting next to or what section of the page their in, and lots of data-foo attributes.

    So for what I do all day, and for examples like the above (a shopping cart), ARIA actually does need to be used pretty much right off the bat. Yes, with good HTML, but not first one and then the other, but both of them together as you build, just as you would have your alt text at the very moment you write your image code.

    And because e-commerce is an example where ARIA is necessary and shines, I like the idea of this article using e-commerce as an example. It would be very nice not to have the application role in the article, as I think back to my time looking at code examples on A List Apart years ago when learning things like writing bits of Javascript. Yeah, those pages got re-opened just to grab code. People will do that here too: code examples are what makes articles like the ones here nice. So for this reason I’d change stuff in the code above as edits.

    Chrome Vox is the One Very Strange Child Who Insists On Doing Things Completely Different From Everyone Else. Not that it’s got low usage, but that it doesn’t use the DOM the way every other screen reader does (uses Javascript to do everything in-browser, as opposed to other readers who interface with the computer’s applications also outside the browser).

  8. Hi Andrew,
    thanks for this article! I do applaud the level of detail you provide and the step by step guidance you provide to making this shopping cart more accessible.

    I do have some issues with some of the semantics, though. Jason and others have already pointed out a few questions/problems with this example. But I would like to draw attention to the start of the first example, the role “application” you are using. Two years ago, I wrote a lengthy article about role “application”, and that special care should be taken when using it, and when not. The most important point: The role “application” takes away all means of getting to document details. While you are right in stating that you are conscious about keyboard navigation, and your aria-labels and other techniques provide semantic info for most of the controls, I can show you an example where this falls on its head right in your example. 🙂 And that is in the shopping cart dialog. Role “dialog” is an implicit role “application”. And while you can nicely tab to the buttons, a screen reader user using JAWS or NVDA has no easy access to the items in the cart. The quantity, individual and total prices are not keyboard navigable, and neither is the total of the cart. One has to use cumbersome object navigation (NVDA) or touch cursor (JAWS 15 and above) navigation to actually get to this info, because it is not available otherwise. And that is because roles “application” and “dialog” take away all standard browsing functionality AKA the virtual cursor. The solution to this problem is to either do away with that “dialog” role, and allow standard navigation, and just set the focus when the cart becomes visible, which will redirect virtual cursors, too. Or make the table items tabable, too, so users can get to the relevant info more easily.

    Second issue: aria-hidden. Yes, this is a red cloth for me, and your example plainly shows that aria-hidden is absolutely not necessary here. Use display: none; and display: block; or whatever for your dialog, and that will hide and show the dialog for screen reader users without having to resort to the inconsistent and error-prone aria-hidden.

    Third issue: role “listitem” is the wrong role for a container with role “listbox”. “listitem” corresponds to a html:li element, which is not by definition an interactive control. The correct role to use is role “option”. With a parent with role “listbox”, this acts similar to a html:select with size > 1, and is exactly what you want here. With that, screen readers like JAWS and NVDA will automatically allow forms or focus mode on the listbox, which obsoletes your need for role “application”, and will make all the rest of the document available. And that, btw, includes the nice landmark roles you put there, which are also not used by screen readers that rely on a virtual cursor if you force them into application mode. 🙂

    Sorry for being so blunt, but your article touches on two of my favorite “do not use” subjects within WAI-ARIA: role “application” & friends, and aria-hidden.

  9. Great article, but why start here? The accessibility of the interface with the keyboard is only a small part of WAI-ARIA. Why not begin with just making the information of a page more accessible by putting the content on top of the source document? Most big websites out there don’t even bother. Or upholding the pragma of only one

    per page. Or the order of headers (h1,h2,h3.. in that order). Or the tenacious use of “_blank” in an anchor for opening another tab. Or opening in another tab alltogether. Skiplinks? Scripted menus? I could go on and on.

    Blunt mode: As long as (the larger) corporations are not penalized by law because of a non-accessible website, this kite will never fly. Why?
    Making a website or an app accessible means it costs more money and companies will not pay more for something that is not directly related to profit. They just go to the cheapest developer.
    I don’t say I like it but it is the truth, corporations do not engage in ‘no profit yielding’ social commitment.

    By the way, I would advise anyone NOT to use new HTML5 tags. Until HTML5 is an official standard by terms of the W3C it is not good practice and unneccesary to implement it. Just wait to 2018 or so, when HTML5 (at the soonest) could be made a full-fledged W3C standard.

  10. By the way, I would advise anyone NOT to use new HTML5 tags. Until HTML5 is an official standard by terms of the W3C it is not good practice and unneccesary to implement it. Just wait to 2018 or so, when HTML5 (at the soonest) could be made a full-fledged W3C standard.

    This is not useful advice for a number of reasons.

    Developers can and do use features when they are implemented in browsers (i.e. supported). Interoperable implementation is a much better metric to base feature use on. It is factually incorrect. HTML5 is expected to become a W3C Recommendation in Q4 2014 (i.e in about 6 months) not 2018. The W3C itself uses HTML5 in its sites.
    Nowhere does the W3C state that HTML5 features should not be used until HTML5 becomes a Rec.

  11. Most important, this hard work will yield a more usable product for all customers and a more readable codebase for developers.
    Accessibility is not a extra work, she ask for quality and i think all we take advantage. Who would accept a rough job from a plumber? Why for web works have to be different?

  12. In addition to Steve Faulkner’s advice above it also bears mention that part of the W3C standards process (depending on the specific charter) is to have working implementations in the wild. This means browsers must support it and developers must use it.

  13. Oy. I found this article very informative as a foundation for accessibility specifically geared toward web applications (rather than just web pages).

    What I found distressing is the comments and unwarranted criticism coming from clearly newly experienced accessibility people. With each new web technology that comes out (not that accessibility or ARIA is new) there are always arrogant people that seem to be bent on the notion of doing it the “right” way. In other words – their way.

    Like many things in web design and development… there is no “right” way to do accessibility. In fact, W3C is quite clear on this fact:

    “Techniques are informative—that means they are not required. The basis for determining conformance to WCAG 2.0 is the success criteria from the WCAG 2.0 standard—not the techniques.

    W3C cautions against requiring W3C’s sufficient techniques. The only thing that should be required is meeting the WCAG 2.0 success criteria.”

    In other words, it doesn’t matter HOW you get accessibility done… it’s just that it works and works well. Start with ARIA… end with ARIA… it doesn’t matter. Just make sure it works and meets the success criteria.

    The biggest lesson I’ve learned is that it doesn’t matter how good you think you are… if a visually impaired person can’t use your site it doesn’t matter what technique you use.

    In other words, USER TESTING. It’s more important than any technique your arrogant, non-visually impaired geek brain can come with. And user-centered user-testing is a key part of good UX.

    I know appeals to authority aren’t considered good arguments in a debate. But, I’m going to use one here: As a someone who’s been designing and developing web sites and web applications professionally for almost 20 years (and over 10 years as an online communications amateur before that) and who specializes in accessibility … I’m going to tell you there is NO “right” way to do anything and it could always be done better. Bottom line: It just has to work well. Always user test. Even the biggest know-it-all will be brought down by someone who tries to use their software and fails.

  14. The point I tried to make (rather bluntly in retrospect) is that HTML5 is all good and well, learning the how and why to XHTML and basic XML is the foundation on which you build. XHTML5 is in this way an extension of my possibilities on the web. But the logic in it is the same: It’s better for data if you do it right.
    For example, take this website, the tags in the code contain plain text, not

    blocks. It is not proper XHTML5 coding and I am rather positively convinced that there is a reason behind it to do so, be it for SEO or for speed (both confirmed by yours truly).

    Hey, I love HTML5 and it’s new features, I use it, everybody uses it. But I always (well, not always) remember that the internet is based on information in the first place, so that is where my attention goes out to. I can recommend a short XML Basic simple class to any starting webdeveloper.

Got something to say?

We have turned off comments, but you can see what folks had to say before we did so.

More from ALA

I am a creative.

A List Apart founder and web design OG Zeldman ponders the moments of inspiration, the hours of plodding, and the ultimate mystery at the heart of a creative career.
Career