vadikom / smartmenus

Advanced jQuery website menu plugin. Mobile first, responsive and accessible list-based website menus that work on all devices.
http://www.smartmenus.org
MIT License
592 stars 163 forks source link

menu toggle button not accessible (enough) #137

Open jenlampton opened 3 years ago

jenlampton commented 3 years ago

Hello and thank you for this fantastic library!

We've been using the menu heavily on many sites that need to be accessible to visitors with a wide range of differences in mobility and visual ability, so we've had our site reviewed by the Center for Accessible Technology. One of the concerns they had was with our menu toggle button, which we implemented according to an example in the smartmenus documentation.

Here's the feedback from our contact at CforAT:

Mobile hamburger menu is not coded properly. It looks like a button visually but is coded as form input checkbox.

Please change the mobile hamburger menu to be coded as an accessible accordion component. The hamburger menu should be a semantic HTML button with the screen reader text of "Menu" and then it should have the aria-expanded attribute on it. <button aria-expanded="false">Menu</button>

Refer to the following W3C specification for information on implementing accessible accordions: https://www.w3.org/TR/wai-aria-practices-1.2/#accordion

I wasn't aware of people having trouble with the checkbox, but have you received any feedback expressing similar concerns before now? And if so, have you ever attempted building the menu toggle button as an accessible accordion component?

vadikom commented 3 years ago

Hi, thanks for your feedback and question!

I have considered and tested (in CrossOver on Mac and JAWS on Windows, as far as I remember) the <button> instead of <input type="checkbox"> solution and both are completely accessible. And that makes sense when you think about it - it's like saying a form button is more accessible than a form input control. I guess it is arguable if either of them has better usability than the other, however, the <button> approach has one major drawback and it is that it cannot work in browsers that have disabled JavaScript support (while the checkbox is a CSS-only solution and works fine). And since I believe it is important the main menu links to remain fully accessible with or without JavaScript support I opted for the <input type="checkbox"> by default.

Not sure if this is the correct website of the Center for Accessible Technology https://www.cforat.org/ but it also suffers from that issue - i.e. their main navigation links are inaccessible to users on smaller screens/smaller browser windows with JavaScript support disabled (which is a due to the fact that Bootstrap uses a button instead of a checkbox by default).

Of course, it is not too complicated to replace the checkbox with a button if needed. If you prefer to do that, please let me know and I can prepare a quick demo for you (as I am thinking, I may actually add an example in the docs too so that both solutions are documented).

Cheers!

jenlampton commented 3 years ago

Thank you for taking the time to provide such a detailed and useful response.

Not sure if this is the correct website of the Center for Accessible Technology cforat.org

Yep, that's the one :)

If you prefer to do that, please let me know and I can prepare a quick demo for you (as I am thinking, I may actually add an example in the docs too so that both solutions are documented).

My preference would be to stick with the input, but I suspect my client might choose to try the button instead. I would greatly appreciate it if you would add an alternate example to the docs.

AuliArbete commented 2 years ago

Hello! I was wondering if the demo for replacing the checkbox with button is available for everyone?

Of course, it is not too complicated to replace the checkbox with a button if needed. If you prefer to do that, please let me know and I can prepare a quick demo for you (as I am thinking, I may actually add an example in the docs too so that both solutions are documented).

vadikom commented 2 years ago

Hi, sorry for the late reply but I've been quite overwhelmed with client work lately and apart from that I am now focusing mainly on the next major release of the script. Anyway, I've backported some code that you could use with the current v1.1.1. This solution uses a couple of buttons (actually, links with role="button") which allows properly focusing the toggle button via keyboard and also works when JS support is disabled (like the checkbox solution) thanks to using the :target selector.

The instructions should be paratically the same like in the docs:

https://www.smartmenus.org/docs/#menu-toggle-button

Just the code is different:

HTML:

<!-- Mobile menu toggle button (hamburger/x icon) -->
<span class="sm-toggler-state" id="sm-toggler-state-1"></span>
<div class="sm-toggler">
  <a class="sm-toggler-anchor sm-toggler-anchor--show" href="#sm-toggler-state-1" role="button" aria-label="Open main menu" onclick="return false;">
    <span class="sm-toggler-icon sm-toggler-icon--show"></span>
  </a>
  <a class="sm-toggler-anchor sm-toggler-anchor--hide" href="#" role="button" aria-label="Close main menu" onclick="return false;">
    <span class="sm-toggler-icon sm-toggler-icon--hide"></span>
  </a>
</div>

<!-- Sample menu definition -->
<ul id="main-menu" class="sm sm-blue"> ...

CSS:

.sm-toggler {
  position: relative;
}

/* Hide the toggler when not needed */
@media (min-width: 768px) {
  .sm-toggler {
    display: none;
  }
}

/* The state element */
.sm-toggler-state {
  display: none;
}

/* The anchors */
.sm-toggler-anchor {
  display: inline-block;
  color: currentColor !important;
}
.sm-toggler-anchor--hide {
  display: none;
}
.sm-toggler-state.sm-show ~ .sm-toggler .sm-toggler-anchor--show,
.sm-toggler-state:target ~ .sm-toggler .sm-toggler-anchor--show {
  display: none;
}
.sm-toggler-state.sm-show ~ .sm-toggler .sm-toggler-anchor--hide,
.sm-toggler-state:target ~ .sm-toggler .sm-toggler-anchor--hide {
  display: inline-block;
}

/* The icons */
.sm-toggler-icon {
  display: block;
  position: relative;
  width: 26px;
  height: 18px;
}

/* Show icon (hamburger) */
.sm-toggler-icon--show {
  border-top: 2px solid;
}
.sm-toggler-icon--show::before,
.sm-toggler-icon--show::after {
  content: '';
  position: absolute;
  top: calc(50% - 2px);
  left: 0;
  right: 0;
  border-top: 2px solid;
  height: 0;
  overflow: hidden;
}
.sm-toggler-icon--show::after {
  top: auto;
  bottom: 0;
}

/* Hide icon (x) */
.sm-toggler-icon--hide::before,
.sm-toggler-icon--hide::after {
  content: '';
  position: absolute;
  top: calc(50% - 1px);
  left: 0;
  right: 0;
  border-top: 2px solid;
  height: 0;
  overflow: hidden;
  transform: rotate(-45deg);
}
.sm-toggler-icon--hide::after {
  transform: rotate(45deg);
}

/* Hide the menu when not needed */
.sm-toggler-state:not(.sm-show):not(:target) ~ #main-menu {
  display: none;
}
@media (min-width: 768px) {
  .sm-toggler-state:not(.sm-show):not(:target) ~ #main-menu {
    display: block;
  }
}

JavaScript:

$(function() {
  var $menuState = $('.sm-toggler-state'),
    $menuShowButton = $('.sm-toggler-anchor--show'),
    $menuHideButton = $('.sm-toggler-anchor--hide'),
    $menu = $('#main-menu');
  if ($menuState.length && $menuShowButton.length && $menuHideButton.length) {
    $menuShowButton.click(function(e) {
      $menuState.addClass('sm-show');
      $menuHideButton.focus();
      $menu.hide().slideDown(250, function() { $menu.css('display', ''); });
      e.preventDefault();
    });
    $menuHideButton.click(function(e) {
      $menuState.removeClass('sm-show');
      $menuShowButton.focus();
      $menu.show().slideUp(250, function() { $menu.css('display', ''); });
      e.preventDefault();
    });
  }
});