Exemplo de menu mobile (responsivo) acessível

Postado em 22/07/2016 por Luiz Volso

Com o crescimento do uso de smartphones para acesso à conteúdo online, desenvolvedores precisam adaptar seus documentos HTML às novas tendências. Assim surgiu o design responsivo e com ele o menu mobile, aquele que fica no cantinho superior da tela e na grande maioria das vezes tem o famoso ícone das três barrinhas empilhadas.

Esse artigo demonstra uma forma (dentre várias possíveis) de criar um menu desses com um diferencial: acessível pelo teclado.

Marcação HTML do Menu

Primeiramente a marcação HTML do menu:

<nav>
  <h2 aria-haspopup="true" tabindex="0" id="abremenu">Menu Principal</h2>
  <ul class="menu" aria-expanded="false" aria-hidden="true">
    <li role="menuitem"><a href="#">Início</a></li>
    <li role="menuitem"><a href="#">Serviços</a></li>
    <li role="menuitem"><a href="#">Produtos</a></li>
    <li role="menuitem"><a href="#">Contato</a></li>
  </ul>
</nav>

O título <h2> vai servir de gatilho para abrir o menu, foi adicionado um ID para facilitar com os seletores jQuery e um tabindex="0" que serve para o elemento ser adicionado ao fluxo de navegação quando utilizada a tecla TAB.

O atributo aria-haspopup="true" indica ao agente de usuário que existe um submenu adiante, o menu possui aria-expanded="false" indicando que está retraído e também aria-hidden="true" indicando que ele está escondido. Os itens li possuem o atributo role="menuitem" indicando que cada ítem faz parte do menu.

Vamos adicionar uma folha de estilos CSS de reset:

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css">

Adicionamos alguns estilos CSS para configurar os estados iniciais dos elementos do menu e também adicionar algumas melhorias visuais:

body{
  font-family: 'Verdana';
}
header{
  background-color: #ECECEC;
  position: relative;
  height: 80px;
}
nav h2{
  text-indent: -9999px;
  background-color: #666;
  width: 60px;
  height: 60px;
  margin: 10px;
  float: left;
  cursor: pointer;
}
.menu{
  position: absolute;
  left: -200px;
  top: 80px;
  width: 200px;
  background-color: #ECECEC;
}
.menu[aria-hidden="true"]{
  display: none;
}
.menu a{
  display: block;
  padding: 0 24px;
  line-height: 50px;
  font-size: 1.5em;
  text-decoration: none;
  color: #666;
}
.menu a:hover, .menu a:focus{
  background-color: #666;
  color: #ECECEC;
}

O menu precisa funcionar obrigatoriament com o mouse e teclado, quando forem adicionados os códigos jQuery na camada de comportamento isso deve ser levado em consideração. Primeiramente criamos duas funções, uma para abrir e outra para fechar o menu, ambas utilizam o animate do jQuery para posicionar o menu fora da tela e trazê-lo de volta. Além a animação, quando o menu é aberto, alteramos aria-hidden="true" para false e aria-expanded="false" para true.

function fechaMenu(){
  $('.menu').animate({ 'left':'-200px' }, 200, function(){
    $(this).attr({'aria-hidden': true, 'aria-expanded': false});
  });
}
function abreMenu(){
  $('.menu').attr({'aria-hidden': false, 'aria-expanded': true}).animate({ 'left' : 0 }, 200);
}

Agora a parte do jQuery que vai controlar a abertura e fechamento do menu:

$('document').ready(function(){
  // Verifica se o menu está aberto, se não, abre, tanto no clique quanto no foco
  $('#abremenu').on('click focusin', function(){
    if($('.menu').attr('aria-hidden') == 'true'){
      abreMenu();
    }
  });
  // Na perda de algum elemento dentro de nav, fecha-se o menu
  $('nav').focusout(function(){
    setTimeout(function(){
      var el = $(document.activeElement);
      if($('nav').find(el).length == 0) {
        fechaMenu();
      }
    }, 100);
  });
});

Tanto o click quanto o focusin servem para abrir o menu. Em focusout há uma verificação para detectar se o elemento que tem o foco atual está contido no menu, se não, este é fechado. Foi adicionada uma função de timeout devido a um pequeno atraso que o navegador tem para trocar o elemento que possui o foco.

Para encerrar, mais algumas pequenas melhorias no comportamento. Fechar o menu clicando fora dele e também pressionando a tecla ESC.

$('document').ready(function(){
  // Para fechar o menu caso o usuário clique fora dele,
  // O clique no documento todo é monitorado, fechando o menu
  $(document).click(function(){
    fechaMenu();
  });
  // Paramos a propagação do clique caso este seja dentro do menu
  $('nav').click(function(){
    event.stopPropagation();
  });
  // Fechar o menu caso a tecla ESC seja pressionada
  $(document).on('keyup', function (e){
    if (e.keyCode === 27) {
      fechaMenu();
    }
  });
});

Espero que o artigo seja útil, deixe suas dúvidas ou opniões nos comentários abaixo e sucesso!

Um abraço.