Animação e microinterações

Troca de abas

Componente de abas com navegação por teclas de direção e animação de alternância de conteúdo.

HTMLCSSJavaScriptTabs
É possível editar, executar e salvar este exemplo após ativar JavaScript. O código-fonte fixo é o seguinte:
<!doctype html>
<html lang="pt-BR" dir="ltr">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Troca de abas</title>
  <style>
    * { box-sizing: border-box; }
    body {
      min-height: 100vh;
      margin: 0;
      display: grid;
      place-items: center;
      padding: 22px;
      color: #eff4ff;
      font-family: "Futura", "Microsoft YaHei", sans-serif;
      background: #101119;
    }
    .window { width: min(820px, 100%); overflow: hidden; border: 1px solid #343747; border-radius: 22px; background: #181a24; box-shadow: 0 30px 80px rgba(0,0,0,.42); }
    .titlebar { display: flex; align-items: center; gap: 8px; padding: 16px 20px; border-bottom: 1px solid #343747; }
    .dot { width: 11px; height: 11px; border-radius: 50%; background: #ff6b67; }
    .dot:nth-child(2) { background: #f7c95c; }
    .dot:nth-child(3) { background: #61d88c; }
    .titlebar strong { margin-left: auto; color: #7d829a; font-size: 11px; letter-spacing: .16em; }
    .layout { display: grid; grid-template-columns: 210px 1fr; min-height: 440px; }
    [role="tablist"] { padding: 26px 14px; border-right: 1px solid #343747; background: #141620; }
    [role="tab"] { width: 100%; display: flex; gap: 12px; align-items: center; padding: 13px 14px; border: 0; border-radius: 12px; color: #9197ad; text-align: left; font: inherit; font-weight: 700; background: transparent; cursor: pointer; }
    [role="tab"] + [role="tab"] { margin-top: 8px; }
    [role="tab"][aria-selected="true"] { color: #fff; background: #292d3e; box-shadow: inset 3px 0 #67e8b8; }
    [role="tab"]:focus-visible { outline: 2px solid #67e8b8; outline-offset: 2px; }
    .tab-icon { display: grid; width: 30px; height: 30px; place-items: center; border: 1px solid #41465c; border-radius: 9px; }
    .content { padding: clamp(28px, 6vw, 54px); }
    [role="tabpanel"] { animation: reveal .32s ease both; }
    [role="tabpanel"][hidden] { display: none; }
    .label { color: #67e8b8; font-size: 12px; font-weight: 800; letter-spacing: .16em; }
    h1 { margin: 14px 0 16px; font: 700 clamp(34px, 6vw, 54px)/1.02 Georgia, "STSong", serif; }
    p { color: #a5aac0; line-height: 1.75; }
    .metric-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-top: 28px; }
    .metric { padding: 18px; border: 1px solid #343747; border-radius: 14px; background: #1e2130; }
    .metric strong { display: block; margin-bottom: 5px; color: #fff; font-size: 24px; }
    .metric span { color: #7f859d; font-size: 12px; }
    @keyframes reveal { from { opacity: 0; transform: translateY(10px); } }
    @media (max-width: 650px) { .layout { grid-template-columns: 1fr; } [role="tablist"] { display: flex; overflow-x: auto; padding: 12px; border-right: 0; border-bottom: 1px solid #343747; } [role="tab"] { min-width: max-content; } [role="tab"] + [role="tab"] { margin: 0 0 0 6px; } .metric-grid { grid-template-columns: 1fr; } }
    @media (prefers-reduced-motion: reduce) { [role="tabpanel"] { animation: none; } }
  </style>
</head>
<body>
  <main class="window">
    <div class="titlebar"><i class="dot"></i><i class="dot"></i><i class="dot"></i><strong>PROJECT ATLAS</strong></div>
    <div class="layout">
      <div role="tablist" aria-label="Painel do projeto">
        <button id="tab-overview" role="tab" aria-selected="true" aria-controls="panel-overview"><span class="tab-icon">⌁</span>Visão geral</button>
        <button id="tab-progress" role="tab" aria-selected="false" aria-controls="panel-progress" tabindex="-1"><span class="tab-icon">↗</span>Progresso</button>
        <button id="tab-team" role="tab" aria-selected="false" aria-controls="panel-team" tabindex="-1"><span class="tab-icon">◎</span>Equipe</button>
      </div>
      <section class="content">
        <div id="panel-overview" role="tabpanel" aria-labelledby="tab-overview"><span class="label">01 / OVERVIEW</span><h1>Tudo está nos trilhos</h1><p>Atlas é um plano de lançamento de produto voltado para equipes pequenas. Estamos focando em concluir a experiência principal e nos preparando para a próxima rodada de testes com usuários.</p><div class="metric-grid"><div class="metric"><strong>82%</strong><span>Conclusão geral</span></div><div class="metric"><strong>14</strong><span>Tarefas desta semana</span></div><div class="metric"><strong>6</strong><span>Membros da colaboração</span></div></div></div>
        <div id="panel-progress" role="tabpanel" aria-labelledby="tab-progress" hidden><span class="label">02 / PROGRESS</span><h1>Faltam 12 dias para o lançamento</h1><p>O sistema de design e os principais fluxos já foram concluídos. Agora estamos realizando testes de regressão em vários dispositivos e resolvendo os últimos problemas de acessibilidade.</p><div class="metric-grid"><div class="metric"><strong>31</strong><span>Concluído</span></div><div class="metric"><strong>7</strong><span>Em andamento</span></div><div class="metric"><strong>3</strong><span>Aguardando confirmação</span></div></div></div>
        <div id="panel-team" role="tabpanel" aria-labelledby="tab-team" hidden><span class="label">03 / TEAM</span><h1>Seis pessoas, uma obra</h1><p>Produto, design e desenvolvimento mantêm juntos a mesma lista de lançamento, cada decisão é rastreável e cada progresso fica claramente visível.</p><div class="metric-grid"><div class="metric"><strong>2</strong><span>Designer</span></div><div class="metric"><strong>3</strong><span>Desenvolvedor</span></div><div class="metric"><strong>1</strong><span>Gerente de produto</span></div></div></div>
      </section>
    </div>
  </main>
  <script>
    const tabs = [...document.querySelectorAll('[role="tab"]')];
    function selectTab(tab) {
      tabs.forEach(item => {
        const selected = item === tab;
        item.setAttribute('aria-selected', String(selected));
        item.tabIndex = selected ? 0 : -1;
        document.getElementById(item.getAttribute('aria-controls')).hidden = !selected;
      });
      tab.focus();
    }
    tabs.forEach((tab, index) => {
      tab.addEventListener('click', () => selectTab(tab));
      tab.addEventListener('keydown', event => {
        const direction = event.key === 'ArrowRight' || event.key === 'ArrowDown' ? 1 : event.key === 'ArrowLeft' || event.key === 'ArrowUp' ? -1 : 0;
        if (direction) { event.preventDefault(); selectTab(tabs[(index + direction + tabs.length) % tabs.length]); }
      });
    });
  </script>
</body>
</html>
Feedback da páginaEncontrou um problema ou tem uma sugestão de melhoria?
Recursos de feedback precisam ser usados na versão online

Envie somente após ativar JavaScript na versão online; a funcionalidade local da ferramenta ou do exemplo não é afetada.

Ir para o feedback na versão online