Landing page de barbearia em HTML e CSS: passo a passo

Quase todo pequeno negócio precisa de uma página que diga quem é, o que oferece, quanto cobra e como falar com ele. Barbearia, salão, oficina, loja de bairro: o desenho muda, mas a receita é a mesma. Esse tipo de página, de uma só tela longa e com um objetivo claro, chama-se landing page.

Neste artigo vamos montar uma landing page completa para uma barbearia, só com HTML e CSS, e duas ajudas externas: o Font Awesome (ícones) e a biblioteca AOS (animações ao rolar a página). O layout usa o que já vimos: flexbox e @media.

E o trabalho é dividido em duas partes. Primeiro você recebe a página pronta, mas sem nenhuma animação, e entende cada bloco (passos 1 a 9). Depois aplica as seis animações, uma de cada vez, testando o efeito a cada etapa. Assim dá para ver exatamente o que cada linha de código faz.

O que vamos construir

Z Landing page de barbearia em HTML e CSS: passo a passo
Topo da página no computador: ícones, logo, botão, frase principal e o botão fixo do WhatsApp.
9k= Landing page de barbearia em HTML e CSS: passo a passo
Seção de serviços: três cartões lado a lado, com foto, nome e preço.
Z Landing page de barbearia em HTML e CSS: passo a passo
No celular, o mesmo HTML se reorganiza em uma coluna só.

A página tem seis blocos, e cada um vira uma seção do nosso código:

Esquema da página: cabeçalho, frase principal, sobre, serviços, mapa e rodapé, mais o botão fixo do WhatsApp mapa 1 · cabeçalhoícones, logo e botão 2 · frase principal (destaque)título e botão de agendar 3 · sobreimagem de um lado, texto do outro 4 · serviçostrês cartões com preço 5 · mapaiframe do Google Maps 6 · rodapéredes sociais, logo e copyright botão fixo do WhatsApp
Os seis blocos da página, mais o botão do WhatsApp, que fica fixo na tela enquanto você rola.

Baixe os dois projetos:

barbearia-inicial.zip: a página sem animações, ponto de partida para você acompanhar o artigo e aplicar as animações.

⬇ Baixar barbearia-inicial.zip

barbearia-final.zip: a página completa, com as seis animações. Use para conferir o seu resultado (gabarito).

⬇ Baixar barbearia-final.zip

O projeto é baseado no repositório profwsp/barbearia2026, com as correções que explicamos mais adiante.

A estrutura de pastas

barbearia-inicial/   (e barbearia-final/, com a mesma estrutura)
├── index.html
├── estilo.css
├── script.js
├── fontes/
│   ├── sora-300.woff2
│   ├── sora-400.woff2
│   ├── sora-500.woff2
│   └── sora-600.woff2
└── imagem/
    ├── fundo.svg         (foto de fundo do topo)
    ├── fotos-sobre.svg   (montagem de fotos da seção "Sobre")
    ├── logo.svg
    ├── whatsapp.svg
    ├── corte1.png
    ├── corte2.png
    └── corte3.png
Atenção ao caminho: dentro do estilo.css, o caminho da imagem de fundo é url("imagem/fundo.svg"), porque o estilo.css está na mesma pasta que a pasta assets/. Lembre: o caminho é sempre contado a partir do arquivo CSS.

As ajudas externas

Duas bibliotecas vêm de fora do nosso projeto e são carregadas pela internet (por isso a página precisa de conexão para mostrar os ícones e, mais tarde, as animações). A fonte, ao contrário, fica dentro do projeto, como veremos no passo 1:

RecursoPara que serveComo entra na página
Sora (fonte)A fonte dos textosArquivos .woff2 na pasta fontes/, ligados por @font-face no CSS (nada externo)
Font AwesomeÍcones de Instagram, Facebook e TikTok<link> no <head> e <i class="fa-brands fa-instagram"> no HTML
AOSAnimar os blocos quando aparecem na tela<link> e <script> no <head>, atributos data-aos no HTML e AOS.init() no script.js. Entra só na etapa 6, depois do Passo 9

Passo 1: o <head> e o idioma

A primeira linha útil do arquivo é <html lang="pt-BR">. Ela diz ao navegador, ao Google e aos leitores de tela que a página é em português do Brasil. Em seguida vêm o charset, a meta viewport (que já conhecemos) e uma meta description, o resumo que o Google mostra nos resultados de busca.

Depois vem a biblioteca externa de ícones (Font Awesome); o AOS só entra na etapa 6. Repare que o nosso estilo.css é carregado por último: se dois CSS mandarem na mesma coisa, o último vence, e queremos que o nosso tenha a palavra final.

  <!-- Ícones (Font Awesome) -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css">

  <!-- Nosso CSS (por último, para vencer os anteriores em caso de empate).
       A fonte Sora é carregada por ele, a partir da pasta fontes/ -->
  <link rel="stylesheet" href="estilo.css">

A fonte Sora dentro do projeto

É comum pegar fontes do Google Fonts por um <link>. O problema é que a página passa a depender de um site de fora: se o serviço ficar indisponível, bloqueado ou lento, o visitante vê a fonte padrão do navegador. Por isso deixamos os arquivos da fonte Sora (licença livre) dentro da pasta fontes/ e os ligamos com @font-face:

