Tucano v0.37.2

Menu lateral

A lista de navegação, e só ela: é a parte que se repete igual em todo projeto. Não há componente nem JavaScript próprio — são classes sobre links. O que vai nela, como agrupa e onde mora continua sendo decisão sua: fixa na lateral no desktop, dentro de uma gaveta no celular, ou nas duas.

Clique nos itens

O item atual leva cor, fundo e peso no primeiro nível, e uma pílula neutra dentro do submenu. Os grupos abrem e fecham com a animação do acordeão.

No celular, dentro de uma gaveta

A mesma lista numa gaveta pela esquerda — o padrão de menu de sistema em tela pequena.

Exemplos

As peças do menu, uma de cada vez.

Itens e item atual

is-active marca a página em que a pessoa está.

Ícone e contador

__icon à esquerda; __count é a .tuc-badge is-plain, empurrada para a direita.

Seções e separador

__section é o rótulo de um bloco; __separator, uma linha entre blocos.

Submenu

Um <details> com __sub; a seta aponta para a direita fechado e para baixo aberto.

Como usar

Um <nav> com a classe e links dentro. O data-tuc-accordion no <nav> só é preciso quando há submenu: é ele que anima os <details>.

<nav class="tuc-menu" aria-label="Principal" data-tuc-accordion>

  <!-- item simples -->
  <a class="tuc-menu__item" href="/billing/">
    <span class="tuc-menu__icon"><svg>…</svg></span>Faturamento
  </a>

  <!-- item com contador -->
  <a class="tuc-menu__item" href="/products/">
    <span class="tuc-menu__icon"><svg>…</svg></span>Produtos
    <span class="tuc-badge is-plain tuc-menu__count">12</span>
  </a>

  <!-- item com submenu -->
  <details open>
    <summary class="tuc-menu__item">
      <span class="tuc-menu__icon"><svg>…</svg></span>Desempenho
    </summary>
    <div class="tuc-menu__sub">
      <a class="tuc-menu__item is-active" href="/performance/period/" aria-current="page">Por período</a>
      <a class="tuc-menu__item" href="/performance/branches/">Unidades</a>
    </div>
  </details>

  <hr class="tuc-menu__separator">
  <div class="tuc-menu__section">Cadastros</div>
  <a class="tuc-menu__item" href="/customers/">Clientes</a>
</nav>

No template do Django

O item atual sai da rota resolvida. Escreva o menu uma vez num include e ele marca a página certa em todas:

{% with route=request.resolver_match.url_name %}
<nav class="tuc-menu" aria-label="Principal" data-tuc-accordion>
  <a class="tuc-menu__item{% if route == 'contract-list' %} is-active{% endif %}"
     href="{% url 'contract-list' %}"{% if route == 'contract-list' %} aria-current="page"{% endif %}>
    Contratos
  </a>

  <details{% if route|slice:":12" == 'performance-' %} open{% endif %}>
    <summary class="tuc-menu__item">Desempenho</summary>
    <div class="tuc-menu__sub">
      <a class="tuc-menu__item{% if route == 'performance-period' %} is-active{% endif %}"
         href="{% url 'performance-period' %}">Por período</a>
    </div>
  </details>
</nav>
{% endwith %}

Deixe aberto no template o grupo que contém a página atual: quem chega por um link de submenu já vê onde está.

Na lateral e na gaveta

O menu não sabe onde mora. No desktop, ponha-o numa coluna fixa; no celular, o mesmo include vai dentro de uma gaveta pela esquerda:

<dialog class="tuc-drawer is-left is-sm" id="menu-main" aria-labelledby="menu-main-title">
  <div class="tuc-drawer__panel">
    <div class="tuc-drawer__top">
      <div class="tuc-drawer__header">
        <h2 class="tuc-drawer__title" id="menu-main-title">Painel</h2>
      </div>
      <button type="button" class="tuc-btn is-ghost is-icon is-sm tuc-drawer__close" aria-label="Fechar" data-tuc-drawer-close>…</button>
    </div>
    <div class="tuc-drawer__body">
      {% include "partials/menu.html" %}
    </div>
  </div>
</dialog>

<button class="tuc-btn is-outline is-icon" aria-label="Abrir menu" data-tuc-drawer="#menu-main">…</button>

Na coluna fixa do desktop, faça dela uma coluna flex com altura, com a marca em cima e o menu embaixo. O .tuc-menu ocupa o que sobra e rola sozinho quando a lista passa da tela — a marca continua parada no topo. Na gaveta quem rola é o corpo dela, e não é preciso nada.

<aside style="display: flex; flex-direction: column; height: 100vh; position: sticky; top: 0">
  <a href="/">Minha marca</a>
  <nav class="tuc-menu" aria-label="Painel">…</nav>
</aside>

O item que abre e revela filhos é um <details>, e a abertura reaproveita o acordeão: a animação, o teclado e o funcionamento sem JavaScript já vêm prontos. O menu cuida só da leitura — quem é filho de quem, e onde está o atual dentro da árvore.

O <summary> leva tuc-menu__item e fica com a mesma cara dos vizinhos, com o rótulo alinhado à esquerda e a seta empurrada para a direita. Os filhos ficam em tuc-menu__sub, recuados e presos a um trilho à esquerda. Filho não repete ícone: ele herda o contexto do pai, e o ponto na frente marca a linha sem disputar atenção com a fileira de ícones de cima.

A seta gira 90 graus, não 180

Direita quando fechado, para baixo quando aberto — é como uma árvore de navegação se lê: "há algo aqui dentro" contra "esta é a ramificação aberta". E o trilho à esquerda existe porque a indentação sozinha se perde assim que a lista passa de quatro ou cinco linhas.

O item atual leva três pistas

No primeiro nível, cor de destaque, fundo e peso — nenhuma depende só da cor. Dentro do submenu o atual é uma pílula neutra com o ponto aceso: a cor de destaque fica com o nível de cima, e dois itens laranja na mesma coluna diriam menos.

Teclado e acessibilidade

São links e <details>, e o teclado é o deles: nada a aprender, nada a implementar.

TeclaAção
Tab Shift+TabAnda entre os itens, incluindo os títulos de submenu; filhos de um grupo fechado ficam fora do caminho
EnterSegue o link, ou abre e fecha o submenu focado
EspaçoAbre e fecha o submenu focado

Escreva aria-current="page" junto do is-active: a classe desenha, o atributo é o que o leitor de tela anuncia. Dê ao <nav> um aria-label quando houver mais de uma navegação na página. E não use role="menu" aqui: esse papel promete as setas de um menu de aplicativo, e a lista é navegação comum.

Classes

Só classes — o menu não tem JavaScript nem opções. O que anima o submenu é o acordeão.

Classe ou atributoOndePara quê
tuc-menu<nav>A lista, em coluna
tuc-menu__item<a> ou <summary>Uma linha do menu, com a altura de um controle
is-active.tuc-menu__itemO item atual: destaque no primeiro nível, pílula neutra no submenu
tuc-menu__icon<span> no itemCaixa de 16px para o ícone, na cor de apoio; no item ativo, na cor de destaque
tuc-menu__count.tuc-badge is-plain no itemEmpurra o contador para a direita, com dígitos de largura fixa
tuc-menu__separator<hr>Linha entre blocos
tuc-menu__section<div>Rótulo de um bloco, em caixa alta
tuc-menu__sub<div> dentro do <details>Os filhos do submenu, recuados e com trilho
data-tuc-accordion<nav>Anima a abertura dos submenus; data-single="true" deixa um aberto por vez