Pular para o conteúdo
Gustavo Tutorial

HTML puro — parte 2

Continuação da trilha de HTML puro: recursos nativos que dispensam JavaScript, JavaScript além do básico, ferramentas de fluxo, entrega em produção, segurança e o glossário de siglas. Se você ainda não leu a primeira metade, comece por ela.

Revisão já conversamos sobre isso Novo ainda não vimos 6 partes · 27 tópicos · parte 2 de 2

Nenhum tópico com esse filtro.

PARTE 7

HTML que resolve sem JavaScript

Elementos nativos que substituem código que você provavelmente escreve à mão — e que já vêm com teclado, foco e acessibilidade prontos.

<dialog> — modal nativo

Novo HTML

Fazer um modal à mão significa: escurecer o fundo, travar a rolagem, prender o foco dentro dele, fechar no Esc, devolver o foco ao abrir e fechar. O <dialog> entrega tudo isso de fábrica.

html
<button id="abrir">Ver fotos do evento</button>

<dialog id="galeria">
  <img src="evento.webp" alt="Bar montado em casamento">
  <button id="fechar">Fechar</button>
</dialog>
javascript
const galeria = document.getElementById('galeria');

abrir.addEventListener('click', () => galeria.showModal());
fechar.addEventListener('click', () => galeria.close());

// fechar ao clicar fora (o ::backdrop é o próprio dialog)
galeria.addEventListener('click', (e) => {
  if (e.target === galeria) galeria.close();
});
css
dialog { border: 0; border-radius: 8px; padding: 0; max-width: 90vw; }
dialog::backdrop { background: rgba(0, 0, 0, .7); }

O ::backdrop é o fundo escuro — um pseudo-elemento que o navegador cria sozinho. Você não precisa daquela <div class="overlay">.

Atenção a um detalhe: showModal() e show() são diferentes. O primeiro trava a página e escurece o fundo; o segundo abre o dialog sem bloquear nada. Para lightbox e modal, é sempre showModal().

<details> — acordeão de graça

Novo HTML

Um FAQ que abre e fecha, sem uma linha de JavaScript, já acessível por teclado e indexável pelo Google.

html
<details name="faq">
  <summary>Vocês atendem fora de Campo Grande?</summary>
  <p>Sim, atendemos todo o estado. Deslocamento cobrado à parte.</p>
</details>

<details name="faq">
  <summary>Qual o prazo mínimo para reservar?</summary>
  <p>Trinta dias para eventos até 100 convidados.</p>
</details>

O atributo name igual nos dois faz eles funcionarem como acordeão exclusivo: abrir um fecha o outro. Isso é recente e resolve mais um pedaço de JavaScript.

css
summary {
  cursor: pointer;
  font-weight: 600;
  padding: 16px 0;
  list-style: none;           /* tira a setinha padrão */
}
summary::-webkit-details-marker { display: none; }

/* sua própria seta, girando ao abrir */
summary::after {
  content: "+";
  float: right;
  transition: transform .2s;
}
details[open] summary::after { transform: rotate(45deg); }
Ganho de SEO: diferente de um acordeão feito em JavaScript, o conteúdo fechado do <details> está no HTML e o Google lê. Um FAQ assim pode virar resultado enriquecido na busca.

<picture> e srcset — imagem responsiva de verdade

Novo HTML

O loading="lazy" adia o carregamento; ele não diminui o arquivo. Mandar uma imagem de 1600px para um celular de 400px continua desperdiçando banda. O srcset resolve: você oferece versões, o navegador escolhe.

html
<img src="hero-800.webp"
     srcset="hero-400.webp   400w,
             hero-800.webp   800w,
             hero-1600.webp 1600w"
     sizes="(max-width: 700px) 100vw, 800px"
     alt="Bar montado em festa ao ar livre"
     width="1600" height="900">
AtributoO que diz ao navegador
srcsetQue versões existem e a largura real de cada uma (w)
sizesQue espaço a imagem vai ocupar no layout, por faixa de tela
srcReserva, para navegador antigo

<picture> — trocar o formato ou a própria foto

Quando não é só tamanho: servir AVIF para quem suporta, ou usar um recorte vertical no celular e horizontal no desktop.

html
<picture>
  <source type="image/avif" srcset="hero.avif">
  <source media="(max-width: 700px)" srcset="hero-vertical.webp">
  <img src="hero.jpg" alt="Bar montado em festa ao ar livre">
</picture>
Impacto real: numa landing page com foto de topo, isso costuma ser o maior ganho isolado de LCP no celular. Exportar três tamanhos dá cinco minutos de trabalho e corta metade do peso da página.

