Artigo original: How to build a responsive navbar with a toggle menu using Flexbox

Escrito por: Charlie Waite

Durante um projeto recente, minha equipe teve que remover todos os vestígios do Bootstrap. Isso significou que a barra de navegação responsiva, extremamente útil, teve que ser criada do zero. Eu sou relativamente novo em CSS, tendo sempre confiado nas navbars do Bootstrap pela sua simplicidade. Então, me ofereci para assumir essa tarefa. Aqui está o que aprendi e fiz ao longo do processo.

Neste artigo, vou assumir que você tem conhecimento básico de HTML, CSS e JavaScript — sabe como vincular uma folha de estilo ao HTML ou aplicar os estilos em uma tag <style> — e que sabe como importar um arquivo de JavaScript para sua página.

Já vi elitistas se defendendo por meio de críticas ao meu jeito de fazer as coisas, em especial pelo fato de usar position: absolute para o menu de sanduíche se tiver maneiras melhores de fazer isso, fique à vontade para publicar sua solução. Juntos, podemos ajudar milhares de pessoas que leem estes artigos!

Introdução

Primeiro, comecei com o HTML básico para fazer o layout:

<div class="Navbar">
  <div class="Navbar__Link Navbar__Link-brand">
    Website title
  </div>
  <div class="Navbar__Link">
    Link
  </div>
  <div class="Navbar__Link">
    Link
  </div>
  <div class="Navbar__Link">
    Link
  </div>
  <div class="Navbar__Link">
    Link
  </div>
  <div class="Navbar__Link">
    Link
  </div>
</div>

Você pode usar a convenção de nomes que quiser para as classes.

1_3tYOXMsOAdUXGmcXmOt50g

Bem, não estamos vendo muita coisa aqui ainda. Essa é apenas uma lista de itens simples. Porém, com apenas uma linha de CSS, vemos o poder do Flexbox.

.Navbar {
  display: flex;
}
1_dK5IrDcMRW00HPZ-wX6cBw
Divs da navbar alinhadas horizontalmente

Uma linha de código e já temos nossos itens de navegação alinhados horizontalmente na parte superior da página.

Agora, vamos adicionar dois elementos nav ao nosso HTML para termos alguns itens à esquerda e à direita da navbar:

<div class="Navbar">
  <nav class="Navbar__Items">
    <div class="Navbar__Link Navbar__Link-brand">
      Website title
    </div>
    <div class="Navbar__Link">
      Link
    </div>
    <div class="Navbar__Link">
      Link
    </div>
    <div class="Navbar__Link">
      Link
    </div>
  </nav>
  <nav class="Navbar__Items Navbar__Items--right">
    <div class="Navbar__Link">
      Link
    </div>
    <div class="Navbar__Link">
      Link
    </div>
  </nav>
</div>

Vamos fazer também a estilização básica de nossa classe Navbar, que envolve todos os outros elementos:

.Navbar {
  background-color: #46ACC2;
  display: flex;
  padding: 16px;
  font-family: sans-serif;
  color: white;
}

Você, logicamente, pode escolher seu próprio esquema de cores, fontes e preenchimento (do inglês, padding).

Nossa navbar, de momento, terá esta aparência:

1_ulTsKdXWB72DtxfDLuzl9A

É... já parece um pouco melhor, mas não podemos deixar nossos itens de navegação serem exibidos na vertical. Antes de seguir lendo, tente adivinhar o que faremos a seguir…

Agora, a propriedade display:flex da classe .Navbar já não está mais responsável por esses itens. Eles são responsabilidade dos contêineres <nav>. Queremos que os dois estejam alinhados na horizontal.

Desse modo, alteramos a classe .Navbar__Items também:

.Navbar__Items {
  display:flex;
}

Em seguida, vamos adicionar preenchimento aos nossos links para deixar tudo um pouco mais bonito:

.Navbar__Link {
  padding-right: 8px;
}

A aparência da navbar, agora, é esta:

