The Accessible Current Page Link Conundrum

When I’m done staring with fear-induced catalepsy at the vast array of complex and overlapping app building, testing, integration and deployment tools that are quickly amassing around me, I like to take a little break and try to solve a simple problem. The “current page” link is just such a problem.

The current page

By “current page”, I mean the page you are on right now. It’s this page to you, or to me if I have the vanity to read this post after writing it. In our navigation schema we like to highlight the current page, indicating to the user where they are. A highlighted current page link is like a YOU ARE HERE indicator in a shopping mall floor map. I’m rarely there, incidentally, because I hate shopping.

The problem with “current page” links is there’s no interoperable (read: accessible) way of communicating them. Unlike links to page fragments, which are identified as “same page link” (or similar) by popular screen readers, the mechanism by which a link points to the current location is not exposed via browsers to assistive technologies. At least, not in my experience.

Class confusion

It’s perfectly simple to add a class — either manually or dynamically — to whichever link corresponds to the current page to add our highlight. Drupal adds an active class to current page links. It’s a poor name because it shares terminology with the :active state, but it does the job.

<nav role="navigation">
   <ul>
      <li><a href="/" class="active">Home</a></li>
      <li><a href="/about">About</a></li>
      <li><a href="/contact">Contact</a></li>
   </ul>
</nav>

Unfortunately, this class, as all classes, will remain uninterpreted by screen readers. Screen readers do not read classes. Classes do not climb accessibility trees. In order to provide an aural indication of context, we’ll need to try something else.

Accessible names

The accessible name of an element — its lexical content — is calculated by one of

  • the element’s text node,
  • the value of any associated aria-label or aria-labelledby attributes,
  • the alt attribute if it’s an image etc.,
  • the title attribute. Don’t leave it up to the title attribute. That’s flimsy as hell.

Unavoidably, if we want to do the right thing here and communicate the current page both visually and aurally, using an accessible name is the key. First let’s try adding some text.

The span approach

<nav role="navigation">
   <ul>
      <li><a href="/" class="active">Home <span class=”visually-hidden”>current page</span></a></li>
      <li><a href="/about">About</a></li>
      <li><a href="/contact">Contact</a></li>
   </ul>
</nav>

This is pretty good, but necessitates the hacky “hide to visual users but not to screen reader users” approach of clipping the <span> text or hiding it off screen. Because the span communicates nothing visually, we are then forced to tackle the same problem separately for visual users: We have to leave the .current class intact.

The aria-label approach

The aria-label attribute can be added to elements which are lacking a text node. Commonly, <button>s with just unicode icons or background images.


   <nav role="navigation">
       <ul>
          <li><a href="/" class="active" aria-label="current page">Home</a></li>
          <li><a href="/about">About</a></li>
          <li><a href="/contact">Contact</a></li>
       </ul>
    </nav>

I prefer this to the “shove a span in there” approach for one reason above all: You can use the aria-label attribute in a selector. By pairing the accessible name with the style you can remove the class, tidying up the HTML. More important than tidiness, though, is the creation of a mechanism whereby the visual appearance of “current page” and the aural announcement of “current page” are paired. That is, when you move the aria-label between links, you are doing it for both kinds of user.

[aria-label="current page"] {
   /* highlighted styles */
}

Roger Johansson notes an internationalization problem with this method. Should we translate the site to French, we would want to change the “current page” value of the aria-label to “page actuelle” or similar. This would break the above selector. You could change the selector to [aria-label] so it just matches the presence of the attribute — which would only be on our current link — but there’s a bigger problem with aria-label than that.

The problem

Having been testing in VoiceOver, I was fully expecting the “current page” text to be appended (or is it prepended? It doesn’t matter) to the text node — “current page home”, for example. Unfortunately, for our purposes, NVDA, JAWS and ChromeVox all override the text node with the label, leaving just “current page”. Irritatingly cryptic to most users, I would imagine…

  • Home
  • About
  • Contact
  • Current page
  • Blog

The aria-describedby approach

Unlike aria-label, aria-describedby is actually specified to append its value to the target element’s text node. I have set up a test case on CodePen where you will note that both JAWS and NVDA both read “home link current page” when focusing the “home” navigation item. The markup looks like this:

<nav role="navigation">
  <ul>
    <li><a href="/" aria-describedby="current">home</a></li>
    <li><a href="/about">about</a></li>
    <li><a href="/contact">contact</a></li>
  </ul>
  <div id="current">current page</div>
</nav>

Naturally, we hide #current with display: none. This will make it invisible to all users. Now, only one item needs translating and it’s easier because it is text which appears within the document rather than an attribute value. The aria-describedby value need not change as it just forms a relationship with #current. This is the best solution I have come up with.

aria-current

You may or may not know that WAI-ARIA tab interfaces have an aria-selected attribute which can be used to indicate the “open” tab in such an interface. “Selected tab” (or “tab selected”) is communicated whenever the selected tab is focused.

<a role="tab" aria-selected="true">My tab</a>

Note how I have omitted the class. As in our “current page” example there’s no need for it, because the presence of the aria-selected attribute can do all the work. The W3C recommend tying state to appearance in this way, in fact:

For supporting browsers, tie CSS attribute selectors to WAI-ARIA properties to reduce script

Like aria-checked and aria-pressed, aria-selected is intended to communicate the selected state of a control amongst other controls. It’s tempting to think aria-selected would be suitable for the use case I am exploring for “current” pages, but there are conceptual differences between that which is “on” or “chosen” and one’s location within a continuum. Accordingly, Léonie Watson has been discussing aria-current as a proposed standard method for indicating one’s current location within a site or step-based process. What do you think?

Remove the link?

One more approach, as suggested to me by Jonathan Schofield, would be to remove the link for the current page altogether.

<nav role="navigation">
  <ul>
    <li><strong>home</strong></li>
    <li><a href="/about">about</a></li>
    <li><a href="/contact">contact</a></li>
  </ul>
</nav>

Though perhaps a bit more of a handful styling wise, this makes sense to me. Why link to the place you’ve arrived at already? My only concern is that a <strong>, <span> or other inert element would be unfocusable, not allowing a screen reader user the context we are trying to communicate.

This is design

The next time someone asks me what web design is or categorizes me as a “visual designer”, I’m going to send them to this post. You are welcome to as well. Any other suggestions for “current page” solutions, anyone?