Popover API

Novo HTML

Para tudo que aparece “por cima” mas não bloqueia a página: menu suspenso, tooltip, painel de filtro, aviso. Abre e fecha por atributo, sem JavaScript.

html
<button popovertarget="menu-mobile">Menu</button>

<nav id="menu-mobile" popover>
  <a href="#sobre">Sobre</a>
  <a href="#contato">Contato</a>
</nav>

Só isso. O navegador cuida de abrir, fechar ao clicar fora, fechar no Esc e colocar acima de tudo — sem z-index, porque popover vive numa camada própria, acima da página inteira.

Comparando com o <dialog>: dialog é para o que exige decisão — o resto da página fica bloqueado. Popover é para o que é auxiliar — a pessoa pode simplesmente clicar fora e continuar. Menu mobile é popover; confirmação de exclusão é dialog.
PARTE 8

JavaScript além do básico

Sintaxe e recursos do navegador que aparecem em todo código moderno — e que você vai encontrar em qualquer exemplo que buscar na internet.

Métodos de array

Novo JS

São a forma moderna de trabalhar com listas. Em vez de um for com contador, você descreve o que quer.

javascript
const drinks = [
  { nome: "Negroni",  alcool: true,  preco: 32 },
  { nome: "Maracujá", alcool: false, preco: 18 },
  { nome: "Gin Tônica", alcool: true, preco: 28 }
];

// map — transforma cada item, devolve lista do mesmo tamanho
const nomes = drinks.map(d => d.nome);
// ["Negroni", "Maracujá", "Gin Tônica"]

// filter — devolve só os que passam no teste
const semAlcool = drinks.filter(d => !d.alcool);

// find — devolve o PRIMEIRO que bate (ou undefined)
const negroni = drinks.find(d => d.nome === "Negroni");

// reduce — reduz a lista a um único valor
const total = drinks.reduce((soma, d) => soma + d.preco, 0);  // 78

// some / every — devolvem true ou false
const temSemAlcool = drinks.some(d => !d.alcool);   // true
const todosCaros   = drinks.every(d => d.preco > 20); // false

// sort — ordena (cuidado: altera o array original)
const porPreco = [...drinks].sort((a, b) => a.preco - b.preco);

O ganho fica claro quando você encadeia — gerar o HTML da carta de drinks, só os sem álcool, ordenados por preço:

javascript
const html = drinks
  .filter(d => !d.alcool)
  .sort((a, b) => a.preco - b.preco)
  .map(d => `<li>${d.nome} — R$ ${d.preco}</li>`)
  .join('');
Regra para escolher: quer a lista transformada? map. Quer parte da lista? filter. Quer um item? find. Quer um número só? reduce. Quer sim ou não? some ou every.

Destructuring, spread e outros atalhos

Novo JS

Sintaxe que aparece em praticamente todo código dos últimos anos. Saber ler isso é o que separa entender um exemplo da internet de copiar sem saber o que faz.

javascript
// DESTRUCTURING — extrair campos de um objeto
const drink = { nome: "Negroni", preco: 32, alcool: true };

const { nome, preco } = drink;        // cria duas variáveis
const { nome: titulo } = drink;       // renomeando
const { estoque = 0 } = drink;        // valor padrão se não existir

// SPREAD (...) — espalhar o conteúdo
const copia = { ...drink };                    // cópia rasa
const atualizado = { ...drink, preco: 35 };    // cópia com campo trocado
const juntos = [...listaA, ...listaB];         // juntar arrays

// ENCADEAMENTO OPCIONAL (?.) — não quebra se não existir
const cidade = cliente?.endereco?.cidade;   // undefined em vez de erro

// OPERADOR ?? — usa o padrão só quando é null ou undefined
const titulo2 = config.titulo ?? "Sem título";
// (diferente do ||, que também troca string vazia e zero)

// TEMPLATE LITERAL — texto com variáveis e quebras de linha
const msg = `Olá ${nome}, o preço é R$ ${preco}.`;
Diferença que pega muita gente: || considera 0 e "" como “vazio” e troca pelo padrão. O ?? só troca quando é null ou undefined. Num formulário onde zero é resposta válida, essa diferença é bug.

Módulos ES

Novo JS

Dividir o JavaScript em arquivos que se importam entre si. Funciona nativamente no navegador, sem bundler — basta declarar type="module".

whatsapp.js
export const NUMERO = "5567999999999";

