DEV Community

Create an accessible dropdown navigation

Lindsey Kopacz on December 05, 2018

Hover navigations are pretty simple to do without JavaScript, which is how I usually see them implemented. The HTML and CSS are pretty simple. ...
Collapse
 
equinusocio profile image
Mattia Astorino

Nice! Just note that keyboard navigation is not the only thing to consider for accessibility. You should also consider to add aria attributes and implement aria widgets.

You may find useful this resource:
w3.org/TR/wai-aria-practices-1.2/

Collapse
 
lkopacz profile image
Lindsey Kopacz

totally! I actually presented about aria stuff a while back and I feel like it would be a good extension of this blog post!

Collapse
 
lkopacz profile image
Lindsey Kopacz

Additionally, I tend to use aria as a fallback since HTML5 is so great now <3

Collapse
 
equinusocio profile image
Mattia Astorino • Edited

Aria attributes are mandatory to support screen readers. Using semantic tags is not enough ☺️

EDIT
Since people doesn't read but go for interpretation i will explain and translate my really simple statement:
If you need to provide extra informations to SR, your are obliged (so why they are mandatory) to use aria attributes. No one will save you.

Thread Thread
 
lkopacz profile image
Lindsey Kopacz

After talking with blind users (IE the ones who use screenreaders most), they tell me they prefer HTML5 and add ARIA in afterward if more context is needed.

Sometimes manual testing I learn that it's not always mandatory, but if someone is visually impaired is telling me it's not mandatory, I am going to listen to them.

A lot of times ARIA isn't mandatory, it just adds in extra context when things aren't super clear. HTML5 is built to be accessible out of the box, it's when we create advanced features is when we need ARIA.

Thread Thread
 
equinusocio profile image
Mattia Astorino • Edited

Wait :). They aren’t mandatory when you don’t need to provide more informations. But if use for example a nav element as tab widget you must tell to SR which element is selected. And you can do that through aria-states and roles. These are design patterns defined inside the WCAG guidelines and we must follow them to be WCAG AA(A) compliant.

Another example is when you have an interactive element without content (and this is not an advanced feature), you need to provide the aria-label attribute to describe the action to SR.

In a multilevel menu (pure semantic html) you must tell elements relationship to SR otherwise users won’t know where they are... even in this case you should use aria attributes and roles to avoid the UI pollution.

Btw good job with this article. 👍🏻

Collapse
 
link2twenty profile image
Andrew Bone

Do you know about focus-within?

You could do something like this and still have full keyboard navigation

.menu__item:hover .submenu,
.menu__item:focus-within .submenu {
  padding: 0.5rem 0;
  width: 9rem;
  height: auto;
  background: #eedbff;
  clip: auto;
}

Tweeked example on jsfiddle

Oh, and good to see you got the syntax highlighting working 🙂

Collapse
 
lkopacz profile image
Lindsey Kopacz

Hmm interesting. I'll check it out. What's the support like?

Collapse
 
lkopacz profile image
Lindsey Kopacz

Ugh yeah, the reason I haven't used this is because most of my clients still support IE and Edge. Me: sobs in a corner.

Thanks for showing me this though. Pretty cool to learn about how people are using pseudo-classes.

Thread Thread
 
link2twenty profile image
Andrew Bone

I'd probably still use the CSS method, I try to avoid JS where I can, if I could and have your method as a backup in case focus-within wasn't supported.

try {
  document.querySelector(':focus-within');
} catch (err) {
  console.log("focus-within is not available, using polyfill");
  focusWithinFallback(topLevelLinks);
}

function focusWithinFallback(array) {
  array.forEach(link => {
    ...
  }
}
.menu__item:hover .submenu,
.menu__item:focus-within .submenu,
.menu__item.focus .submenu {
  padding: 0.5rem 0;
  width: 9rem;
  height: auto;
  background: #eedbff;
  clip: auto;
}

This would give you support back to IE6 😀
Unparsable CSS is ignored by default.

Thread Thread
 
lkopacz profile image
Lindsey Kopacz

I'm a front-end dev, I am aware that unparsable CSS is ignored by default ;)

Thread Thread
 
link2twenty profile image
Andrew Bone

It was more for people that might read later 😜

Collapse
 
moopet profile image
Ben Sinclair

I tried this (which is admittedly a bit messy) using relatedTarget on the event to determine whether the link was headed to another menu- or submenu-item. I'm also clearing all the top level focus classes whenever the top level menu is re-focused... it seems to work.

topLevelLinks.forEach(link => {
    if (link.nextElementSibling) {
      link.addEventListener('focus', function() {
        topLevelLinks.forEach(link => {
          link.parentElement.classList.remove('focus');
        });

        link.addEventListener('blur', function(e) {
          link.parentElement.classList.remove('focus');
        });

        this.parentElement.classList.add('focus');
      });

      const subMenu = link.nextElementSibling;
      const subMenuLinks = subMenu.querySelectorAll('a');

      subMenuLinks.forEach(link => {
        const topLevelLink = link.parentElement.parentElement.parentElement;

        link.addEventListener('focus', function() {
          topLevelLink.classList.add('focus');
        });

        link.addEventListener('blur', function(e) {
          if (e.relatedTarget) {
            const targetTopLevelLink = e.relatedTarget.parentElement.parentElement.parentElement;

            if (targetTopLevelLink != topLevelLink) {
              topLevelLink.classList.remove('focus');
            }
          }
        });
      });
    }
  });
Collapse
 
nlynchbs profile image
Nicole

Hi, this is great. I found your follow up CSS-only solution using :focus-within. But wondered if you posted the follow up JS solution for traversing backwards through the menu?

Collapse
 
lkopacz profile image
Lindsey Kopacz

I haven't gotten to it yet, maybe my next blog post 🤔