Capítulo 40
Melhoria progressiva
7 min de leitura 1.310 palavras 2 gravuras
Este capítulo ensina a construir a Oficina Costeira de dentro para fora: primeiro o HTML, depois o CSS, depois o JavaScript. A Oficina Costeira é um coletivo fictício de design em João Pessoa. Este volume tem 52 capítulos. Esta é a 40.
O exemplo está em exemplos/40-melhoria-progressiva/. Abra index.html no navegador. O ciclo é o de sempre: ler, copiar, mudar uma coisa, prever, recarregar, conferir.
O que você vai aprender
- Tratar o HTML como a página inteira, não como um rascunho.
- Usar CSS como melhoria, não como condição para existir.
- Ligar JavaScript só quando ele acrescenta, nunca quando ele substitui o conteúdo.
- Detectar recursos com
@supportsantes de usar CSS novo. - Testar a página sem script, com fonte maior e na impressão.
- Distinguir arquivo no disco, teste em
127.0.0.1e publicação.
A página precisa funcionar sem enfeite
Melhoria progressiva é uma ordem de trabalho. Você entrega o conteúdo primeiro. Depois acrescenta aparência. Por último, comportamento.
A visitante não é você. Ela pode estar no ônibus, com a fonte enorme, só com teclado, ou com o JavaScript bloqueado. O que você controla é o arquivo. O que você não controla é o aparelho.
Três camadas, na ordem:
- HTML — títulos, parágrafos, listas, links, formulário com rótulo.
- CSS — cor, tipo, grade, espaço.
- JavaScript — gráfico extra, menu extra, validação extra.
Se a camada de cima falhar, a de baixo continua. A home da Oficina Costeira já conta quem é o coletivo só com tags. O infográfico, quando existir, vai repetir números que a lista HTML já mostra.
Copie e salve como pe.html. Abra no navegador.
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Início — Oficina Costeira</title>
</head>
<body>
<a href="#conteudo">Pular para o conteúdo</a>
<header>
<p><a href="index.html">Oficina Costeira</a></p>
<nav aria-label="Principal">
<ul>
<li><a href="index.html" aria-current="page">Início</a></li>
<li><a href="projetos.html">Projetos</a></li>
</ul>
</nav>
</header>
<main id="conteudo">
<h1>Oficina Costeira</h1>
<p>Coletivo fictício de design em João Pessoa.</p>
<ul>
<li>Identidade: 8 trabalhos fictícios</li>
<li>Publicação: 12 trabalhos fictícios</li>
</ul>
</main>
</body>
</html>
Mude só o segundo item da lista para Oficina: 5. Preveja o texto na janela. Recarregue. Confira. Ainda não há CSS nem script. A página já é um documento.
Erro comum: começar pelo efeito visual e só depois “preencher” o HTML. Sem conteúdo, não há o que melhorar.
CSS chega depois, e pode faltar
Ligue uma folha no head. Se o arquivo CSS não baixar, o HTML continua. O navegador usa o visual padrão. Isso não é falha grave. É o combinado da Web.
<link rel="stylesheet" href="assets/stylesheets/main.css">
No CSS, escreva o básico primeiro: cor do texto, tamanho do tipo, foco visível. Recursos novos entram atrás de uma pergunta: o navegador entende isto?
body {
font-family: "Segoe UI", Tahoma, sans-serif;
line-height: 1.5;
color: #14222b;
background: #f7f4ee;
}
a:focus-visible {
outline: 3px solid #c45c26;
outline-offset: 3px;
}
@supports (display: grid) {
.cards {
display: grid;
gap: 1rem;
grid-template-columns: 1fr;
}
}
@supports pergunta ao navegador. Se display: grid existir, as regras de dentro valem. Se não existir, os cartões empilham como blocos. A leitura segue.
Mude só o valor de color no body. Preveja o tom do texto. Recarregue. Confira. Depois desligue a folha no HTML (comente o link) e recarregue de novo. O texto ainda precisa estar lá.
Erro comum: colocar o conteúdo só como fundo de uma imagem CSS. Se a imagem não carregar, a página fica muda.