export function abrirWhatsApp(mensagem) {
  const url = `https://wa.me/${NUMERO}?text=${encodeURIComponent(mensagem)}`;
  window.open(url, '_blank', 'noopener');
}
index.html
<script type="module">
  import { abrirWhatsApp } from './whatsapp.js';

  form.addEventListener('submit', (e) => {
    e.preventDefault();
    abrirWhatsApp('Olá! Vim pelo site.');
  });
</script>
DetalheConsequência
Módulo roda em modo estritoErros silenciosos viram erros visíveis
Carrega adiado por padrãoNão precisa colocar no fim do <body>
Escopo próprioVariáveis não vazam para o global
Exige servidorNão funciona abrindo o arquivo direto (file://)
Aquele último item importa para você. Módulos quebram o modelo de “abrir o HTML com dois cliques”. Se a entrega precisa funcionar sem servidor, mantenha o script inline. É mais um motivo pelo qual o arquivo único continua fazendo sentido no seu caso.

Debounce e throttle

Novo JS

Duas formas de conter eventos que disparam demais. Debounce espera a pessoa parar; throttle executa no máximo a cada X tempo.

javascript
function debounce(fn, espera = 300) {
  let t;
  return function (...args) {
    clearTimeout(t);
    t = setTimeout(() => fn.apply(this, args), espera);
  };
}

function throttle(fn, intervalo = 200) {
  let liberado = true;
  return function (...args) {
    if (!liberado) return;
    liberado = false;
    fn.apply(this, args);
    setTimeout(() => { liberado = true; }, intervalo);
  };
}

// buscar só quando parar de digitar
campo.addEventListener('input', debounce(buscarCep, 500));

// recalcular no máximo 5x por segundo ao redimensionar
window.addEventListener('resize', throttle(ajustarLayout, 200));
SituaçãoUsePor quê
Campo de buscaDebounceSó interessa o texto final
Validar CEP enquanto digitaDebounceEvita chamar a API a cada tecla
Redimensionar janelaThrottlePrecisa acompanhar, mas não 60x por segundo
RolagemrequestAnimationFrameMelhor que os dois para efeito visual

APIs do navegador que valem conhecer

Novo JS

Recursos nativos que resolvem problemas reais de landing page — nenhum precisa de biblioteca.

Web Share — o compartilhar nativo do celular

Abre o menu do sistema, com WhatsApp, Instagram e tudo que a pessoa tem instalado. Faz muito mais sentido que uma fileira de botões de rede social.

javascript
botaoCompartilhar.addEventListener('click', async () => {
  if (!navigator.share) {          // desktop geralmente não tem
    botaoCompartilhar.hidden = true;
    return;
  }
  try {
    await navigator.share({
      title: 'Art Bar',
      text: 'Bar de drinks para eventos',
      url: window.location.href
    });
  } catch (e) { /* a pessoa cancelou — não é erro */ }
});

Clipboard — copiar com um clique

javascript
await navigator.clipboard.writeText('contato@artbar.com.br');
// exige HTTPS e um gesto da pessoa (clique)

Outras que aparecem

APIPara que serveObservação
GeolocationLocalização da pessoaPede permissão; use só se agregar de verdade
NotificationNotificação do sistemaPedir isso de cara afasta visitante
matchMediaLer media query pelo JSDetectar tema escuro, tela pequena
ResizeObserverAvisa quando um elemento muda de tamanhoMelhor que ouvir resize
URLSearchParamsLer parâmetros da URLSaber de qual anúncio a pessoa veio
rastrear origem sem analytics
// artbar.com.br/?origem=instagram
const origem = new URLSearchParams(location.search).get('origem');

// entra na mensagem do WhatsApp — você descobre o que converteu
const msg = `Olá! Vim pelo site${origem ? ` (${origem})` : ''}.`;

PWA e Service Worker

Novo JS

PWA (Progressive Web App) é um site que o celular trata quase como aplicativo: instala na tela inicial, abre sem barra de navegador e funciona offline. São dois ingredientes.

1. O manifesto — dados de instalação

manifest.json
{
  "name": "Art Bar",
  "short_name": "Art Bar",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#0B0B0B",
  "theme_color": "#7A1C30",
  "icons": [
    { "src": "/icone-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/icone-512.png", "sizes": "512x512", "type": "image/png" }
  ]
}

2. O service worker — o que faz funcionar offline

É um script que fica entre o site e a internet, interceptando requisições. Ele guarda arquivos e responde com a cópia quando não há conexão.

sw.js
const CACHE = 'artbar-v1';

self.addEventListener('install', (e) => {
  e.waitUntil(
    caches.open(CACHE).then(c => c.addAll(['/', '/index.html']))
  );
});

self.addEventListener('fetch', (e) => {
  e.respondWith(
    caches.match(e.request).then(r => r || fetch(e.request))
  );
});
Antes de vender PWA para um cliente: o service worker guarda versões antigas com muita eficiência. Se você não mudar o nome do cache a cada atualização, o cliente vai continuar vendo o site velho e jurar que você não publicou. É o problema número um de quem começa com PWA.
Vale a pena quando: o site é consultado repetidamente — cardápio, catálogo, área do aluno. Numa landing page que a pessoa visita uma vez e vai pro WhatsApp, não agrega.
PARTE 9

Ferramentas e fluxo de trabalho

Nada disso é tecnologia web — é o que determina quanto tempo você leva para entregar. Provavelmente a parte com maior retorno por hora estudada.

Emmet

Novo

Já vem no VS Code, ligado. Você escreve uma abreviação, aperta Tab, e ela vira HTML completo. É o item desta lista que mais economiza tempo por dia.

você digita
section.hero>h1+p+a.botao{Falar no WhatsApp}
Tab produz
<section class="hero">
  <h1></h1>
  <p></p>
  <a href="" class="botao">Falar no WhatsApp</a>
</section>
SímboloSignificaExemplo
>Filhoul>li
+Irmãoh1+p
^Sobe um níveldiv>p^span
*Repeteli*5
. / #Classe / iddiv.card#topo
{}Texto dentroa{Clique}
[]Atributoimg[alt="Bar"]
$Número da repetiçãoli.item$*3
três exemplos que valem decorar
!                    → o esqueleto HTML inteiro, com head e meta
ul>li.item$*5>a      → lista de 5 itens numerados, cada um com link
.card*3>img+h3+p     → três cards com imagem, título e parágrafo

Funciona em CSS também: m10-20 vira margin: 10px 20px;, df vira display: flex;, p20 vira padding: 20px;.

VS Code na prática

Novo

Atalhos que mudam o ritmo

AtalhoO que faz
Alt + cliqueMúltiplos cursores — edita vários lugares ao mesmo tempo
Ctrl + DSeleciona a próxima ocorrência da palavra. Renomear classe fica trivial
Alt + ↑ / ↓Move a linha para cima ou para baixo
Shift + Alt + ↓Duplica a linha
Ctrl + /Comenta ou descomenta
Ctrl + PAbre arquivo pelo nome
Ctrl + Shift + FBusca em todos os arquivos do projeto
F2Renomeia em todos os lugares de uma vez

Extensões que valem para o seu fluxo

ExtensãoPara que
Live ServerSobe um servidor local e recarrega ao salvar. Essencial
PrettierFormata o código sozinho
Auto Rename TagRenomeia a tag de fechamento junto com a de abertura
Color HighlightMostra a cor real ao lado do hexadecimal
Error LensMostra o erro na própria linha, sem precisar passar o mouse

Snippets — seus próprios atalhos

Em Arquivo → Preferências → Configurar snippets, você cria abreviações suas. É aqui que entra aquele bloco de código que você copia de projeto em projeto.

html.json
{
  "Botão WhatsApp": {
    "prefix": "zap",
    "body": [
      "<a href=\"https://wa.me/$1\" class=\"botao\"",
      "   target=\"_blank\" rel=\"noopener\">$2</a>"
    ],
    "description": "Link de WhatsApp"
  }
}

Agora digitar zap + Tab insere o bloco, com o cursor parando em cada $1, $2. É a versão organizada do “copiar e colar do projeto anterior”.

Prettier e formatação

Novo

O Prettier reescreve o seu código com formatação consistente — indentação, aspas, quebras de linha — toda vez que você salva. Você para de pensar em alinhamento.

.prettierrc
{
  "printWidth": 90,
  "singleQuote": true,
  "tabWidth": 2
}

Para formatar ao salvar, ative Format On Save nas configurações do VS Code.

Num arquivo único e longo, isso rende mais do que parece: código formatado de forma consistente é muito mais rápido de escanear com o olho quando você precisa achar uma seção seis meses depois.

Do Figma para o código

Novo

O Figma é a ferramenta padrão de design de interface. Mesmo que você não desenhe nele, vai receber layout nele — e saber extrair as informações certas economiza muita conversa.

No FigmaO que você extrai
Painel de inspeçãoCor exata, tamanho de fonte, espaçamento, raio de borda
Estilos de corA paleta — vira seu bloco :root
Estilos de textoA escala tipográfica
Auto layoutJá é Flexbox: direção, gap e alinhamento saem direto
ExportarSVG para ícones e logo, WebP/PNG para fotos
Não copie o CSS que o Figma gera. Ele produz posicionamento absoluto e medidas fixas, que não são responsivas. Use o painel para ler os valores, mas escreva o layout você mesmo com Flex e Grid.

Um hábito que evita retrabalho: antes de codar, pergunte ao designer o que acontece no celular. O Figma quase sempre mostra só a versão desktop, e adivinhar o comportamento responsivo é a fonte mais comum de “não era isso que eu queria”.

Variáveis do Figma e o caminho dos tokens

Novo

Os estilos de cor viram o bloco :root, como no tópico anterior. As Variáveis do Figma vão além: guardam cor, número (espaço, raio) e texto, podem apontar uma para a outra — o mesmo esquema de primitivo e semântico — e têm modos, que é como o designer desenha claro e escuro no mesmo arquivo.

No FigmaNo CSS
Coleção de variáveisum bloco :root
Variável que aponta para outra (alias)var(--outra)
Modo claro e modo escuro[data-tema="escuro"] ou @media (prefers-color-scheme: dark)
Variável de númerotoken de espaço ou de raio, em px
Dev Modeo painel em que o dev lê o nome da variável em vez de um hex solto

O caminho de volta, do código para o Figma, passa pelo formato do Design Tokens Community Group do W3C: um JSON em que cada token tem $type e $value, e o alias é escrito entre chaves. Plugins de tokens para Figma, como o Tokens Studio, importam esse formato. A ferramenta Design system exporta nele.

json
{
  "cor": {
    "vinho-700": { "$type": "color", "$value": "#7A1C30" },
    "destaque":  { "$type": "color", "$value": "{cor.vinho-700}" }
  },
  "espaco": {
    "3": { "$type": "dimension", "$value": "16px" }
  }
}
Se o arquivo do cliente não usa variáveis — só cores soltas e textos sem estilo —, isso é o inventário dando o alarme dentro do Figma. Vale apontar antes de codar: cada cor solta vira uma pergunta depois. E o Figma tem um servidor MCP oficial que deixa assistentes como o Claude Code ler o frame e as variáveis direto do arquivo; o aviso do tópico anterior continua valendo, porque a IA também tende a copiar medida fixa.
PARTE 10

Entrega e produção

A parte que separa “o site funciona na minha máquina” de “o cliente está no ar e vendendo”.

HTML único autocontido

Revisão

Um único arquivo .html com tudo dentro: o CSS numa tag <style> no <head>, o JS numa tag <script> no fim do <body>. Sem arquivos separados, sem instalação, sem etapa de build.

GanhoCusto
Entrega de um arquivo sóArquivo longo de navegar quando cresce
Sem caminho quebrado de CSS ou JSReaproveitar código entre projetos é copiar e colar
Menos requisições — carrega mais rápidoNada é cacheado separadamente entre páginas
Hospeda em qualquer lugarDiff do Git mistura HTML, CSS e JS
Nada envelhece: abre igual daqui a cinco anos—
Sobre organização: separar arquivos não protege contra perda — isso é trabalho de backup e de Git. O que separar resolve é conforto de edição em projeto grande. Numa landing page, comentários de seção no CSS (/* === MENU === */) resolvem a navegação sem abrir mão da entrega enxuta.

Um meio-termo válido: trabalhar com arquivos separados e juntar tudo só na hora de entregar. Você ganha conforto durante o desenvolvimento e mantém a entrega de um arquivo só.

Performance

Novo

Em landing page, velocidade é conversão: cada segundo a mais de carregamento derruba a taxa de gente que fica. E quase sempre o culpado é o mesmo — imagem.

Imagens, na ordem de impacto

  1. Redimensione antes de subir. Foto de celular tem 4000px de largura. Se ela aparece num espaço de 1200px, exporte em 1200px. É a economia mais fácil que existe.
  2. Converta para WebP. Mesma qualidade visual, arquivo 25–35% menor que JPG.
  3. loading="lazy" em tudo que está abaixo da dobra — nunca na imagem do topo.
  4. width e height no HTML, para o navegador reservar o espaço e a página não pular enquanto carrega.

As três métricas que o Google mede

SiglaMedeAlvoVilão típico
LCPQuando o maior elemento apareceabaixo de 2,5sImagem de hero pesada
CLSQuanto o layout “pula” ao carregarabaixo de 0,1Imagem sem dimensão; fonte trocando
INPDemora para responder ao toqueabaixo de 200msJS pesado bloqueando a thread

Outros pontos que rendem

  • Fontes: use &display=swap e carregue só os pesos que realmente aparecem. Cada peso extra é um arquivo.
  • Vídeo do YouTube: um embed direto custa centenas de KB antes de alguém apertar play. Use uma imagem estática com botão e só injete o <iframe> no clique.
  • Teste no lugar certo: PageSpeed Insights, na aba de celular, com conexão lenta simulada. Seu notebook no wi-fi de casa mente.

SEO e Open Graph

Novo HTML

SEO em landing page de serviço local é simples: título claro, descrição honesta, HTML semântico, site rápido e dados de negócio marcados. Não tem truque.

o mínimo no head
<title>Art Bar — Bar de Drinks para Casamentos | Campo Grande MS</title>
<meta name="description"
      content="Bar de drinks completo para casamentos e eventos
               corporativos em MS. Peça seu orçamento no WhatsApp.">
<link rel="canonical" href="https://artbar.com.br/">

<!-- Open Graph: como o link aparece no WhatsApp e redes -->
<meta property="og:title"       content="Art Bar — Bar de Drinks">
<meta property="og:description" content="Coquetelaria autoral para o seu evento.">
<meta property="og:image"       content="https://artbar.com.br/capa.jpg">
<meta property="og:url"         content="https://artbar.com.br/">
<meta property="og:type"        content="website">
O og:image não é detalhe. Sem ele, o link compartilhado no WhatsApp aparece como um retângulo cinza sem imagem. Considerando que é por ali que o cliente vai divulgar, essa tag sozinha muda a taxa de clique. Tamanho: 1200×630.

JSON-LD — dados estruturados

Um bloco que descreve o negócio em linguagem de máquina. É o que permite ao Google mostrar endereço, telefone e avaliação direto no resultado da busca.

html
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "LocalBusiness",
  "name": "Art Bar",
  "telephone": "+5567999999999",
  "email": "contato@artbar.com.br",
  "areaServed": "Mato Grosso do Sul",
  "url": "https://artbar.com.br"
}
</script>

