DEV Community

loading...

Aria Labels here we come

jdbrewerhofmann profile image JD Brewer-Hofmann ・3 min read

I recently built an e-commerce site, and I attempted to build a strong accessible foundation along the way. The project is mostly complete, but I thought it was a great opportunity to comb through the project to add aria labels where they are needed.

I hope this can serve as an introduction to adding aria labels in a React/NextJS project.

Landing Page

landing-page-one

On the landing page we have a few items to address. Using Firefox's Accessibility Inspector we can see multiple issues in the header alone.

Screen Shot 2021-04-06 at 11.35.58 AM

Burger Menu

First and foremost I want to make sure the burger menu is accessible and makes sense to everyone. Adding an aria-label easily removes our warning.

<button 
  className={ menuOpen ? "burger-btn active" : "burger-btn"}
  onClick={handleMenuToggle}
  aria-label="open main menu"
>
Enter fullscreen mode Exit fullscreen mode

But, you may notice that this button is a toggle button, so what happens when our menu is open?

Screen Shot 2021-04-06 at 11.42.25 AM

Our label informs our user that this button will "open main menu". But the menu is already open, and visually we are showing a huge X, so this is rubbish.

Using the same ternary logic I used for my button className, I can update the aria-label to change based on the state.

<button 
  className={ menuOpen ? "burger-btn active" : "burger-btn"}
  onClick={handleMenuToggle}
  aria-label={ menuOpen ? "close main menu" : "open main menu"}
>
Enter fullscreen mode Exit fullscreen mode

Now our aria label updates with our menu opening and closing. When I use voice-over I hear "open main menu, button" when I apply focus (tab to the button) and when I hit enter, I hear "close main menu" immediately. That worked better than I even though honestly.

Page Header

My site title is "C&G", which doubles as a link to the landing page, though I am not sure you'd never know that from the voice over. What I currently hear is "link, C & G". I'm going to add an aria label to this as well.

<Link href="/">
  <a aria-label="c & g home page">C<span>&</span>G</a>
</Link>
Enter fullscreen mode Exit fullscreen mode

Now I hear "link, c & g home page" when I apply focus to the element. If this link element looks strange, just know I'm using NextJS along with React.

Cart Icon

Next up is our cart icon. The code currently reads as such

<button 
  className="header-cart"
  onClick={handleCartPreviewClick}
>
  <img className="cart-vector" src="/shopping-cart-vector.png"></img>

  {context.contextCart.length > 0 ? 
  <span className="cart-count"> {context.contextCart.length}</span>
  : null }
</button>
Enter fullscreen mode Exit fullscreen mode

I immediately notice some issues. For one, my image tag has no alt attribute. While I'm fixing that, I can add a ternary aria label just like our burger menu button.

<button 
  className="header-cart"
  onClick={handleCartPreviewClick}
  aria-label={ cartPreview ? "close shopping cart preview" : "open shopping cart preview" }
>
  <img 
    className="cart-vector" 
    src="/shopping-cart-vector.png"
    alt="shopping cart" 
   />
{context.contextCart.length > 0 ? 
    <span className="cart-count">{context.contextCart.length}</span>
: null }
</button>
Enter fullscreen mode Exit fullscreen mode

That solves the same issues I had with my burger menu toggle button. Although when I use the screen reader I only receive information about the image inside the button, or the span when I am first loading the page. I'm not thrilled about that, I'd like to have the number of cart items be relayed somehow. I am going to try to add that into the button's aria label.

I attempted to interpolate my cart items length into the aria label, but that doesn't work. So I will leave that issue open for now. At least the button instructions make sense now.

Conclusion

Well addressing only the header took a while, so I will have to write another article detailing the slider on the landing page and digging further into our cart items - which I fear aren't laid out too well.

If you'd like to see the page as it stands currently or checkout out the code the links are included below.

live page
github repo

Discussion (1)

pic
Editor guide
Collapse
inhuofficial profile image
InHuOfficial • Edited

Wonderful that you are putting effort into accessibility. ❤

I had a quick look at the home page - few suggestions / quick fixes:-

Invalid WAI-ARIA

You have a couple of minor mistakes.

First the role on your <nav> is not valid. Currently it is "main menu". I think you meant "menu".

Two problems there. First that isn't the correct role in the first place (menu is for application style menus not navigation) and secondly you have now added the requirement to add arrow key navigation along with other shortcut keys!

Just remove the role - because you have used a <nav> element it already has an explicit role of "navigation" - which is the relevant role here anyway!


edit sorry I just realised this was probably just another minor mistake - was that also meant to be aria-label="main menu"? I have left the above in as you may find it interesting but I imagine the fix is to just change it from role to aria-label 😋


Second you have an aria-labelledby that is pointing to "hide product categories". I think that was meant to be an aria-label perhaps?

What you have essentially done is say "the label for this element is provided by 3 other elements. The ones with the IDs #hide, #product and #categories, read in that order!

That is how aria-labelledby works - it wants an ID (or several) of an element on the page that acts as the label.

h1

You must have a <h1> on the page to help screen reader users orient themselves.

Now although this should be visible (to help comprehension and orientation for everybody) if you design really can't accomodate it you can visually hide it with visually-hidden text (screen reader only text). You can read about that more and the recommended class properties I suggest in this StackOverflow answer I gave:

A better solution to the bootstrap "sr-only" class.

There are numerous problems with the Bootstrap "sr-only" class.

  1. First of all you will see from this discussion that a negative margin can cause issues on VoiceOver.

  2. Secondly you must account for words wrapping one per line as screen readers do not

To use it simply give the element you want screen reader users to see but nobody else the class visually-hidden and include the CSS class I linked:

<!-- this will be accessible to assistive tech (screen readers) but completely invisible on the page -->
<h1 class="visually-hidden">Shop for devices<h1>
Enter fullscreen mode Exit fullscreen mode

<main> and skip links

I may have missed it but it looks like you missed a <main> landmark. This is important as some people who use a screen reader will jump to this using shortcuts.

Along the same lines "skip links" allow people who use a screen reader to jump past the navigation on your site so they don't have to tab past it on every page.

Erroneous tabindex="-1"

Not sure why but you have a tabindex="-1" on your link that says "shop watches".

This disables the link and means it cannot receive focus for keyboard users.

If you are trying to focus it programatically you do not need the tabindex="-1" so you can remove it and still use .focus() as it is an element that can receive focus in the first place.

That is enough for now!

It is quite a broad subject so I don't want to overwhelm you. But as you can see the fixes themselves are easy, it is just the knowledge that is harder to acquire (but you will get there!).

Any questions on the above just ask and if you get stuck on a coding problem related to accessibility throw it up on StackOverflow with the tag [accessibility] - I am always lurking around there to help!