JavaScript é extra, não o miolo
O script só entra quando a página já conta a história. Na Oficina Costeira o arquivo assets/javascript/infografico.js procura um elemento. Se o elemento não existir, o script não quebra a página.
<script src="assets/javascript/infografico.js" defer></script>
defer espera o HTML terminar de ser lido. Assim o script encontra as tags. Mesmo assim, o conteúdo visível não pode nascer só no JavaScript.
const raiz = document.querySelector("[data-grafico]");
if (!raiz) {
// A lista HTML abaixo já conta a história.
} else {
const barras = [
{ nome: "Identidade", valor: 8 },
{ nome: "Publicação", valor: 12 },
{ nome: "Oficina", valor: 5 }
];
raiz.innerHTML = "";
for (const item of barras) {
const p = document.createElement("p");
p.textContent = item.nome + ": " + item.valor + " trabalhos fictícios";
raiz.append(p);
}
}
A lista <ul> fica no HTML. O div data-grafico é o enfeite. Sem script, a visitante lê a lista. Com script, ela lê os mesmos números de outro jeito.
Mude só o valor 8 para 9 no HTML da lista, não no script. Preveja: a lista muda, o gráfico extra pode ficar desatualizado. Recarregue. Confira. Depois alinhe o script. Os dois precisam dizer a mesma coisa.
Erro comum: esconder o conteúdo no HTML e só revelar com JavaScript. Se o script falhar, a página parece vazia.
Checar o que o navegador sabe fazer
Nem todo navegador entende o mesmo CSS. Antes de um recurso frágil, pergunte.
@supports (display: flex) and (not (display: grid)) {
.cards {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
}
A regra acima só vale se houver flex e não houver grid. É um plano B. Não copie dez planos B “por garantia”. Um caminho sólido e um reforço bastam.
No JavaScript, a mesma ideia: só use o que existir.
const aguenta = "querySelector" in document && "addEventListener" in window;
if (aguenta) {
const script = document.createElement("script");
script.src = "assets/javascript/infografico.js";
script.defer = true;
document.head.append(script);
}
Se o navegador antigo não tiver querySelector, o HTML continua. Este volume não empacota bibliotecas de detecção. @supports e um if curto resolvem o estudo.
Mude a condição para false de propósito (if (false)). Preveja: o gráfico extra some, a lista fica. Recarregue. Confira. Depois devolva a condição verdadeira.
Erro comum: testar só o navegador da sua máquina com todas as extensões desligadas. Bloqueie o JavaScript uma vez nas ferramentas do navegador e recarregue a home.
Falha, fonte grande, teclado e impressão
Faça uma lista mental de “e se”. Não precisa marcar tudo de uma vez. Precisa provar os itens que o seu site usa.
- E se as imagens não baixarem? O
altainda descreve. - E se o CSS não baixar? Os títulos e os links ainda existem.
- E se o JavaScript não rodar? A lista e o formulário ainda funcionam.
- E se a fonte for bem maior? O texto não deve sair da tela nem se sobrepor.
- E se a pessoa só usa teclado? Tab chega no skip-link, no menu, no conteúdo.
- E se ela imprimir? O menu enorme não precisa ocupar duas folhas.
O formulário de contato.html usa action="#" e method="get". O apoio pronto cancela o envio depois da validação. O botão permanece desativado quando esse apoio não carrega; action="#" sozinho não impediria um pedido GET. Validação do navegador (required, type="email") já é uma camada. Script de “sucesso” seria outra, opcional.
Impressão é CSS também:
@media print {
.menu,
.skip,
video {
display: none;
}
a[href]::after {
content: none;
}
}
Mude só o h1 da home para um título longo. Dê zoom até 200%. Preveja se o menu ainda quebra linha. Recarregue. Confira. Estreite a janela. O conteúdo não pode sumir atrás do cabeçalho.
Erro comum: achar que “todo mundo tem JavaScript”. Extensão, falha de rede e erro no seu próprio arquivo desligam o script.

Disco, 127.0.0.1 e publicação
Abrir o arquivo pelo Explorador usa file://. Serve para ler. Alguns testes (fonte, teclado, CSS desligado) já funcionam assim.
Servir a pasta curso só nesta máquina:
python -m http.server 8769 --bind 127.0.0.1 --directory curso
Visite http://127.0.0.1:8769/exemplos/40-melhoria-progressiva/. Encerre com Ctrl+C. Isso não é publicação. Não use 0.0.0.0.
Publicação pública é colocar os arquivos num servidor que qualquer pessoa na Internet alcança. Não publique este exercício. Os nomes da equipe são personagens. O formulário não deve pedir dado real.
A melhoria progressiva não termina neste capítulo. Ela vira hábito: HTML primeiro, CSS depois, JavaScript por último, e sempre um jeito de conferir com a camada de cima desligada.
Atividades
- Se o arquivo
main.cssnão carregar, a home da Oficina Costeira ainda informa o que o coletivo faz? Onde? - Para que serve
@supports (display: grid)neste volume? - O script do infográfico pode ser a única fonte dos números 8, 12 e 5? Por quê?
- Qual a diferença entre abrir
index.htmlpelo disco e visitá-lo em127.0.0.1? - O formulário de contato deve enviar a mensagem para um servidor do capítulo? Por quê?
Respostas comentadas
- Sim. O
h1, o parágrafo e os cartões estão no HTML. Sem CSS eles aparecem com o visual padrão do navegador. - Só aplica a grade se o navegador entender grid. Caso contrário os cartões empilham e o conteúdo continua.
- Não. A lista HTML já mostra os números. O script só repete. Se ele for a única fonte, a página some quando o JS falha.
- Disco é
file://, arquivo local.127.0.0.1é um servidor de teste nesta máquina. Nenhum dos dois é site público. - Não. O formulário só demonstra marcação e validação. Nada é enviado. Não use dado pessoal.
Referências
O arquivo completo
Referência do exemplo 40-melhoria-progressiva, reproduzida aqui para que você possa conferir sem sair da página.
assets/javascript/simulacao.js
const form=document.querySelector("#simulacao");if(form){form.querySelector("button[type=submit]").disabled=false;form.addEventListener("submit",event=>{event.preventDefault();document.querySelector("#retorno").textContent="Simulação concluída. Nada foi enviado ou guardado.";});}