@font-face {
  font-family: 'Sora';
  src: url("fontes/sora-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
  • font-family: o nome que vamos usar no resto do CSS (font-family: 'Sora').
  • src: url(...): onde está o arquivo. Como o CSS está na raiz, o caminho começa em fontes/.
  • font-weight: a espessura que esse arquivo representa (300 fina, 400 normal, 500 média, 600 semi-negrito). Cada espessura tem seu arquivo, e o projeto repete o bloco para as quatro.
  • font-display: swap: mostra o texto com uma fonte do sistema enquanto a Sora carrega, para a página não ficar em branco.
Onde conseguir os arquivos: sites como o Google Fonts e o Fontsource permitem baixar fontes livres. Procure pelo formato .woff2, que é leve e aceito por todos os navegadores atuais.

Passo 2: a base do CSS

Antes de estilizar os blocos, o CSS define regras gerais para a página inteira:

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: 'Sora', sans-serif;
  background-color: #06121e;
}

img {
  width: 100%;
}

a {
  text-decoration: none;
  cursor: pointer;
}

i {
  color: #fff;
}

/* Contorno para quem navega pelo teclado */
a:focus-visible {
  outline: 3px solid #f68e36;
  outline-offset: 3px;
}
  • * { margin: 0; padding: 0; box-sizing: border-box; } zera as margens e paddings padrão do navegador e faz a largura incluir o padding e a borda. Com isso, os cálculos de tamanho ficam previsíveis.
  • img { width: 100% } faz toda imagem ocupar a largura da caixa em que está. Quando precisamos limitar, usamos max-width nos casos específicos.
  • i { color: #fff } deixa os ícones brancos (o Font Awesome usa a tag <i>).
  • a:focus-visible desenha um contorno laranja em quem navega pelo teclado com a tecla Tab. Não remova esse contorno: sem ele, essa pessoa não sabe onde está.

Passo 3: o cabeçalho

O cabeçalho tem três grupos lado a lado: ícones das redes sociais, logo e botão de agendar. Em HTML são três <div> dentro de uma <nav>:

      <nav class="cabecalho-conteudo conteiner">
        <div class="cabecalho-icones">
          <a href="#" aria-label="Instagram"><i class="fa-brands fa-instagram fa-2x"></i></a>
          <a href="#" aria-label="Facebook"><i class="fa-brands fa-facebook fa-2x"></i></a>
          <a href="#" aria-label="TikTok"><i class="fa-brands fa-tiktok fa-2x"></i></a>
        </div>

        <div class="cabecalho-logo">
          <img
            src="imagem/logo.svg"
            alt="Logo da barbearia"
          >
        </div>

        <div>
          <a class="cabecalho-botao"
             href="https://api.whatsapp.com/send?phone=5511000000000&text=Quero%20fazer%20um%20agendamento%20hoje!"
             target="_blank" rel="noopener">
            Agendar horário
          </a>
        </div>
      </nav>

Cada link com apenas um ícone recebe um aria-label (“Instagram”, “Facebook”, “TikTok”). Sem isso, um leitor de tela só diria “link”, sem dizer para onde.

No CSS, o flexbox alinha os três grupos: justify-content: space-between joga o primeiro para a esquerda, o último para a direita e o do meio fica no centro:

.cabecalho-conteudo {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.cabecalho-icones {
  display: flex;
  gap: 14px;
}

O botão e o hover

O botão “Agendar horário” tem borda branca e fundo transparente. A regra com :hover (que vale quando o mouse está em cima) troca a borda e o fundo para vermelho. Por enquanto a troca é instantânea: na etapa 1 vamos suavizá-la.

.cabecalho-botao {
  border: 1px solid #fff;
  padding: 8px 24px;
  border-radius: 4px;
  color: #fff;
  background-color: transparent;
}

.cabecalho-botao:hover {
  border: 1px solid #d34040;
  background-color: #d34040;
}

O mesmo vale para os ícones das redes sociais: no hover eles ficam vermelhos (.cabecalho-icones a:hover i). Na etapa 2 eles vão ganhar movimento.

Passo 4: a frase principal com foto de fundo

O topo da página tem uma foto escura ao fundo. Em vez de colocá-la direto na caixa, usamos um truque comum: um pseudo-elemento ::before que cria uma camada extra atrás do conteúdo, só com a imagem e um degradê.

.fundo-inicio {
  position: relative;
}

.fundo-inicio::before {
  content: '';
  display: block;
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  z-index: -1;
  background-image: linear-gradient(to bottom, rgba(6, 18, 30, 0.1), #06121e), url("imagem/fundo.svg");
  background-size: cover;
  background-repeat: no-repeat;
  background-position: 50% 0;
  opacity: 0.9;
}
Duas camadas: o ::before com foto e degradê atrás, e o conteúdo do cabeçalho na frente foto (fundo.svg) + degradê para o azul-escuro header + frase principal (texto branco por cima da foto) camada da frente o conteúdo normal da página camada de trás ::before com z-index: -1
O ::before fica atrás (z-index: -1). O conteúdo do cabeçalho aparece por cima, sem ser afetado pelo degradê.
  • content: '' é obrigatório: sem ele, o pseudo-elemento não existe.
  • position: absolute com left: 0; top: 0; width: 100%; height: 100% faz a camada cobrir toda a caixa .fundo-inicio, que por isso precisa de position: relative.
  • O background-image tem duas imagens separadas por vírgula: o degradê (linear-gradient) por cima e a foto por baixo. O degradê vai de quase transparente até o azul-escuro do site, escondendo a parte de baixo da foto.
  • background-size: cover cobre a área toda, sem distorcer.

Agora o estilo da frase principal. O .destaque usa flexbox em coluna e centraliza tudo, e min-height: 80vh garante que ela ocupe pelo menos 80% da altura da tela:

.destaque {
  color: #fff;
  min-height: 80vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 14px;
}

.destaque h1 {
  text-align: center;
  line-height: 150%;
  max-width: 940px;
  font-size: 46px;
}

.destaque p {
  text-align: center;
  margin: 14px 0;
}

Passo 5: a seção “Sobre”

Aqui temos uma imagem de um lado e o texto do outro. O HTML são duas <div> dentro de uma caixa:

  <section class="sobre">
    <div class="conteiner sobre-conteudo">
      <div>
        <img src="imagem/fotos-sobre.svg" alt="Fotos de cortes de cabelo e barba feitos na barbearia">
      </div>

      <div class="sobre-descricao">
        <h2>Sobre</h2>
        <p>Aqui você encontra corte de cabelo, barba e atendimento sem pressa, em um ambiente pensado para você relaxar e sair com o visual que combina com a sua personalidade.</p>
        <p>Horário de funcionamento: <strong>09:00</strong> às <strong>19:00</strong></p>
      </div>
    </div>
  </section>

No CSS, display: flex coloca as duas lado a lado. A regra .sobre-conteudo div { flex: 1 } faz as duas dividirem o espaço em partes iguais:

.sobre {
  background-color: #06121e;
  color: #fff;
  overflow: hidden;
}

.sobre-conteudo {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 34px 14px 64px 14px;
}

.sobre-conteudo img {
  max-width: 570px;
}

.sobre-conteudo div {
  flex: 1;
}

.sobre-descricao h2 {
  font-size: 38px;
  margin-bottom: 24px;
}

.sobre-descricao p {
  margin-bottom: 14px;
  line-height: 150%;
}
Texto alternativo: o alt da imagem descreve o que ela mostra (“Fotos de cortes de cabelo e barba…”). É ele que um leitor de tela lê, e aparece se a imagem não carregar.

Passo 6: a seção de serviços

Cada serviço é um cartão com foto, nome e preço. Vamos ver um deles (os outros dois só mudam a imagem e os textos):

      <div class="corte">
        <img src="imagem/corte1.png" alt="Corte normal">
        <div class="corte-info">
          <strong>Corte normal</strong>
          <span class="corte-preco">R$ 45,00</span>
        </div>

Os três cartões ficam dentro de .cortes, que é um container flex. Cada cartão tem flex: 1 (largura igual) e também é flex, mas em coluna: foto em cima, informações embaixo.

.cortes {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 16px;
  padding: 0 44px 34px 44px;
}

.corte {
  flex: 1;
  display: flex;
  flex-direction: column;
  background-color: #fff;
  border-radius: 8px;
  box-shadow: 0 -1px 17px -4px rgba(0, 0, 0, 0.45);
  overflow: hidden;
}

.corte img {
  width: 100%;
  max-width: 354px;
}

.corte-info {
  width: 100%;
  display: flex;
  justify-content: space-between;
  padding: 14px;
  background-color: #fff;
  z-index: 99;
}

.corte-preco {
  color: #fff;
  background-color: #d94140;
  padding: 4px 8px;
  border-radius: 4px;
}
  • overflow: hidden no cartão corta o que passar da borda arredondada. Vai ser importante na etapa 4, quando a foto aumentar ao passar o mouse.
  • box-shadow cria a sombra suave ao redor dos cartões.
  • O preço é um <span> estilizado como etiqueta vermelha. No projeto original ele era um <button>, mas um botão que não faz nada confunde quem usa o teclado: se não é clicável, não deve parecer nem ser um botão.

Passo 7: mapa e rodapé

A seção “Venha conhecer” usa um <iframe> do Google Maps. Para gerar o seu: abra o endereço no Google Maps, clique em Compartilhar, escolha Incorporar um mapa e copie o código.

  <iframe
    title="Mapa com a localização da barbearia"
    src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d7476.4115143508725!2d-54.58602051703828!3d-20.45674056051272!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x9486e8954ac0b10d%3A0x7149aba0cb5688b8!2sShopping%20Campo%20Grande!5e0!3m2!1sen!2sbr!4v1688840244482!5m2!1sen!2sbr"
    width="100%" height="450" style="border:0;" allowfullscreen loading="lazy"
    referrerpolicy="no-referrer-when-downgrade"></iframe>
  • title: descrição do mapa para leitores de tela.
  • loading="lazy": o mapa só carrega quando você chega perto dele, deixando a página inicial mais rápida.
  • No projeto original, o width aparecia duas vezes (“100%” e “600”). O navegador usa só a primeira, mas a duplicação é um erro de código. Mantivemos apenas width="100%".
Troque o mapa: o endereço do projeto é só de exemplo. Cole o código de incorporação do local da sua barbearia (ou do seu negócio).

O rodapé repete os ícones e o logo, centralizados com flexbox em coluna:

  <footer class="rodape">
    <div class="rodape-icones">
      <a href="#" aria-label="Instagram"><i class="fa-brands fa-instagram fa-2x"></i></a>
      <a href="#" aria-label="Facebook"><i class="fa-brands fa-facebook fa-2x"></i></a>
      <a href="#" aria-label="TikTok"><i class="fa-brands fa-tiktok fa-2x"></i></a>
    </div>

    <div>
      <img src="imagem/logo.svg" alt="Logo da barbearia">
    </div>

    <p>Copyright 2026 | Dev Barber - Todos os direitos reservados.</p>
  </footer>

Passo 8: o botão fixo do WhatsApp

O botão redondo que acompanha o visitante enquanto ele rola é um link com position: fixed: ele fica preso à tela, a 14 px do rodapé e 24 px da direita. O texto “Agende seu horário” aparece como uma dica (tooltip) ao passar o mouse.

  <a
    href="https://api.whatsapp.com/send?phone=5511000000000&text=Quero%20fazer%20um%20agendamento%20hoje!"
    class="botao-whatsapp"
    target="_blank" rel="noopener"
   
  >
    <img src="imagem/whatsapp.svg" alt="Agendar pelo WhatsApp">
    <span class="dica-texto">Agende seu horário</span>
  </a>
.botao-whatsapp {
  position: fixed;
  bottom: 14px;
  right: 24px;
  z-index: 99;
}

.botao-whatsapp img {
  max-width: 74px;
}

.botao-whatsapp .dica-texto {
  visibility: hidden;
  position: absolute;
  width: 120px;
  top: 8px;
  left: -144px;
  padding: 6px;
  border-radius: 8px;
  text-align: center;
  background-color: #232224;
  color: #fff;
  opacity: 0;
}

.botao-whatsapp:hover .dica-texto,
.botao-whatsapp:focus-visible .dica-texto {
  visibility: visible;
  opacity: 1;
}
  • O tooltip começa com visibility: hidden e opacity: 0. Com :hover (mouse) ou :focus-visible (teclado), ele aparece. A vírgula entre os dois seletores aplica a mesma regra aos dois. Por enquanto ele surge de uma vez: na etapa 5 vamos suavizar.
  • position: absolute dentro de um pai fixed posiciona o tooltip em relação ao botão, 144 px à esquerda.
  • O link do WhatsApp tem este formato: https://api.whatsapp.com/send?phone=55DDDNÚMERO&text=mensagem. Troque o número de exemplo pelo seu. O texto da mensagem usa %20 no lugar dos espaços.
Para quem vai publicar: o número 5511000000000 é fictício. Se você deixar o de exemplo, o visitante vai tentar falar com um número que não existe.

Passo 9: responsividade

Na seção de CSS, tudo foi escrito pensando no computador. No final do arquivo, duas media queries ajustam para telas menores:

/* Tablets e celulares (até 768px) */
@media screen and (max-width: 768px) {
  .cabecalho-conteudo {
    width: 100%;
    flex-direction: column;
    align-items: center;
    justify-content: center;
  }

  .cabecalho-conteudo i {
    margin-top: 16px;
    font-size: 34px;
  }

  .cabecalho-logo img {
    width: 160px;
    margin-top: 46px;
  }

  .cabecalho-botao {
    display: none;
  }

  .destaque {
    min-height: 60vh;
  }

  .destaque h1 {
    font-size: 34px;
    padding: 0 14px;
    width: 100%;
  }

  .destaque p {
    font-size: 14px;
  }

  .sobre-conteudo {
    flex-direction: column;
  }

  .cortes {
    flex-direction: column;
  }

  .corte img {
    max-width: 740px;
  }

  .botao-whatsapp img {
    max-width: 64px;
  }
}

/* Celulares (até 480px) */
  • Em até 768px: o cabeçalho vira coluna, o botão do topo some (o botão da frase principal continua), a “Sobre” e os serviços empilham e o WhatsApp diminui.
  • Em até 480px: o título da frase principal diminui para 24px (mostrado no arquivo completo mais abaixo).
Lembre: as media queries ficam no final do arquivo. Se viessem antes das regras normais, elas seriam sobrescritas.

Dinâmica: aplicando as animações, uma a uma

Agora vem a parte prática. Abra a pasta barbearia-inicial no editor, abra o index.html no navegador e aplique as seis etapas abaixo na ordem. Depois de cada uma, salve os arquivos, atualize o navegador (F5) e teste o efeito antes de seguir.

Como funciona: as etapas 1 a 5 mexem só no estilo.css. A etapa 6 mexe no index.html e no script.js. Em caso de dúvida, compare com o barbearia-final.zip, que tem tudo pronto.

Etapa 1: suavizar a cor do botão (transition)

Hoje o botão “Agendar horário” troca de cor de repente. A propriedade transition faz a mudança acontecer aos poucos. No estilo.css, acrescente uma linha dentro de .cabecalho-botao:

.cabecalho-botao {
  border: 1px solid #fff;
  padding: 8px 24px;
  border-radius: 4px;
  color: #fff;
  background-color: transparent;
  transition: background-color 0.8s;
}

.cabecalho-botao:hover {
  border: 1px solid #d34040;
  background-color: #d34040;
}
  • background-color: qual propriedade deve ser suavizada.
  • 0.8s: quanto tempo a mudança leva (0,8 segundo).

Teste: passe o mouse sobre “Agendar horário” no topo (no computador). A cor deve ir para o vermelho aos poucos. Diminua para 0.2s e compare.

Etapa 2: ícones que pulam (@keyframes + animation)

Aqui a animação é descrita à mão. O bloco @keyframes diz como o elemento está no início (from) e no fim (to), e a propriedade animation liga o bloco ao elemento. Acrescente o @keyframes e a linha animation:

.cabecalho-icones a:hover i {
  animation: sobe-e-desce 0.8s infinite alternate;
  color: #d34040;
}

@keyframes sobe-e-desce {
  from { transform: translateY(0) scale(1); }
  to   { transform: translateY(-8px) scale(1.1); }
}
  • sobe-e-desce: o nome que demos à animação (precisa ser igual nos dois lugares).
  • 0.8s: duração de cada ida.
  • infinite: repete para sempre enquanto o mouse estiver em cima.
  • alternate: vai e volta, em vez de recomeçar do início.
  • translateY(-8px) sobe 8 px e scale(1.1) aumenta 10%.

Teste: passe o mouse sobre um ícone de rede social. Ele deve pular e ficar vermelho.

Etapa 3: botão que pulsa

Mesma ideia da etapa anterior, agora no botão grande da frase principal. Ele “respira” (aumenta 7% e volta) enquanto o mouse está em cima:

.botao-contato:hover {
  animation: pulsa 0.8s alternate infinite;
}

@keyframes pulsa {
  from { transform: scale(1); }
  to   { transform: scale(1.07); }
}

Teste: passe o mouse no botão vermelho “Agendar horário”. Pergunta para pensar: por que aqui não precisamos de transition?

Etapa 4: zoom na foto dos serviços

Ao passar o mouse sobre a foto de um serviço, ela aumenta 20%. São duas peças: o transition na foto (para o aumento ser suave) e o :hover com transform: scale(1.2):

.corte img {
  width: 100%;
  max-width: 354px;
  transition: transform 0.2s;
}

.corte img:hover {
  transform: scale(1.2);
  z-index: 1;
}
  • transform: scale(1.2) aumenta o elemento para 120% do tamanho.
  • transition: transform 0.2s deixa o aumento durar 0,2 segundo.
  • O overflow: hidden do cartão (Passo 6) corta o que passa da borda, e o zoom parece acontecer dentro da foto.

Teste ao vivo: a miniatura abaixo mostra o efeito pronto. Passe o mouse sobre uma foto e arraste o canto inferior direito da moldura para a esquerda: quando ela ficar estreita, os cartões se empilham, como no celular.

Arraste o canto ↘ da moldura para diminuir
Foto 1
Corte normalR$ 45
Foto 2
Barba completaR$ 35
Foto 3
Corte e barbaR$ 85

No celular, abra o artigo no computador para arrastar a moldura.

Etapa 5: botão do WhatsApp (zoom e dica suaves)

O botão fixo do WhatsApp ganha duas melhorias: ele cresce um pouco ao passar o mouse, e o texto “Agende seu horário” aparece aos poucos, em vez de surgir de uma vez.

.botao-whatsapp img {
  max-width: 74px;
  transition: transform 0.4s;
}

.botao-whatsapp img:hover {
  transform: scale(1.1);
}
.botao-whatsapp .dica-texto {
  visibility: hidden;
  position: absolute;
  width: 120px;
  top: 8px;
  left: -144px;
  padding: 6px;
  border-radius: 8px;
  text-align: center;
  background-color: #232224;
  color: #fff;
  opacity: 0;
  transition: opacity 0.4s;
}

Teste: passe o mouse sobre o botão verde. Ele deve crescer e a dica aparecer suavemente. Experimente também navegar com a tecla Tab até ele.

Etapa 6: animações ao rolar a página com AOS

As cinco etapas anteriores reagem ao mouse. A última é diferente: o AOS (Animate On Scroll) anima os blocos quando eles entram na tela durante a rolagem. São três passos.

  1. Carregue a biblioteca.No <head> do index.html, antes do link do nosso estilo.css, acrescente o CSS e o JavaScript do AOS:
  <!-- Animações ao rolar a página (AOS) -->
  <link rel="stylesheet" href="https://unpkg.com/aos@2.3.1/dist/aos.css">
  <script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
  1. Marque os elementos.Acrescente o atributo data-aos (e, se quiser, data-aos-delay) nos elementos da tabela abaixo.
Anatomia de um elemento com AOS: o atributo data-aos define a animação e data-aos-delay define o atraso em milissegundos <div data-aos= “fade-up” data-aos-delay= “400” > liga o AOS ao elemento a animação (aparece subindo) atraso: 400 ms
Quem quer outra animação só troca o nome: fade-down, zoom-in, flip-up…
ElementoAtributos
<div class="cabecalho-icones">data-aos="fade-down"
<div class="cabecalho-logo">data-aos="fade-up" data-aos-delay="300"
<img> do logo (dentro dele)data-aos="flip-up" data-aos-delay="300" data-aos-duration="1500"
<div> do botão do topo (sem classe)data-aos="fade-down"
<main class="destaque conteiner">data-aos="fade-up" data-aos-delay="400"
<div> da imagem em “Sobre”data-aos="zoom-in" data-aos-delay="100"
<div class="sobre-descricao">data-aos="zoom-out-left" data-aos-delay="250"
Os três <div class="corte">data-aos="fade-up" com atraso de 150, 250 e 400
<a class="botao-whatsapp">data-aos="zoom-in-up" data-aos-delay="400"
  1. Ligue o AOS.No script.js, escreva uma única linha:
AOS.init();

Teste: atualize a página e role devagar: os blocos devem aparecer com animações diferentes. Se algum ficar invisível, veja a dica abaixo.

Sumiu algum bloco? Sem o AOS.init(), os elementos com data-aos ficam invisíveis. Confira se o link e o script do AOS carregaram (precisa de internet), se o script.js vem depois do AOS e se o nome da animação está certo.
Moderação: animação demais cansa e atrasa a leitura. Se você acha que está exagerado, apague os atributos de alguns elementos: o resto continua funcionando.

O que mudamos no projeto original

O repositório de partida funcionava, mas tinha detalhes que atrapalham o visitante ou o Google. Corrigimos, e vale usar essa lista como checklist nos seus projetos:

OriginalCorrigidoPor quê
Fonte carregada de sites externos (Google Fonts)Arquivos em fontes/ com @font-faceA página não depende de um serviço de fora para mostrar a fonte
Classes e arquivos com nomes em inglês (haircut, style.css, assets/)Nomes em português (corte, estilo.css, imagem/)Fica mais fácil de entender e de ensinar. Só as palavras da linguagem e das bibliotecas ficam em inglês
<html lang="en">lang="pt-BR"Idioma certo para leitores de tela e buscadores
“ás” (09:00 ás 19:00)“às”Erro de português: o certo tem crase
Links só com íconearia-label em cada umLeitor de tela precisa de um nome para o link
Preço em <button><span class="corte-preco">Não faz nada ao clicar, então não deve ser botão
outline: 0 em tudoa:focus-visible com contornoQuem usa teclado precisa ver onde está
Botão do topo com href="#"Link do WhatsAppUm botão “Agendar” que não leva a lugar nenhum frustra o visitante
width duplicado no iframeSó width="100%" e titleHTML válido e acessível
Tooltip só no hoverTambém no :focus-visibleO teclado também vê a dica
Texto “Lorem ipsum”Texto de exemplo em portuguêsTextos de enchimento esquecidos vão ao ar com frequência
“Copyright 2023”2026Ano desatualizado passa sensação de abandono
body { height: 100vh }RemovidoLimitava o corpo à altura da tela, mas o conteúdo é maior

Erros comuns

1. Caminho da imagem de fundo errado

O caminho do url() é contado a partir do arquivo CSS. Se o estilo.css está na raiz, é imagem/fundo.svg, e não ../imagem/fundo.svg.

2. Esquecer o AOS.init()

Sem ele, todos os elementos com data-aos ficam invisíveis. Confira também se o <script> do AOS vem antes do seu script.js.

3. Esquecer o content no ::before

Sem content: '', o pseudo-elemento não aparece e a foto de fundo some.

4. Ícones que viram quadradinhos ou números

O link do Font Awesome não carregou (sem internet, ou erro na URL). Abra com conexão e confira o <link> do <head>.

5. Esquecer de trocar o que é de exemplo

Número de WhatsApp, links das redes sociais (href="#"), mapa, nome e preços: tudo isso precisa ser trocado antes de publicar.

Código completo do exemplo

Esta é a versão final, com as seis animações (o gabarito da dinâmica). Monte a pasta e abra o index.html no navegador, com internet para carregar os ícones e as animações:

index.html
<!DOCTYPE html>
<html lang="pt-BR">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="description" content="Dev Barber: corte, barba e agendamento rápido pelo WhatsApp.">
  <title>Dev Barber - Sua barbearia!</title>

  <!-- Animações ao rolar a página (AOS) -->
  <link rel="stylesheet" href="https://unpkg.com/aos@2.3.1/dist/aos.css">
  <script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>

  <!-- Ícones (Font Awesome) -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css">

  <!-- Nosso CSS (por último, para vencer os anteriores em caso de empate).
       A fonte Sora é carregada por ele, a partir da pasta fontes/ -->
  <link rel="stylesheet" href="estilo.css">
</head>
<body>

  <!-- CABEÇALHO -->
  <div class="fundo-inicio">
    <header>

      <nav class="cabecalho-conteudo conteiner">
        <div class="cabecalho-icones" data-aos="fade-down">
          <a href="#" aria-label="Instagram"><i class="fa-brands fa-instagram fa-2x"></i></a>
          <a href="#" aria-label="Facebook"><i class="fa-brands fa-facebook fa-2x"></i></a>
          <a href="#" aria-label="TikTok"><i class="fa-brands fa-tiktok fa-2x"></i></a>
        </div>

        <div class="cabecalho-logo" data-aos="fade-up" data-aos-delay="300">
          <img
            data-aos="flip-up"
            data-aos-delay="300"
            data-aos-duration="1500"
            src="imagem/logo.svg"
            alt="Logo da barbearia"
          >
        </div>

        <div data-aos="fade-down">
          <a class="cabecalho-botao"
             href="https://api.whatsapp.com/send?phone=5511000000000&text=Quero%20fazer%20um%20agendamento%20hoje!"
             target="_blank" rel="noopener">
            Agendar horário
          </a>
        </div>
      </nav>

      <main class="destaque conteiner" data-aos="fade-up" data-aos-delay="400">
        <h1>ESTILO É UM REFLEXO DA SUA ATITUDE E SUA PERSONALIDADE.</h1>
        <p>Horário de funcionamento: 09:00 às 19:00</p>
        <a
          href="https://api.whatsapp.com/send?phone=5511000000000&text=Quero%20fazer%20um%20agendamento%20hoje!"
          class="botao-contato"
          target="_blank" rel="noopener"
        >
          Agendar horário
        </a>
      </main>

    </header>
  </div>

  <!-- SOBRE -->
  <section class="sobre">
    <div class="conteiner sobre-conteudo">
      <div data-aos="zoom-in" data-aos-delay="100">
        <img src="imagem/fotos-sobre.svg" alt="Fotos de cortes de cabelo e barba feitos na barbearia">
      </div>

      <div class="sobre-descricao" data-aos="zoom-out-left" data-aos-delay="250">
        <h2>Sobre</h2>
        <p>Aqui você encontra corte de cabelo, barba e atendimento sem pressa, em um ambiente pensado para você relaxar e sair com o visual que combina com a sua personalidade.</p>
        <p>Horário de funcionamento: <strong>09:00</strong> às <strong>19:00</strong></p>
      </div>
    </div>
  </section>

  <!-- SERVIÇOS -->
  <section class="servicos">

    <div class="servicos-conteudo conteiner">
      <h2>Serviços</h2>
      <p>Escolha o serviço, agende pelo WhatsApp e chegue na hora marcada. Os valores abaixo são exemplos e podem ser trocados pelos da sua barbearia.</p>
    </div>

    <section class="cortes">

      <div class="corte" data-aos="fade-up" data-aos-delay="150">
        <img src="imagem/corte1.png" alt="Corte normal">
        <div class="corte-info">
          <strong>Corte normal</strong>
          <span class="corte-preco">R$ 45,00</span>
        </div>
      </div>

      <div class="corte" data-aos="fade-up" data-aos-delay="250">
        <img src="imagem/corte2.png" alt="Barba completa">
        <div class="corte-info">
          <strong>Barba completa</strong>
          <span class="corte-preco">R$ 35,00</span>
        </div>
      </div>

      <div class="corte" data-aos="fade-up" data-aos-delay="400">
        <img src="imagem/corte3.png" alt="Corte e barba">
        <div class="corte-info">
          <strong>Corte e barba</strong>
          <span class="corte-preco">R$ 85,00</span>
        </div>
      </div>

    </section>
  </section>

  <!-- ONDE ESTAMOS -->
  <div class="servicos">
    <h2>Venha conhecer</h2>
  </div>

  <iframe
    title="Mapa com a localização da barbearia"
    src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d7476.4115143508725!2d-54.58602051703828!3d-20.45674056051272!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x9486e8954ac0b10d%3A0x7149aba0cb5688b8!2sShopping%20Campo%20Grande!5e0!3m2!1sen!2sbr!4v1688840244482!5m2!1sen!2sbr"
    width="100%" height="450" style="border:0;" allowfullscreen loading="lazy"
    referrerpolicy="no-referrer-when-downgrade"></iframe>

  <!-- RODAPÉ -->
  <footer class="rodape">
    <div class="rodape-icones">
      <a href="#" aria-label="Instagram"><i class="fa-brands fa-instagram fa-2x"></i></a>
      <a href="#" aria-label="Facebook"><i class="fa-brands fa-facebook fa-2x"></i></a>
      <a href="#" aria-label="TikTok"><i class="fa-brands fa-tiktok fa-2x"></i></a>
    </div>

    <div>
      <img src="imagem/logo.svg" alt="Logo da barbearia">
    </div>

    <p>Copyright 2026 | Dev Barber - Todos os direitos reservados.</p>
  </footer>

  <!-- BOTÃO FIXO DO WHATSAPP -->
  <a
    href="https://api.whatsapp.com/send?phone=5511000000000&text=Quero%20fazer%20um%20agendamento%20hoje!"
    class="botao-whatsapp"
    target="_blank" rel="noopener"
    data-aos="zoom-in-up" data-aos-delay="400"
  >
    <img src="imagem/whatsapp.svg" alt="Agendar pelo WhatsApp">
    <span class="dica-texto">Agende seu horário</span>
  </a>

  <script src="script.js"></script>
</body>
</html>
estilo.css
/* ===== FONTE SORA (hospedada no projeto, sem depender de site externo) ===== */
@font-face {
  font-family: 'Sora';
  src: url("fontes/sora-300.woff2") format("woff2");
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Sora';
  src: url("fontes/sora-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Sora';
  src: url("fontes/sora-500.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Sora';
  src: url("fontes/sora-600.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

/* ===== BASE ===== */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: 'Sora', sans-serif;
  background-color: #06121e;
}

img {
  width: 100%;
}

a {
  text-decoration: none;
  cursor: pointer;
}

i {
  color: #fff;
}

/* Contorno para quem navega pelo teclado */
a:focus-visible {
  outline: 3px solid #f68e36;
  outline-offset: 3px;
}

.conteiner {
  max-width: 1440px;
  margin: 0 auto;
  padding: 0 8px;
}

/* ===== CABEÇALHO ===== */
.fundo-inicio {
  position: relative;
}

.fundo-inicio::before {
  content: '';
  display: block;
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  z-index: -1;
  background-image: linear-gradient(to bottom, rgba(6, 18, 30, 0.1), #06121e), url("imagem/fundo.svg");
  background-size: cover;
  background-repeat: no-repeat;
  background-position: 50% 0;
  opacity: 0.9;
}

header {
  width: 100%;
}

.cabecalho-conteudo {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.cabecalho-icones {
  display: flex;
  gap: 14px;
}

.cabecalho-icones a:hover i {
  animation: sobe-e-desce 0.8s infinite alternate;
  color: #d34040;
}

@keyframes sobe-e-desce {
  from { transform: translateY(0) scale(1); }
  to   { transform: translateY(-8px) scale(1.1); }
}

.cabecalho-logo {
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 14px;
}

.cabecalho-botao {
  border: 1px solid #fff;
  padding: 8px 24px;
  border-radius: 4px;
  color: #fff;
  background-color: transparent;
  transition: background-color 0.8s;
}

.cabecalho-botao:hover {
  border: 1px solid #d34040;
  background-color: #d34040;
}

/* ===== HERO (frase principal) ===== */
.destaque {
  color: #fff;
  min-height: 80vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 14px;
}

.destaque h1 {
  text-align: center;
  line-height: 150%;
  max-width: 940px;
  font-size: 46px;
}

.destaque p {
  text-align: center;
  margin: 14px 0;
}

.botao-contato {
  padding: 14px 28px;
  color: #fff;
  background-color: #d94140;
  font-size: 18px;
  font-weight: 600;
  margin: 14px 0;
}

.botao-contato:hover {
  animation: pulsa 0.8s alternate infinite;
}

@keyframes pulsa {
  from { transform: scale(1); }
  to   { transform: scale(1.07); }
}

/* ===== SOBRE ===== */
.sobre {
  background-color: #06121e;
  color: #fff;
  overflow: hidden;
}

.sobre-conteudo {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 34px 14px 64px 14px;
}

.sobre-conteudo img {
  max-width: 570px;
}

.sobre-conteudo div {
  flex: 1;
}

.sobre-descricao h2 {
  font-size: 38px;
  margin-bottom: 24px;
}

.sobre-descricao p {
  margin-bottom: 14px;
  line-height: 150%;
}

/* ===== SERVIÇOS ===== */
.servicos {
  background-color: #fff;
  color: #232224;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 54px 14px;
}

.servicos-conteudo h2 {
  text-transform: uppercase;
  text-align: center;
  font-size: 38px;
  margin-bottom: 14px;
}

.servicos-conteudo p {
  line-height: 150%;
  margin-bottom: 14px;
  text-align: center;
  max-width: 780px;
}

.cortes {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 16px;
  padding: 0 44px 34px 44px;
}

.corte {
  flex: 1;
  display: flex;
  flex-direction: column;
  background-color: #fff;
  border-radius: 8px;
  box-shadow: 0 -1px 17px -4px rgba(0, 0, 0, 0.45);
  overflow: hidden;
}

.corte img {
  width: 100%;
  max-width: 354px;
  transition: transform 0.2s;
}

.corte img:hover {
  transform: scale(1.2);
  z-index: 1;
}

.corte-info {
  width: 100%;
  display: flex;
  justify-content: space-between;
  padding: 14px;
  background-color: #fff;
  z-index: 99;
}

.corte-preco {
  color: #fff;
  background-color: #d94140;
  padding: 4px 8px;
  border-radius: 4px;
}

/* ===== RODAPÉ ===== */
.rodape {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 54px 0;
  gap: 24px;
  color: #fff;
}

.rodape-icones {
  display: flex;
  gap: 8px;
}

/* ===== BOTÃO FIXO DO WHATSAPP ===== */
.botao-whatsapp {
  position: fixed;
  bottom: 14px;
  right: 24px;
  z-index: 99;
}

.botao-whatsapp img {
  max-width: 74px;
  transition: transform 0.4s;
}

.botao-whatsapp img:hover {
  transform: scale(1.1);
}

.botao-whatsapp .dica-texto {
  visibility: hidden;
  position: absolute;
  width: 120px;
  top: 8px;
  left: -144px;
  padding: 6px;
  border-radius: 8px;
  text-align: center;
  background-color: #232224;
  color: #fff;
  opacity: 0;
  transition: opacity 0.4s;
}

.botao-whatsapp:hover .dica-texto,
.botao-whatsapp:focus-visible .dica-texto {
  visibility: visible;
  opacity: 1;
}

/* ===== RESPONSIVIDADE ===== */

/* Tablets e celulares (até 768px) */
@media screen and (max-width: 768px) {
  .cabecalho-conteudo {
    width: 100%;
    flex-direction: column;
    align-items: center;
    justify-content: center;
  }

  .cabecalho-conteudo i {
    margin-top: 16px;
    font-size: 34px;
  }

  .cabecalho-logo img {
    width: 160px;
    margin-top: 46px;
  }

  .cabecalho-botao {
    display: none;
  }

  .destaque {
    min-height: 60vh;
  }

  .destaque h1 {
    font-size: 34px;
    padding: 0 14px;
    width: 100%;
  }

  .destaque p {
    font-size: 14px;
  }

  .sobre-conteudo {
    flex-direction: column;
  }

  .cortes {
    flex-direction: column;
  }

  .corte img {
    max-width: 740px;
  }

  .botao-whatsapp img {
    max-width: 64px;
  }
}

/* Celulares (até 480px) */
@media screen and (max-width: 480px) {
  .destaque h1 {
    font-size: 24px;
  }
}
script.js
AOS.init();

Resumo rápido

RecursoPara que serve
lang="pt-BR"Informa o idioma da página
display: flexAlinha cabeçalho, seções, cartões e rodapé
::before + z-index: -1Camada de fundo atrás do conteúdo
linear-gradientDegradê sobre a foto de fundo
transition / @keyframesSuavizar mudanças e criar animações
position: fixedBotão do WhatsApp preso à tela
transitionSuavizar uma mudança (cor, tamanho, opacidade)
@keyframes + animationCriar uma animação com início e fim
transform: scale()Aumentar ou diminuir um elemento
data-aos + AOS.init()Animar blocos ao rolar a página
aria-labelDar nome a links que só têm ícone
@media (max-width: 768px)Ajustar o layout para telas menores

Desafio para praticar

  1. Troque o nome “Dev Barber”, o número do WhatsApp, os serviços e os preços pelos de um negócio de verdade (ou inventado, mas com cara de real).
  2. Gere o seu mapa no Google Maps e substitua o iframe.
  3. Troque o vermelho #d94140 por outra cor de destaque em todo o CSS. Dica: use o “localizar e substituir” do editor.
  4. Acrescente um quarto serviço e ajuste o layout. O que acontece com a largura dos cartões?
  5. Mude as animações do AOS (por exemplo, zoom-in-up nos cartões) e teste data-aos-duration="1500".
  6. Desafio extra: crie uma sétima animação sua, como o preço de cada cartão aumentando de tamanho no hover.

Washington Paiva — instrutor de Desenvolvimento de Sistemas · washington.pro.br

Compartilhe:

Profissional engajado com as últimas tendências tecnológicas e de gestão, buscando continuamente aprimorar suas competências e compartilhar seu conhecimento.

Publicar comentário