Acessibilidade7 min de leitura·Ago 2026

Hierarquia semântica de headings em SPAs

SPAs frequentemente quebram a hierarquia de headings durante navegação client-side. Veja como auditar e corrigir sem mudar o design visual.

AcessibilidadeHTML SemânticoARIASPAScreen Readers

O problema que ninguém vê, mas screen readers sentem

Em aplicações multi-página tradicionais, cada novo documento HTML começa com um `<h1>` limpo. Em SPAs, o conteúdo muda dinamicamente, mas a hierarquia de headings muitas vezes não é redefinida — resultando em `<h2>` aparecendo sem `<h1>` pai, ou `<h1>` duplicados na mesma página lógica.

Usuários de screen readers navegam por headings como se fossem uma tabela de conteúdo. Uma hierarquia quebrada é equivalente a um índice onde capítulos não têm seções — confuso e inacessível.

Como auditar a hierarquia atual

A extensão "Headings Map" do Firefox (ou Chrome) gera um outline dos headings da página atual. Execute a auditoria em cada rota da sua SPA. O outline deve ter exatamente um `<h1>` por "página lógica" e níveis em cascata sem pular: h1 → h2 → h3, nunca h1 → h3.

Outra ferramenta útil é o axe DevTools: ele detecta automaticamente hierarquias inválidas e classifica por severidade. Integre o axe ao Cypress ou Playwright para capturar regressões automaticamente.

Atenção

O maior erro: usar headings para controle visual de tamanho de texto. Se você precisa de texto "tamanho h2 visualmente" sem ser um heading semântico, use um `<p>` ou `<span>` com classes CSS. Nunca use heading level para fins estéticos.

Focus management após navegação

Além da hierarquia, SPAs precisam gerenciar o foco do teclado após cada navegação. Quando o usuário clica em um link e o conteúdo muda, o foco permanece no link — não no início do novo conteúdo.

A solução é mover o foco programaticamente para o `<h1>` da nova página (ou um elemento `<main>` com `tabIndex={-1}`) após cada transição de rota. Frameworks como Remix fazem isso automaticamente; no React Router ou router customizado, é responsabilidade do desenvolvedor.

// código
// Após mudança de rota, mova o foco para o título principal
useEffect(() => {
  const h1 = document.querySelector('h1')
  if (h1) {
    h1.setAttribute('tabindex', '-1')
    h1.focus()
  }
}, [path]) // path da sua solução de routing
P

Portodev Studio

Consultoria e engenharia digital · Ago 2026

← Ver todos os conteúdos
[ Aplicar na prática ]

Esses padrões no seu produto.

Em 30 minutos mapeamos como esses conceitos se aplicam ao contexto do seu projeto real.