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
A página tem seis blocos, e cada um vira uma seção do nosso código:
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).
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.pngestilo.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:
| Recurso | Para que serve | Como entra na página |
|---|---|---|
| Sora (fonte) | A fonte dos textos | Arquivos .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 |
| AOS | Animar 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 emfontes/.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.
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, usamosmax-widthnos casos específicos.i { color: #fff }deixa os ícones brancos (o Font Awesome usa a tag<i>).a:focus-visibledesenha 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;
}::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: absolutecomleft: 0; top: 0; width: 100%; height: 100%faz a camada cobrir toda a caixa.fundo-inicio, que por isso precisa deposition: relative.- O
background-imagetem 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: covercobre 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%;
}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: hiddenno cartão corta o que passar da borda arredondada. Vai ser importante na etapa 4, quando a foto aumentar ao passar o mouse.box-shadowcria 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
widthaparecia duas vezes (“100%” e “600”). O navegador usa só a primeira, mas a duplicação é um erro de código. Mantivemos apenaswidth="100%".
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: hiddeneopacity: 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: absolutedentro de um paifixedposiciona 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%20no lugar dos espaços.
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).
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.
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 escale(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.2sdeixa o aumento durar 0,2 segundo.- O
overflow: hiddendo 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.
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.
- Carregue a biblioteca.No
<head>doindex.html, antes do link do nossoestilo.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>- Marque os elementos.Acrescente o atributo
data-aos(e, se quiser,data-aos-delay) nos elementos da tabela abaixo.
fade-down, zoom-in, flip-up…| Elemento | Atributos |
|---|---|
<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" |
- 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.
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.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:
| Original | Corrigido | Por quê |
|---|---|---|
| Fonte carregada de sites externos (Google Fonts) | Arquivos em fontes/ com @font-face | A 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 ícone | aria-label em cada um | Leitor 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 tudo | a:focus-visible com contorno | Quem usa teclado precisa ver onde está |
Botão do topo com href="#" | Link do WhatsApp | Um botão “Agendar” que não leva a lugar nenhum frustra o visitante |
width duplicado no iframe | Só width="100%" e title | HTML válido e acessível |
| Tooltip só no hover | Também no :focus-visible | O teclado também vê a dica |
| Texto “Lorem ipsum” | Texto de exemplo em português | Textos de enchimento esquecidos vão ao ar com frequência |
| “Copyright 2023” | 2026 | Ano desatualizado passa sensação de abandono |
body { height: 100vh } | Removido | Limitava o corpo à altura da tela, mas o conteúdo é maior |
Erros comuns
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.
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.
Sem content: '', o pseudo-elemento não aparece e a foto de fundo some.
O link do Font Awesome não carregou (sem internet, ou erro na URL). Abra com conexão e confira o <link> do <head>.
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:
<!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
| Recurso | Para que serve |
|---|---|
lang="pt-BR" | Informa o idioma da página |
display: flex | Alinha cabeçalho, seções, cartões e rodapé |
::before + z-index: -1 | Camada de fundo atrás do conteúdo |
linear-gradient | Degradê sobre a foto de fundo |
transition / @keyframes | Suavizar mudanças e criar animações |
position: fixed | Botão do WhatsApp preso à tela |
transition | Suavizar uma mudança (cor, tamanho, opacidade) |
@keyframes + animation | Criar 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-label | Dar nome a links que só têm ícone |
@media (max-width: 768px) | Ajustar o layout para telas menores |
Desafio para praticar
- 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).
- Gere o seu mapa no Google Maps e substitua o
iframe. - Troque o vermelho
#d94140por outra cor de destaque em todo o CSS. Dica: use o “localizar e substituir” do editor. - Acrescente um quarto serviço e ajuste o layout. O que acontece com a largura dos cartões?
- Mude as animações do AOS (por exemplo,
zoom-in-upnos cartões) e testedata-aos-duration="1500". - 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:






Publicar comentário