DEV Community

João Iacillo
João Iacillo

Posted on

Criando uma Barra de Navegação Mobile sem JavaScript

Barras de navegação em dispositivos móveis devem ser pensadas de antemão e criadas para serem confortáveis e fáceis de usar, e você consegue fazer elas sem usar JavaScript.

Normalmente, a navegação é escondida em um menu especial chamado de "hambúrguer", que se revela após um botão ser pressionado. Isso faz com que o conteúdo do site se torne mais relevante e o cabeçalho ocupe menos espaço.

Apesar do JavaScript ser o caminho mais utilizado para criar esse tipo de menu, além de ser uma adição bem leve que não afeta a performance do seu site, é possível implementar essa mesma funcionalidade usando apenas HTML e CSS.

Os navegadores já apresentam elementos e subclasses nativas para criar essa interatividade sem a necessidade de código extra, apenas utilizando inputs, labels, e as pseudo classes :has() e :checked. Todas amplamente suportadas desde versões antigas.

Ir por esse caminho também ajudará você a compreender mais como o CSS seleciona elementos com base em suas posições no DOM e ser mais criativo na hora de criar elementos interativos sem a necessidade de inserir JavaScript em sua página.

Criando o Toggler

O Toggler é o botão responsável por alterar entre os estados de aberto e fechado do menu.

Pensando em botão, lembramos do elemento <button> do HTML. Porém, ele por si só não consegue armazenar um estado a menos que adicionado manualmente via código.

Um outro elemento do HTML que possui esse comportamento nativo é o <input> do tipo checkbox. Com ele podemos checar seu estado através da pseudo classe :checked:

<!-- HTML -->
<input type="checkbox" />
Enter fullscreen mode Exit fullscreen mode
/* CSS */
input:checked { }
Enter fullscreen mode Exit fullscreen mode

Somente com esse input, já inserimos um botão do tipo toggler em nosso site. Porém, ele por si só não é muito descritivo, nem muito bonito, portanto podemos melhorá-lo da seguinte forma:

  1. Envolve-lo com um <label>;
  2. Adicionar um ícone de menu hambúrguer;
  3. Esconder o input;
  4. Adicionar um CSS extra para estilizar o botão:
<!-- Botão Toggler -->
<label class="toggler">
  <!-- Input -->
  <input type="checkbox" />

  <!-- Ícone -->
  <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
    <path d="M0 0h24v24H0z" fill="none" />
    <path fill="currentColor" d="M3 18v-2h18v2zm0-5v-2h18v2zm0-5V6h18v2z" />
  </svg>
</label>
Enter fullscreen mode Exit fullscreen mode
.toggler {
  display: inline-flex;
  padding: 8px;
  border: solid 1px rgba(0 0 0 / 30%);
  border-radius: 4px;
}

.toggler input {
  display: none;
}
Enter fullscreen mode Exit fullscreen mode

botão de menu de hambúrguer

Nesse momento, o estilo do botão é estático, e apesar de ele funcionar, não temos um feedback visual apropriado. Podemos fazer com que o estilo do botão mude com base no estado do input utilizando a pseudo classe :has(:checked):

.toggler:has(:checked) {
  background-color: black;
  color: white;
}
Enter fullscreen mode Exit fullscreen mode

E agora quando a checkbox for marcada (ou em outras palavras, o menu for aberto), o botão ficará com as cores invertidas:

botão de menu de hambúrguer ativado

Por que :has(:checked) e não .toggler input:checked?

Para que esse efeito funcione, é preciso que a <label> seja a estilizada, e não o <input>.

Pelas regras de seleção do CSS, quando abrimos um seletor de criança, o parente não é o escolhido, e sim a criança. Nesse caso, se utilizássemos a segunda opção, o selecionado seria o <input> e não o <label>, causando um comportamento não esperado.

:has() é uma pseudo classe que faz a verificação interna do parente, mas não muda a seleção, e com ele podemos fazer essas seleções mais complexas. Nesse caso, se qualquer criança da <label> der como verdadeiro a condição :checked, então a regra é aplicada à <label>.

Criando o menu de navegação

A barra de navegação é a parte mais comum até o momento. Uma estrutura simples e bem reconhecida:

<nav class="navbar">
  <ul>
    <li><a href="/pagina1">Página 1</a></li>
    <li><a href="/pagina2">Página 2</a></li>
    <li><a href="/pagina3">Página 3</a></li>
  </ul>
</nav>
Enter fullscreen mode Exit fullscreen mode

Mas é muito importante que o menu venha depois do toggler. Nós usaremos o seletor de vizinho do CSS e a mesma tática da pseudo classe :has() para controlar o estado de visibilidade da navbar:

<label class="toggler"><!-- ... --></label>

<nav class="navbar"><!-- ... --></nav>
Enter fullscreen mode Exit fullscreen mode
.navbar {
  display: none;
}

.toggler:has(:checked) + .navbar {
  display: block;
}
Enter fullscreen mode Exit fullscreen mode

Com isso, já temos uma barra de navegação colapsável funcional, mas para tornar isso realmente confortável no mobile, precisamos fazer algumas alterações no estilo e na estrutura:

  1. Envolver todo esse código em um <header>;
  2. Estilizar a barra de navegação para um formato mais fácil de clicar;
  3. E para finalizar, adicionar um indicador de página atual:
<header class="header">
  <label class="toggler"><!-- ... --></label>
  <nav class="navbar">
    <ul>
      <li><a href="/pagina1" class="active">Página 1</a></li>
      <li><a href="/pagina2">Página 2</a></li>
      <li><a href="/pagina3">Página 3</a></li>
    </ul>
  </nav>
</header>
Enter fullscreen mode Exit fullscreen mode
.header {
  position: relative;
  background-color: rgba(0 0 0 / 10%);
  padding: 8px;
}

.navbar {
  display: none;
  position: absolute;
  width: 100%;
  left: 0;
  top: 100%;
  background-color: rgba(0 0 0 / 10%);
}

.navbar ul {
  list-style-type: none;
  padding: 0;
  margin: 0;
}

.navbar a {
  display: block;
  padding: 8px 16px;
  color: black;
  text-decoration: none;
}

.navbar .active {
  background-color: rgba(0 0 0 / 20%);
}
Enter fullscreen mode Exit fullscreen mode

Bônus: Tailwind

Caso você esteja usando o Tailwind, fazer esse tipo de menu se torna uma tarefa muito mais fácil:

<header>
  <label class="peer">
    <input type="checkbox" class="hidden" />
    <!-- icon here -->
  </label>

  <nav class="hidden peer-has-checked:block">
    <!-- ... -->
  </nav>
</header>
Enter fullscreen mode Exit fullscreen mode

Top comments (0)