Acessibilidade

Navegação por Teclado

Como tornar interfaces totalmente navegáveis via teclado.

Por que Navegação por Teclado?

A navegação por teclado é essencial para:

  • Usuários com deficiência motora: Que não podem usar mouse
  • Usuários de leitores de tela: Que navegam principalmente por teclado
  • Usuários que preferem teclado: Para maior eficiência
  • Conformidade WCAG: Requisito básico de acessibilidade

Ordem de Tabulação

Ordem Natural

A ordem de tabulação deve seguir a ordem lógica do conteúdo:

<!-- ✅ Ordem lógica -->
<form>
  <label for="nome">Nome</label>
  <input type="text" id="nome">
  
  <label for="email">Email</label>
  <input type="email" id="email">
  
  <button type="submit">Enviar</button>
</form>

Evite tabindex Positivo

<!-- ❌ Ruim - tabindex positivo quebra a ordem natural -->
<button tabindex="1">Botão 1</button>
<button tabindex="2">Botão 2</button>
<button tabindex="3">Botão 3</button>

<!-- ✅ Bom - ordem natural -->
<button>Botão 1</button>
<button>Botão 2</button>
<button>Botão 3</button>

Remover da Ordem

Use tabindex="-1" para remover elementos interativos da ordem de tabulação quando necessário (mas ainda permita acesso programático):

<!-- Elemento removido da ordem de tab, mas acessível via JavaScript -->
<div tabindex="-1" id="modal">Conteúdo do modal</div>

Indicadores de Foco

Foco Visível

Sempre torne o foco claramente visível:

/* ✅ Estilo de foco visível */
button:focus,
a:focus,
input:focus {
  outline: 2px solid #0066cc;
  outline-offset: 2px;
}

/* Para navegadores modernos */
button:focus-visible {
  outline: 2px solid #0066cc;
  outline-offset: 2px;
}

Elementos Interativos

Use Elementos Nativos

<!-- ❌ Ruim -->
<div onclick="handleClick()">Clique aqui</div>

<!-- ✅ Bom -->
<button onclick="handleClick()">Clique aqui</button>

Elementos Customizados

Se precisar criar elementos interativos customizados:

<!-- Div que funciona como botão -->
<div 
  role="button"
  tabindex="0"
  aria-label="Fechar modal"
  onclick="closeModal()"
  onkeydown="handleKeyDown(event)">
  Fechar
</div>
function handleKeyDown(event) {
  // Ativar com Enter ou Espaço
  if (event.key === 'Enter' || event.key === ' ') {
    event.preventDefault();
    closeModal();
  }
}

Gerenciamento de Foco

Foco em Modais

Quando abrir um modal, mova o foco para dentro:

function openModal() {
  const modal = document.getElementById('modal');
  modal.style.display = 'block';
  
  // Mover foco para o modal
  const firstFocusable = modal.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
  if (firstFocusable) {
    firstFocusable.focus();
  }
  
  // Prevenir tabulação fora do modal
  trapFocus(modal);
}

Armadilha de Foco (Focus Trap)

function trapFocus(element) {
  const focusableElements = element.querySelectorAll(
    'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
  );
  
  const firstElement = focusableElements[0];
  const lastElement = focusableElements[focusableElements.length - 1];
  
  element.addEventListener('keydown', function(e) {
    if (e.key === 'Tab') {
      if (e.shiftKey) {
        // Shift + Tab
        if (document.activeElement === firstElement) {
          e.preventDefault();
          lastElement.focus();
        }
      } else {
        // Tab
        if (document.activeElement === lastElement) {
          e.preventDefault();
          firstElement.focus();
        }
      }
    }
    
    // ESC para fechar
    if (e.key === 'Escape') {
      closeModal();
    }
  });
}

Retornar Foco

Ao fechar um modal, retorne o foco para o elemento que o abriu:

let previousFocus = null;

function openModal() {
  previousFocus = document.activeElement;
  // ... abrir modal e mover foco
}

function closeModal() {
  // ... fechar modal
  if (previousFocus) {
    previousFocus.focus();
    previousFocus = null;
  }
}

Atalhos de Teclado

Atalhos Globais

document.addEventListener('keydown', function(e) {
  // Ctrl/Cmd + K para busca
  if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
    e.preventDefault();
    openSearch();
  }
  
  // ESC para fechar modais
  if (e.key === 'Escape') {
    closeAllModals();
  }
});

Atalhos com aria-keyshortcuts

<button 
  aria-keyshortcuts="Ctrl+K"
  onclick="openSearch()">
  Buscar (Ctrl+K)
</button>

Adicione links para pular conteúdo repetitivo:

<a href="#main-content" class="skip-link">
  Pular para conteúdo principal
</a>

<header>
  <!-- Navegação -->
</header>

<main id="main-content">
  <!-- Conteúdo principal -->
</main>
.skip-link {
  position: absolute;
  top: -40px;
  left: 0;
  background: #000;
  color: #fff;
  padding: 8px;
  text-decoration: none;
  z-index: 100;
}

.skip-link:focus {
  top: 0;
}
<nav aria-label="Menu principal">
  <ul>
    <li>
      <button 
        aria-expanded="false"
        aria-haspopup="true"
        onclick="toggleMenu(this)">
        Produtos
      </button>
      <ul role="menu" hidden>
        <li><a href="/produtos/1" role="menuitem">Produto 1</a></li>
        <li><a href="/produtos/2" role="menuitem">Produto 2</a></li>
      </ul>
    </li>
  </ul>
</nav>
function toggleMenu(button) {
  const menu = button.nextElementSibling;
  const isExpanded = button.getAttribute('aria-expanded') === 'true';
  
  button.setAttribute('aria-expanded', !isExpanded);
  menu.hidden = isExpanded;
  
  if (!isExpanded) {
    menu.querySelector('a').focus();
  }
}

// Fechar com ESC
document.addEventListener('keydown', function(e) {
  if (e.key === 'Escape') {
    const openMenu = document.querySelector('[aria-expanded="true"]');
    if (openMenu) {
      toggleMenu(openMenu);
      openMenu.focus();
    }
  }
});

Testando Navegação por Teclado

Checklist de Teste

  1. Tabulação: Pressione Tab e verifique se a ordem é lógica
  2. Shift+Tab: Navegue para trás e verifique a ordem reversa
  3. Enter/Espaço: Ative botões e links
  4. Setas: Navegue em menus e listas
  5. ESC: Feche modais e menus
  6. Foco visível: Sempre veja onde está o foco
  7. Sem armadilhas: Não fique preso em nenhum elemento

Ferramentas

  • Navegação manual: Teste apenas com teclado
  • Screen readers: Teste com NVDA, JAWS ou VoiceOver
  • Lighthouse: Inclui testes de navegação por teclado
  • axe DevTools: Detecta problemas de navegação
A melhor forma de testar navegação por teclado é desligar o mouse e tentar usar sua aplicação apenas com o teclado.