Checklist de SEO on-page

  • Um <h1> por página, com a palavra que a pessoa busca
  • <html lang="pt-BR">
  • alt descritivo em todas as imagens
  • URL limpa, sem parâmetro estranho
  • Site rápido no celular
  • Google Meu Negócio preenchido — para negócio local, pesa mais que o site

Hospedagem e deploy

Novo

Deploy é o ato de colocar o site no ar. Para arquivo estático, hoje isso é gratuito e leva minutos.

OpçãoComo funcionaBom paraSite de cliente no plano gratuito?
NetlifyArraste a pasta no site e está no ar. Ou conecte ao GitO caminho mais rápido para landing pageSim — a Netlify diz que o gratuito aceita projeto comercial
VercelIgual, integrado ao Git. Dona do Next.jsMesma coisa; obrigatório se um dia usar NextNão — o gratuito é só para uso pessoal; cliente vai no plano Pro, pago
GitHub PagesPublica direto de um repositórioProjeto pessoal, portfólioNão — o GitHub proíbe usar para negócio
Cloudflare PagesRede global muito rápidaPúblico espalhado geograficamenteA documentação oficial não trata disso: leia os termos antes
Hospedagem tradicionalVocê envia por FTP para um servidorQuando o cliente já paga uma e quer usarÉ paga desde o começo