1_DTgVCsT7tQlQ5Qgk5mQpSQ

Estamos chegando lá. No entanto, também queremos que o segundo elemento <nav> esteja alinhado à direita. Como você deve ter percebido, adicionei uma classe a mais ao segundo elemento <nav>, .Navbar__Items--right.

Vamos apenas adicionar um atributo margin-left:auto a essa classe:

.Navbar__Items--right {
  margin-left:auto;
}
1_wDWAi7RCETK64FeP4yLNEA
Depois de adicionar margin-left ao segundo elemento nav
1_wzj25OyMsvenI7APSxBDCg
Em um dispositivo móvel

Como você pode ver, parece bem melhor. Já temos uma navbar totalmente responsiva. O que aconteceria, contudo, se cada item de navegação tivesse um texto maior? O que aconteceria se tivéssemos mais itens?

1_TRNh6xHheH7t5I3sUxLI0A
Exemplos de links mais longos

Como podem ver, não é isso que queremos. Queremos deixar todos os itens de navegação em uma única linha, para manter a consistência, ou que eles fossem escondidos em um menu que o usuário pudesse abrir e fechar.

Vamos usar a segunda opção, pois é um estilo muito mais limpo – e não teremos que nos preocupar com o usuário lutando para ler o texto em cada item de navegação.

flex-direction

Com um item que tenha display:flex;, também há uma regra para a direção na qual queremos que os itens estejam orientados. Como padrão, é ao longo de uma linha (em inglês, row), o que alinha todos os itens ao longo do eixo x.

Em nosso caso, queremos um menu vertical pequeno na parte superior da página. Vamos tentar alterar isso usando flex-direction nos dois elementos .Navbar e dando a .Navbar__Items o valor de column — isso alinhará todos os itens de menu ao longo do eixo y — sempre que a largura da tela for 768px ou menos.

Vamos, também, remover aquele margin-left do segundo elemento <nav>:

@media only screen and (max-width: 768px) {
  .Navbar__Items,
  .Navbar {
    flex-direction: column;
  }
  .Navbar__Items--right {
    margin-left: 0;
  }
}
1_rmC0cKnio5Cg9W2CA1luPQ
Navbar com largura de tela de 768px ou inferior

Agora, contudo, os itens de navegação estão sempre visíveis, o que toma uma quantidade significativa de espaço em tela.

Em nossa media query, vamos adicionar uma segunda regra para .Navbar__Items, de maneira que não fiquem visíveis:

@media only screen and (max-width: 768px) {
  .Navbar__Items,
  .Navbar {
    flex-direction: column;
  }
  .Navbar__Items {
    display:none;
  }
  .Navbar__Items--right {
    margin-left:0;
  }
}
1_8ipupgzJ6_ersFfTgVvm6w
Essa é a aparência da navbar em um dispositivo móvel neste momento

O botão para abrir/fechar o menu

Para o botão de abrir/fechar o menu, usarei um ícone fornecido pelo Font Awesome. Se decidir fazer o mesmo, basta seguir as instruções no site deles para integrar os ícones ao seu projeto. Você pode usar qualquer conjunto de ícones que quiser, ou até texto simples, se isso for o que você quer.

Vamos adicionar esse ícone ao HTML:

<div class="Navbar">
   <div class="Navbar__Link Navbar__Link-brand">
      Website title
    </div>
    <div class="Navbar__Link Navbar__Link-toggle">
      <i class="fas fa-bars"></i>
    </div>
  <nav class="Navbar__Items">
    <div class="Navbar__Link">
      Longer Link
    </div>
    <div class="Navbar__Link">
      Longer Link
    </div>
    <div class="Navbar__Link">
      Link
    </div>
  </nav>
  <nav class="Navbar__Items Navbar__Items--right">
    <div class="Navbar__Link">
      Link
    </div>
    <div class="Navbar__Link">
      Link
    </div>
  </nav>
</div>

