Be Wary of Nesting Roles

Screen capture of a button element with a hyperlink nested inside. Button text is “LOL” and link text is “wut?” As a web developer, you may take it for granted that you cannot nest a hyperlink. I mean, you can nest a hyperlink, but more likely than not you already know how problematic that can be — and not just because the validator will kick that back as an error.

A little less obvious to some is that you cannot nest a button, specifically the <button> element. The HTML specification is clear on this when describing the content model for the <button> element: there must be no interactive content descendant.

The <a> element is a little more explicit, specifically noting that <a> cannot also be a child of itself: there must be no interactive content or a element descendants.

What Is Interactive Content?

Conveniently, the HTML specification provides a handy list of interactive elements (which cannot live in <a> nor <button> nor each other):

Interactive content is content that is specifically intended for user interaction.

Can You Tab to It?

But wait. There is more. You can make something interactive with this one simple, potentially problematic, trick:

The tabindex attribute can also make any element into interactive content.

But you would never put tabindex on an element for no reason, right? That would be silly.

Polyfills FOR THE W…oah.

Sometimes the application of polyfills (or even some libraries) can have an unintended consequence.

This is not new information, as evidenced by a 2012 post that discusses polyfills to make <details> (an interactive element) and <summary> (not an interactive element) behave as promised on the tin.

Go ahead and use details and summary (with an appropriate polyfill). If you must have one or more links nested in the summary, arrange them so that they are not the first things within the summary element. And even if you manage this, you should recognise that:

  • these links still won’t be in any way clearly identifiable to users of VoiceOver in Safari;
  • these links still won’t be available to JAWS in FF or Window-Eyes in both FF and IE when these screen readers are in Browse Mode; and,
  • JAWS users with IE will have a less than ideal experience reading the summary‘s content.

The thing is, how many developers really look at the final, rendered HTML once a polyfill is done chewing through the markup? How many understand just what is going on? How many test the output in assistive technology?

But it does not end there…

And Now the ARIA

Often when you see tabindex in use, it is paired with an ARIA role. Usually that is done to convert a div or span into a button (via role="button"). A terrible, Frankenstein’s monster of a button. Often a polyfill that allows a user to click something follows the same process.

The problem is that not all developers understand that throwing a role="button" onto an element turns it into interactive content (much like adding a tabindex).

We already know this block of code is clearly wrong:


But this block may not be an obvious problem to many developers:

<div role="button">
     <div role="button">

Unfortunately, an HTML checker will not flag that as an error.

Bear in mind that while I am talking about buttons, it is not limited to just buttons. Here is an example that is probably more common:

<div role="button">
     <a href="index.html">

What to Do

Know what the code is doing and how it affects users. The validator won’t save you, and if you are not a regular user of assistive technology you may miss what is happening.

In the meantime, Steve Faulkner has just opened an issue against the ARIA spec: define nesting conformance requirements for roles when used in HTML #54

If we can get that sorted, then we can file an issue against the validator as it will now have rules to follow.

No comments? Be the first!

Leave a Comment or Response

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>