Todas as opções gratuitas acima já vêm com HTTPS e CDN incluídos. Para um HTML único, a diferença prática entre elas é pequena — escolha pela facilidade de você mesmo atualizar depois. Mas gratuito não quer dizer liberado para cliente: a última coluna é o que os termos de cada uma dizem, e o que fazer com isso na hora de cobrar está em A mensalidade: o que ela compra.

Uma decisão que vale conversar com o cliente: a conta é dele ou sua? Se o site fica na sua conta, ele depende de você para qualquer coisa — e você fica responsável para sempre. Criar a conta no nome dele, com o e-mail dele, evita um tipo específico de dor de cabeça futura.

Domínio, DNS e HTTPS

Novo

Domínio é o endereço (artbar.com.br), alugado por ano. No Brasil, .com.br se registra no registro.br, direto e barato.

DNS é a lista telefônica da internet: traduz o nome do domínio para o endereço do servidor. Quando você aponta um domínio para a Netlify, está editando DNS.

RegistroPara que serve
AAponta o domínio para um endereço IP
CNAMEAponta um subdomínio para outro nome (www → site.netlify.app)
MXDefine quem recebe o e-mail do domínio
TXTVerificações — Google, segurança de e-mail
Propagação de DNS leva tempo. Mudança pode demorar de minutos a algumas horas para valer em todo lugar. Se você acabou de apontar e não abriu, não mexa de novo — espere. Mexer duas vezes é o que costuma criar o problema de verdade.