A inclusão é a linha que diz <i class="fas fa-bars"></i>. Você perceberá que o ícone não vai dentro de nenhuma das tags nav, ficando fora, juntamente com o título do site. Isso é algo que faz sentido.

1_GkN1E6ZKm7W93FKDqTiWBQ
ícone do menu adicionado

Claro que não é o local onde queremos que ele esteja. Pior ainda, ele está visível em resoluções para desktop.

1_K5ygf98rpZNXLxQcjsJkXw

Vamos consertar isso. Vamos fazer o que fizemos com os .Navbar__Items nas resoluções para dispositivos móveis. Desta vez, faremos isso com o ícone de menu nas resoluções para desktop:

.Navbar__Link-toggle {
  display: none;
}

Em seguida, vamos adicionar algumas regras para a mesma classe dentro de nossa media query:

.Navbar__Link-toggle {
  align-self: flex-end;
  display: initial;
  position: absolute;
  cursor: pointer;
}
1_5jybOok3eVV03DHWKitL3g
Essa é a aparência da navbar em dispositivos móveis com o botão para abrir e fechar o menu

Estamos praticamente prontos. Já temos a aparência desejada. Precisamos apenas adicionar a funcionalidade de abrir e fechar o menu ao ícone.

No JavaScript, adicione o seguinte:

function classToggle() {
  const navs = document.querySelectorAll('.Navbar__Items')
  
  navs.forEach(nav => nav.classList.toggle('Navbar__ToggleShow'));
}

document.querySelector('.Navbar__Link-toggle')
  .addEventListener('click', classToggle);

Por fim, adicione Navbar__ToggleShow com a regra display:flex à media query.

Pronto! Temos uma navbar totalmente responsiva com um menu que abre e fecha. Usando o Flexbox, fica muito simples!

1_k0-kcRuPaA4LeuxzcIlJMg

Versão final

HTML:

<div class="Navbar">
   <div class="Navbar__Link Navbar__Link-brand">
      Website title
    </div>
    <div class="Navbar__Link Navbar__Link-toggle">
      <i class="fas fa-bars"></i>
    </div>
  <nav class="Navbar__Items">
    <div class="Navbar__Link">
      Longer Link
    </div>
    <div class="Navbar__Link">
      Longer Link
    </div>
    <div class="Navbar__Link">
      Link
    </div>
  </nav>
  <nav class="Navbar__Items Navbar__Items--right">
    <div class="Navbar__Link">
      Link
    </div>
    <div class="Navbar__Link">
      Link
    </div>
  </nav>
</div>

CSS:

.Navbar {
  background-color: #46ACC2;
  display: flex;
  padding: 16px;
  font-family: sans-serif;
  color: white;
}

.Navbar__Link {
  padding-right: 8px;
}

.Navbar__Items {
  display: flex;
}

.Navbar__Items--right {
  margin-left:auto;
}

.Navbar__Link-toggle {
  display: none;
}

@media only screen and (max-width: 768px) {
  .Navbar__Items,
  .Navbar {
    flex-direction: column;
  }
    
.Navbar__Items {
    display:none;
  }
    
.Navbar__Items--right {
    margin-left:0;
  }
    
.Navbar__ToggleShow {
    display: flex;
  }
    
.Navbar__Link-toggle {
    align-self: flex-end;
    display: initial;
    position: absolute;
    cursor: pointer;
   } 
}

JavaScript:

function classToggle() {
  const navs = document.querySelectorAll('.Navbar__Items')
  
  navs.forEach(nav => nav.classList.toggle('Navbar__ToggleShow'));
}

document.querySelector('.Navbar__Link-toggle')
  .addEventListener('click', classToggle);

Você pode ler mais sobre o Flexbox na MDN:

Conceitos básicos de flexbox

Você também pode consultar o local onde eu mesmo aprendi sobre o básico do Flexbox (texto em inglês):

Learn CSS Flexbox in this FREE and interactive tutorial

Siga o autor no GitHub.