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
oraria-labelledby
attributes, - the
alt
attribute if it’s an image etc., - the
title
attribute. Don’t leave it up to thetitle
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?