HTTPS é a versão criptografada do protocolo, sinalizada pelo cadeado. Hoje é obrigatório: navegador marca site sem HTTPS como “não seguro” e o Google rebaixa. Nas hospedagens citadas, ele é automático e gratuito — você não precisa comprar certificado.

Git e versionamento

Novo

Git guarda o histórico do seu projeto: cada versão salva fica registrada, e você pode voltar a qualquer ponto. Resolve de vez o site-final-v2-AGORA-VAI.html.

o essencial
git init                      # começa a versionar esta pasta
git add .                     # marca o que entra na próxima versão
git commit -m "Hero e menu"   # grava a versão com uma descrição
git log --oneline             # lista o histórico

git remote add origin URL     # conecta a um repositório no GitHub
git push                      # envia
git pull                      # traz o que mudou lá

O ganho direto para você: trabalhar sem medo. Pode reescrever a seção inteira sabendo que a versão anterior está guardada. E se o cliente disser “prefiro como estava antes”, você tem o antes.

Bônus que resolve o deploy junto: conectando o repositório à Netlify ou à Vercel, cada git push publica o site automaticamente. Você deixa de subir arquivo na mão — salvar e enviar vira a mesma ação.

DevTools do navegador

Novo

F12 no Chrome. É a ferramenta que mais acelera aprendizado de front-end, porque mostra o efeito de cada mudança na hora.

