Frontends com Vibecoding · Capítulo 8 de 30 · 18 min
Projetar menus, popovers e modais acessíveis
Você sai com uma entrega que integra entrada, foco, escolha, fechamento e retorno.
Este capítulo faz parte do curso gratuito Frontends com Vibecoding. Para marcar como concluído e salvar o progresso, abra este capítulo na página do curso.
Um menu com o papel role="menu" aplicado à navegação comum do site faz quem usa leitor de tela ouvir instrução de seta onde só o Tab funciona. A pessoa fecha a página sem avisar. O engano nasce da palavra menu, que tem quatro sentidos e só um deles é navegação: a lista de links entre páginas do site.
Editar ou excluir pede menu de ação, com teclado próprio. Mostrar e esconder um bloco pede um botão que declara se está aberto. A barra fixa de aplicativo de computador quase nunca cabe num site. O agente (a ferramenta de IA que escreve o código) confunde os quatro ao copiar o padrão errado.
Troque todo rótulo genérico por um que diz o destino: em vez de Continuar, escreva Criar conta grátis; em vez de Enviar, escreva Baixar o PDF. Essa clareza é o aroma de informação, a pista que o link dá, antes do clique, sobre o que vem depois. Rótulo vago é a forma mais barata de perder o visitante.
Escolha o propósito primeiro e a forma depois, pelo volume de itens, como no quadro abaixo. A barra do topo leva de cinco a sete links, a lista suspensa até oito itens por categoria, e o mega menu entra acima de 50 páginas. Um catálogo de 40 categorias já passa desse limite: a barra sozinha vira fila cortada no celular.
As formas de navegação, o volume que cada uma aguenta e o preço que cobra
| Forma | Quando ela cabe | O preço que ela cobra |
|---|---|---|
| Barra horizontal no topo | Navegação principal, de cinco a sete links | Alvo de toque de 44 px; não pode sumir ao rolar |
| Lista suspensa | Categoria com até oito itens | Não aninhe além de dois níveis: o terceiro ninguém encontra |
| Mega menu | Site com mais de 50 páginas | Precisa de atraso ao passar o mouse, senão fecha antes da hora |
| Gaveta com ícone de três traços | Navegação secundária no celular | Esconde demais; ação frequente não pode morar só aí dentro |
| Barra inferior no celular | De três a cinco seções principais | Fica na zona do polegar; cada item com 44 px ou mais |
| Barra lateral vertical | Painel e produto por assinatura | Precisa de modo estreito recolhido e grupos com nome claro |
Escolha primeiro o propósito do menu; só depois escolha a forma nesta tabela, pelo volume de itens.
Fonte: Quadro do autor, com os padrões de acessibilidade seguindo a norma internacional de interface.
O clique no menu tem métrica formal: o INP (Interaction to Next Paint, o tempo entre o toque e a próxima pintura da tela). É um dos Core Web Vitals do Google. A faixa boa vai até 200 milissegundos, apurados no 75º percentil das visitas reais e separados entre celular e computador (Google, web.dev, agosto de 2026).
Menu que abre com atraso perceptível reprova a página nessa métrica, e a causa mais comum é script demais no clique.
A Speculation Rules API (a regra que manda o navegador pré-carregar a próxima página antes do clique) faz o destino abrir na hora. Peça para os links do menu principal. Link que executa ação, como sair da conta, fica de fora: o pré-carregamento dispararia a ação sem clique. Sem suporte, o navegador ignora a regra e segue como sempre.
Aba, filtro e página vivem na URL, nunca só na memória da tela. É o endereço que a pessoa copia para o WhatsApp, o botão de voltar e o resultado que sobrevive ao recarregamento.
Em React (a base mais usada para montar telas em pedaços), a biblioteca nuqs, o pacote pronto que liga esse estado à URL, resolve. Sem React, um formulário que envia pela URL faz o mesmo.
Rolagem infinita quebra as três garantias: sem página na URL, o Google indexa só a primeira carga e quem navega por teclado nunca chega ao rodapé. Onde SEO (a visibilidade da página nos buscadores) e acessibilidade importam, use paginação ou um botão de carregar mais que grava a página na URL.
O menu de ação responde a cinco movimentos de teclado, e o agente costuma entregar só o clique. As setas movem o foco (o destaque de qual item está selecionado) entre os itens e voltam ao primeiro depois do último.
Home e End saltam para as pontas e uma letra pula para o item que começa com ela. Enter aciona; Esc fecha e devolve o foco ao botão que abriu. O defeito só aparece quando alguém testa sem mouse.
Se a figura ultrapassar a área visível, deslize para os lados. Pelo teclado, foque a figura e use as setas.
Quatro requisitos fecham o menu de celular. A gaveta aberta pelo ícone de três traços prende o foco dentro do painel. Ela fecha com Esc ou com um toque fora dela e devolve o foco ao botão que abriu.
Ação que a pessoa usa toda semana não mora só dentro da gaveta, porque quem não vê a lista esquece que ela existe. Uma barra inferior com três a cinco seções, na zona do polegar, resolve.
Cada item segue a régua de alvo por norma. O piso é 24 por 24 pixels CSS, critério 2.5.8 do WCAG 2.2 (a norma internacional de acessibilidade) no nível AA. O nível AAA e a diretriz da Apple pedem 44 px. O Android pede 48 dp, o pixel independente de densidade. Peça 48 e passa nas três.
Conte os itens da navegação principal e escolha a forma pelo quadro. Acima de sete links na barra, agrupe categorias ou peça mega menu; no celular, leve as três a cinco seções mais usadas para a barra inferior. Ação semanal escondida só na gaveta some da cabeça de quem não a vê.
Leia cada rótulo isolado do resto da tela: Ver planos e preços nomeia o destino, Saiba mais não diz nada sozinho. Peça a lista de links, o INP e o pré-carregamento num pedido só, deixando de fora o link que executa ação, como sair da conta.
Percorra depois o menu de ação e a gaveta sem tocar no mouse. Chegue ao botão pelo Tab, abra com Enter, cicle com as setas e feche com Esc, conferindo que o foco volta ao botão que abriu.
Aplique um filtro numa categoria e cole a URL numa aba anônima. Sem o mesmo resultado na tela, o link que a cliente compartilha volta vazio.
Menu, popover e modal sem uma linha de script
Menu suspenso, popover (a caixinha que abre presa a um botão), janela modal e carrossel deixaram de exigir JavaScript próprio. O navegador entrega abertura, fechamento ao clicar fora, posicionamento e indicadores por atributo de HTML e regra de CSS.
Cada script para abrir e fechar uma caixa vira manutenção que a empresa assume sem precisar. O ganho de trocar esse script por atributo nativo aparece na revisão do que o agente (a ferramenta de IA que escreve o código) entrega pronto.
O atributo popovertarget num botão abre a caixa, e o clique fora fecha sozinho. O elemento dialog com closedby faz o mesmo com a janela modal. A Invoker Commands API (o par commandfor e command) abre e fecha pelo atributo, sem JavaScript.
O elemento dialog é Baseline Widely Available desde setembro de 2024. A Popover API é Newly Available desde 27 de janeiro de 2025 e, pela régua de 30 meses de estabilidade, só vira Widely Available em julho de 2027. O atributo closedby seguia sem suporte no Safari em agosto de 2026.
Os comandos por atributo rodam em Firefox, Chrome e Edge. Sem suporte, o botão de abrir fica inerte, e entra o único script aceitável: o mínimo que chama showModal e close. A armadilha é a janela sem botão de fechar visível, porque quem usa leitor de tela fica preso nela, com ou sem closedby.
:has() (o seletor que faz o pai reagir ao que tem dentro) marca o item ativo do menu-pai sem script: a barra muda quando um link carrega aria-current. Ele é Baseline Widely Available desde junho de 2026, e sem ele os itens seguem legíveis, só sem o destaque automático.
O anchor positioning (o posicionamento ancorado nativo) prende a caixa ao botão que a abriu, inclusive o painel largo de um mega menu. O Firefox 147 fechou o conjunto de navegadores, e o recurso entrou em Baseline Newly Available em 13 de janeiro de 2026.
Peça o Floating UI, a biblioteca que calcula posição por JavaScript, só enquanto o painel de visitas mostrar navegadores anteriores a essa data. Sem âncora, a caixa abre no centro da tela e continua utilizável.
O select estilizável nativo, o customizable select, está em desenvolvimento no CSSWG (o grupo que define o CSS). Enquanto ele não chega, o agente reimplementa a lista à mão, e daí nasce a maior parte do script de menu.
O plano B mais barato é o select nativo sem enfeite: acessível e com o teclado certo no celular.
Carrossel simples também dispensa JavaScript. Os pseudoelementos ::scroll-marker e ::scroll-marker-group desenham os indicadores de posição a partir da própria rolagem, com cobertura perto de 69%. Sem suporte, a faixa rola por toque e por encaixe, só sem os pontinhos.
Quando o carrossel precisa de mais, o Embla resolve com cerca de 4 kB. O Swiper, com cerca de 40 kB, só entra quando o efeito 3D pronto justifica dez vezes o peso.
A animação de entrada é nativa. O @starting-style anima a abertura do popover e da janela. A View Transitions same-document (a troca animada entre dois estados da mesma página) vem antes de biblioteca de transição.
Ela entrou em Baseline Newly Available em 14 de outubro de 2025, com o Firefox 144 fechando o conjunto. Sem suporte, a troca acontece seca e ninguém perde conteúdo.
O critério de decisão é o navegador do seu público, lido no painel de visitas. Recurso nativo quando a maioria do acesso vem de navegador atualizado e o plano B da tabela cumpre a função; biblioteca só onde o plano B quebra. Papel de menu de aplicativo numa navegação simples continua errado, com ou sem script.
Se a figura ultrapassar a área visível, deslize para os lados. Pelo teclado, foque a figura e use as setas.
Cada recurso nativo, onde já roda e o que acontece sem suporte
| Recurso | Navegador | Plano B sem suporte |
|---|---|---|
| :has() no item ativo | Baseline Widely Available desde junho de 2026 | Itens legíveis e clicáveis, sem o destaque automático |
| Anchor positioning | Baseline Newly Available em 13 de janeiro de 2026; Firefox 147 fechou o conjunto | Caixa abre no centro da tela; Floating UI enquanto o público não cobre |
| dialog, closedby e Invoker Commands | dialog Widely Available desde setembro de 2024; closedby sem Safari em agosto de 2026 | Botão inerte: script mínimo com showModal e close |
| Popover API | Baseline Newly Available em 27 de janeiro de 2025; Widely Available só em julho de 2027 | Caixa comum em fluxo, visível e utilizável |
| Customizable select | Em desenvolvimento no CSSWG | Select nativo sem enfeite, acessível e com o teclado certo |
| ::scroll-marker no carrossel | Cerca de 69% de cobertura | Faixa rola por toque e encaixe, sem indicadores; Embla com cerca de 4 kB |
| View Transitions same-document | Baseline Newly Available em 14 de outubro de 2025; Firefox 144 fechou o conjunto | Troca de tela sem animação, com o conteúdo inteiro |
Peça o nativo quando a coluna da direita cumpre a função para a fatia do seu público que ainda não tem o recurso.
Fonte: Manual de referência Recursos de frontend moderno, edição setembro de 2026 (Brasil GEO).
Peça os quatro recursos num pedido só, com o plano B de cada um escrito junto: isso evita quatro conversas separadas com o agente. Numa página de reservas: item ativo por :has(), popover ancorado com a diária, carrossel com ::scroll-marker e um dialog com botão de fechar à vista.
Antes de aprovar a entrega, percorra a tela com Tab, feche com Esc e repita no celular. Abra num navegador antigo para ver o plano B: sem o recurso novo, tudo continua visível e utilizável, só sem o enfeite.
Abra o painel de visitas e peça biblioteca, como Floating UI ou Embla, só onde essa fatia de navegador antigo ainda impede a função.
Laboratório visual: projetar menus, popovers e modais acessíveis
Entrada, foco, escolha, fechamento e retorno
Escolha um horário
Os horários demonstrativos são 14h e 16h. Nenhuma reserva será enviada.
Selecione cada estado acima. A simulação permanece em “Enviando” para permitir a inspeção; escolha “Confirmado” ou “Erro” para examinar as saídas.
Todos os capítulos de Frontends com Vibecoding
- 01Projetar o briefing, o repertório da marca e o custo de manter
- 02Dirigir o prompt e avaliar a entrega do agente
- 03Escolher a stack e projetar suas dependências
- 04Investigar uma stack e priorizar atualizações
- 05Construir a identidade com tokens, CSS moderno e DESIGN.md
- 06Construir componentes, usar o registro e publicar o catálogo
- 07Projetar uma linguagem de ícones e ilustração
- 08Projetar menus, popovers e modais acessíveis
- 09Adaptar o layout ao celular e ao contêiner
- 10Projetar movimento, assinatura e narrativas por rolagem
- 11Preparar imagens, ícones e vídeo para a web
- 12Construir profundidade e dirigir a montagem de vídeo
- 13Construir formulários e estados de interação
- 14Publicar conteúdo encontrável em vários idiomas
- 15Projetar gráficos e painéis pela decisão
- 16Dimensionar tabelas e organizar o cálculo das métricas
- 17Escolher mapas, diagramas e cenas espaciais
- 18Comparar direção visual em Stripe e Apple
- 19Avaliar busca, conversão e padrões de comércio
- 20Auditar acessibilidade e contraste nos dois temas
- 21Validar desempenho, autenticação e segurança
- 22Planejar a modernização e publicar com reversão
- 23Construir o projeto final, medir a cara de IA e revisar
- 24Comparar modelos e interpretar benchmarks
- 25Calcular custo por tarefa e definir governança
- 26Supervisionar o modelo que opera o computador
- 27Configurar ambiente, skills e o contêiner de contexto
- 28Aplicar direção de arte, UX writing e padrões de código
- 29Construir um aplicativo instalável e resiliente
- 30Produzir apresentações e PDF com critérios de entrega