Gerador de Abas CSS

Próximo

As abas estão em toda parte, painéis de configurações, sites de documentação, galerias de produtos. O HTML nativo não possui um elemento de aba dedicado, então os desenvolvedores criam os seus próprios. Este gerador monta uma barra de abas simples em HTML + CSS: uma cor de destaque para a aba ativa, um arredondamento dos cantos para os botões e um estado de hover. Cole a marcação no seu projeto e conecte a troca de abas ao seu próprio JavaScript.

Como construir abas

  1. 1

    Escolha uma cor de destaque

    A cor de fundo e de texto da aba ativa. Escolha uma cor que combine com a sua marca.

  2. 2

    Defina o arredondamento dos cantos

    Arredonda os cantos superiores dos botões de aba. Valores maiores deixam o visual mais suave.

  3. 3

    Edite os rótulos

    O resultado traz três botões, "Overview", "Details" e "Settings": renomeie-os para o seu conteúdo.

  4. 4

    Copie o código

    A marcação das abas vem com o próprio bloco <style>. Cole os dois na sua página.

  5. 5

    Adicione a troca de abas

    O gerador produz apenas a parte visual. Ligue os botões ao seu próprio JavaScript ou biblioteca de UI para mostrar e ocultar os painéis.

Marcação acessível correta

<div class="tabs">
  <div role="tablist" aria-label="Informações do produto">
    <button role="tab" id="tab-1" aria-selected="true"  aria-controls="panel-1" tabindex="0">Visão geral</button>
    <button role="tab" id="tab-2" aria-selected="false" aria-controls="panel-2" tabindex="-1">Especificações</button>
    <button role="tab" id="tab-3" aria-selected="false" aria-controls="panel-3" tabindex="-1">Avaliações</button>
  </div>
  <section role="tabpanel" id="panel-1" aria-labelledby="tab-1">...</section>
  <section role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>...</section>
  <section role="tabpanel" id="panel-3" aria-labelledby="tab-3" hidden>...</section>
</div>

Atributos críticos:

  • role="tablist" envolve os botões.
  • role="tab" em cada botão, com aria-selected e aria-controls.
  • tabindex usa o padrão de tabindex vagante: apenas a aba ativa é 0, as outras são -1. As setas movem o foco entre elas.
  • role="tabpanel" em cada painel, com aria-labelledby apontando para sua aba.
  • hidden em painéis inativos (não display: none sozinho, o atributo é o que o AT usa).

Contrato de teclado

Tecla Ação
Tab Mover para dentro / fora da lista de abas
ArrowLeft Aba anterior (volta para a última)
ArrowRight Próxima aba (volta para a primeira)
Home Primeira aba
End Última aba
Enter / Space Ativar a aba em foco (se a ativação for manual)

Ativação automática (a aba muda conforme o foco se move) vs. manual (o foco se move, a aba ativa no Enter), WAI-ARIA permite ambos. A automática é mais fácil para usuários com visão; a manual é melhor quando o conteúdo da aba é pesado para renderizar.

Estilo sublinhado

[role="tablist"] {
  display: flex;
  border-bottom: 1px solid #e5e7eb;
}
[role="tab"] {
  padding: 0.75rem 1rem;
  background: transparent;
  border: 0;
  color: #6b7280;
  position: relative;
  cursor: pointer;
}
[role="tab"][aria-selected="true"] {
  color: #111827;
}
[role="tab"][aria-selected="true"]::after {
  content: "";
  position: absolute;
  left: 1rem; right: 1rem; bottom: -1px;
  height: 2px;
  background: #2563eb;
}

Estilo arredondado

[role="tab"] {
  padding: 0.5rem 0.9rem;
  border-radius: 9999px;
  border: 0;
  background: transparent;
}
[role="tab"][aria-selected="true"] {
  background: #2563eb;
  color: #fff;
}

Foco visível

Sempre desenhe um anel de foco quando o foco chega via teclado:

[role="tab"]:focus-visible {
  outline: 2px solid #60a5fa;
  outline-offset: 2px;
}

O JavaScript

Aproximadamente:

const tabs = document.querySelectorAll('[role="tab"]');
tabs.forEach(tab => {
  tab.addEventListener('click', () => activate(tab));
  tab.addEventListener('keydown', e => {
    const i = [...tabs].indexOf(tab);
    if (e.key === 'ArrowRight') tabs[(i + 1) % tabs.length].focus();
    if (e.key === 'ArrowLeft')  tabs[(i - 1 + tabs.length) % tabs.length].focus();
    if (e.key === 'Home') tabs[0].focus();
    if (e.key === 'End')  tabs[tabs.length - 1].focus();
  });
});

function activate(tab) {
  tabs.forEach(t => {
    const panel = document.getElementById(t.getAttribute('aria-controls'));
    const active = t === tab;
    t.setAttribute('aria-selected', active);
    t.setAttribute('tabindex', active ? '0' : '-1');
    panel.hidden = !active;
  });
  tab.focus();
}

Trinta linhas no total, zero dependências.

Abas vs. acordeões

Em telas estreitas, as linhas de abas ficam apertadas. Padrão comum: abas no desktop, acordeão no mobile. Use uma consulta de contêiner ou consulta de mídia para mudar o tratamento visual sem alterar os papéis ARIA.

Perguntas frequentes

Para abas acessíveis, sim. Sem role="tablist" e role="tab", leitores de tela tratam os botões como botões comuns e os usuários perdem a convenção de navegação. A alternativa é um acordeão simples <details> / <summary>, que possui semântica embutida.

Automática (a aba ativa conforme o foco se move) é boa quando o conteúdo de cada aba é barato para renderizar. Manual (o foco se move, Enter ativa) é melhor quando mudar de abas aciona um trabalho pesado como buscar ou renderizar um grande formulário.

Para abas que contêm conteúdo (documentação, abas de produtos), sim, use um hash (#specs) ou parâmetro de consulta para que os links possam fazer deep-linking para uma aba específica e a aba ativa persista entre recarregamentos. Para abas de UI (painéis de configurações), a URL é opcional.

Apenas com o truque de checkbox/radio, que carece de navegação por teclas de seta e estados ARIA. Para uma implementação genuinamente acessível, o pequeno trecho de JS vale a pena. Se você precisar de alternativas renderizadas no servidor, vincule cada aba a uma URL única e renderize o painel correspondente no lado do servidor.

Ferramentas relacionadas

Ferramenta disponível em outros idiomas