AbaPara que usar
ElementsVer o HTML real e editar CSS ao vivo, sem salvar arquivo
ConsoleLer erros de JavaScript. Primeira parada quando “não funciona”
NetworkVer o que carrega, o tamanho de cada arquivo e o que está pesando
LighthouseRelatório de performance, SEO e acessibilidade com nota
Device toolbarSimular celular e testar responsividade (Ctrl+Shift+M)
Fluxo que vale adotar: antes de entregar, rode o Lighthouse na aba mobile. Ele aponta imagem sem alt, contraste ruim, imagem grande demais e falta de meta tag — uma revisão automática do seu próprio trabalho, em trinta segundos.

No Elements você também consegue inspecionar qualquer site da internet e ver exatamente como um efeito foi feito. É a melhor forma de estudar layout que você achou bonito.

Analytics e rastreamento

Novo

Sem medição, ninguém sabe se a landing page funcionou. Dois números importam: quanta gente chegou e quanta clicou no WhatsApp.

FerramentaPara que
Google Analytics 4Visitas, origem do tráfego, eventos de conversão
Google Search ConsoleQue buscas trazem gente ao site. Gratuito e subaproveitado
Meta PixelNecessário se o cliente anuncia no Instagram ou Facebook
Alternativas levesPlausible, Umami — mais simples e sem cookie
marcar o clique no WhatsApp
document.querySelectorAll('[href*="wa.me"]').forEach(link => {
  link.addEventListener('click', () => {
    if (typeof gtag === 'function') {
      gtag('event', 'clique_whatsapp', { local: link.dataset.origem });
    }
  });
});
LGPD: analytics com cookie exige aviso e base legal. Não instale rastreamento sem o cliente pedir e saber — a responsabilidade legal é dele, mas quem colocou a tag foi você. Deixe o ponto marcado no código e a decisão com ele.
PARTE 11

Segurança

Landing page estática tem pouca superfície de ataque — mas os poucos riscos que existem são exatamente os que passam despercebidos.

XSS — quando texto vira código

Novo JS

XSS (Cross-Site Scripting) acontece quando conteúdo vindo de fora é inserido na página como HTML e o navegador executa. O caminho mais comum é o innerHTML.

o erro
// nome vem da URL: ?nome=<img src=x onerror="alert(1)">
const nome = new URLSearchParams(location.search).get('nome');

saudacao.innerHTML = `Olá, ${nome}!`;   // executa o código do atacante
a correção
saudacao.textContent = `Olá, ${nome}!`;   // insere como texto, não como HTML
Origem do conteúdoPode usar innerHTML?
Escrito por você no códigoSim
Parâmetro da URLNão
Campo de formulárioNão
Resposta de APINão
localStorageNão — o visitante edita
Regra única que basta: textContent por padrão. Só troque por innerHTML quando você mesmo escreveu aquele HTML no código — e nunca com interpolação de dado externo no meio.

Content Security Policy

Novo

Uma declaração que diz ao navegador de onde a página pode carregar coisas. Se um script de outra origem for injetado, o navegador simplesmente recusa.

html
<meta http-equiv="Content-Security-Policy"
      content="default-src 'self';
               style-src  'self' 'unsafe-inline' https://fonts.googleapis.com;
               font-src   https://fonts.gstatic.com;
               img-src    'self' data:;
               script-src 'self' 'unsafe-inline'">
Entregando HTML único, existe um conflito: CSP rigoroso bloqueia CSS e JS inline, que é justamente o seu formato. Por isso o exemplo acima usa 'unsafe-inline' — o que enfraquece bastante a proteção. A solução correta é usar nonce, mas isso exige servidor gerando a página. Numa landing page sem dados sensíveis, CSP é ganho pequeno; saber que existe importa mais que aplicar agora.

Outros cabeçalhos que a hospedagem configura

CabeçalhoProtege contra
X-Frame-Options: DENYSeu site ser embutido em iframe de golpe
X-Content-Type-Options: nosniffNavegador adivinhar tipo de arquivo errado
Referrer-PolicyVazar a URL de origem ao sair do site
Strict-Transport-SecurityAcesso por HTTP sem criptografia

Segredos, chaves e dependências

Novo

A regra é curta: tudo que está no front-end é público. Minificar, ofuscar ou esconder no meio do arquivo não muda isso — é só apertar F12.

ItemPode ficar no HTML?
Número de WhatsApp, e-mail públicoSim — é informação de contato
ID do Google AnalyticsSim — foi feito para ser público
Chave de API do Google MapsSim, se restrita ao domínio no painel do Google
Chave de envio de e-mailNão. Nunca
Token de banco de dados ou CRMNão. Nunca
Senha de qualquer coisaNão. Nunca

Outras três precauções

  • rel="noopener" em todo target="_blank". Sem isso, a página aberta consegue manipular a sua pela referência window.opener.
  • Cuidado com script de CDN aleatório. Você está executando código de terceiro na página do seu cliente. Use CDNs conhecidas, com versão fixa — nunca @latest, que muda sem aviso.
  • Nada de .env, backup ou .git na pasta publicada. Se o servidor entrega arquivo estático, ele entrega esses também para quem souber o caminho.
Teste rápido antes de entregar: abra o site publicado, aperte Ctrl+U e leia o fonte como se fosse um estranho. Tudo que você não gostaria de ver ali precisa sair — porque qualquer visitante vê exatamente isso.
PARTE 12

Glossário de siglas

Para consultar quando aparecer numa documentação, num tutorial ou numa conversa.

Siglas e termos

Revisão
SiglaSignificaO que é
APIApplication Programming InterfaceContrato que permite dois sistemas conversarem
CDNContent Delivery NetworkRede de servidores que entrega arquivos do ponto mais próximo
CLSCumulative Layout ShiftO quanto o layout pula durante o carregamento
CMSContent Management SystemPainel para o cliente editar o conteúdo sozinho (WordPress, Strapi)
CSRClient-Side RenderingO HTML é montado no navegador pelo JavaScript
CSSCascading Style SheetsA linguagem de estilo
DNSDomain Name SystemTraduz o nome do domínio para o endereço do servidor
DOMDocument Object ModelA árvore de elementos que o JavaScript manipula
HTMLHyperText Markup LanguageA linguagem de estrutura
HTTP/HTTPSHyperText Transfer Protocol (Secure)O protocolo da web; a versão “S” é criptografada
INPInteraction to Next PaintQuanto o site demora a responder a um toque
JSONJavaScript Object NotationFormato de texto para trocar dados
LCPLargest Contentful PaintQuando o maior elemento da tela termina de aparecer
LGPDLei Geral de Proteção de DadosLei brasileira sobre dados pessoais
RESTRepresentational State TransferPadrão comum de organização de APIs
SDKSoftware Development KitPacote de ferramentas para integrar com um serviço
SEOSearch Engine OptimizationPráticas para aparecer melhor na busca
SPASingle Page ApplicationSite que troca o conteúdo sem recarregar a página
SSGStatic Site GenerationO HTML é gerado uma vez, na publicação
SSRServer-Side RenderingO HTML é montado no servidor a cada visita
UIUser InterfaceO desenho visual da interface
URLUniform Resource LocatorO endereço de um recurso na web
UXUser ExperienceA experiência de usar
WCAGWeb Content Accessibility GuidelinesAs diretrizes oficiais de acessibilidade