Desenvolvimento web e mobile
Fundamentos de HTML: estrutura semântica, formulários, mídia e acessibilidade
Iniciante10 min de leituraAtualizado
Esta página foi traduzida do inglês com a ajuda de IA e conferida com o original. Ler a versão em inglês
O HTML moderno é um padrão vivo, não uma versão numerada, e os elementos que você escolhe são uma interface para navegadores, tecnologias assistivas e mecanismos de busca ao mesmo tempo. Este guia percorre o esqueleto do documento, os elementos semânticos e o que cada um sinaliza, a validação nativa de formulários, imagens e mídia responsivas, tabelas acessíveis e os metadados do head que outras máquinas leem.
O que você vai aprender
- Montar um esqueleto de documento válido e explicar o que o doctype, o charset e o atributo lang realmente fazem
- Escolher elementos semânticos pelo significado e relacioná-los aos landmarks de acessibilidade que expõem
- Usar controles de formulário nativos, tipos de input e validação por restrições antes de recorrer ao JavaScript
- Entregar imagens responsivas e mídia acessível com srcset, picture, texto alternativo e legendas
- Escrever metadados no head que controlam título, URL canônica, prévias em redes sociais e layout mobile
O HTML tem fama de ser a parte fácil do desenvolvimento front-end e, em certo sentido, com razão: nada lança exceção, nada deixa de compilar e uma página com tags mal fechadas é renderizada mesmo assim. É justamente essa tolerância que facilita escrevê-lo mal. O parser conserta seus erros em silêncio, então um documento pode não ter estrutura nenhuma e parecer correto na sua tela, enquanto um leitor de tela o anuncia como um paredão de texto indiferenciado e um crawler tem dificuldade para entender do que a página trata.
A forma útil de pensar no markup é como uma interface com vários consumidores. O navegador o usa para construir o DOM e a árvore de acessibilidade. As tecnologias assistivas percorrem essa árvore por landmark, título e controle. Os mecanismos de busca extraem dele estrutura e metadados. O seu próprio JavaScript faz consultas nele. Escolher os elementos com cuidado atende aos quatro de uma vez. Este guia cobre as partes da linguagem em que esse cuidado compensa: o esqueleto do documento, o vocabulário semântico, o head, formulários, mídia e as regras de acessibilidade que o markup sozinho consegue cumprir.
A especificação de referência
Não vem aí nenhum HTML6. Desde 2019 existe uma única especificação, o HTML Living Standard do WHATWG, atualizada continuamente em vez de lançada em versões. Quando alguém diz "HTML5" hoje, está falando do conjunto moderno de elementos e das APIs do navegador que chegaram com ele, não de uma edição congelada. Para consulta no dia a dia, a documentação do MDN é mais legível do que a própria especificação e traz tabelas de compatibilidade entre navegadores que a especificação não tem.
Um documento mínimo e correto fica assim:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Order history — Northwind</title>
</head>
<body>
<!-- content -->
</body>
</html>
Cada linha ali tem sua razão de ser. <!DOCTYPE html> não é mais uma declaração de versão: é a chave que coloca o navegador em modo padrão em vez do modo quirks, mantido para documentos escritos nos anos 1990. O atributo lang diz aos leitores de tela quais regras de pronúncia usar e ajuda as ferramentas de tradução; sem ele, uma página em inglês é lida em voz alta com fonética francesa num sistema configurado em francês. A declaração de codificação precisa aparecer nos primeiros 1024 bytes do arquivo, por isso vem primeiro, e deve ser sempre UTF-8. A meta tag viewport impede que navegadores mobile renderizem a página numa largura de desktop simulada e depois diminuam o zoom.
Repare também no que falta: elementos vazios não exigem barra de fechamento, valores de atributo só precisam de aspas quando contêm espaços ou caracteres especiais, e as tags <html>, <head> e <body> são tecnicamente opcionais porque o parser as deduz. Escreva-as mesmo assim. As regras de inferência são sutis, e ser explícito custa três linhas.
Elementos que carregam significado
Os elementos de seccionamento e agrupamento existem para que a forma de um documento seja legível por máquinas. A maioria corresponde diretamente a um landmark de acessibilidade, e é isso que permite a quem usa leitor de tela pular direto para a navegação ou para o conteúdo principal em vez de ouvir tudo desde o início a cada vez.
| Elemento | Use para | Landmark exposto |
|---|---|---|
header | Conteúdo introdutório da página ou de uma seção | banner, quando filho direto de body |
nav | Um bloco de links de navegação principais | navigation |
main | O conteúdo principal, uma vez por página | main |
article | Um item autônomo que faria sentido distribuído por syndication | article |
section | Um agrupamento temático, normalmente com título | region, só quando tem nome acessível |
aside | Conteúdo tangencial, como uma barra lateral ou uma citação em destaque | complementary |
footer | Informações de encerramento da página ou de uma seção | contentinfo, quando filho direto de body |
figure / figcaption | Mídia com legenda associada | nenhum |
Dois deles costumam ser mal usados. section não é uma caixa genérica: só vira uma região navegável quando tem nome acessível, geralmente fornecido por um título referenciado com aria-labelledby. E article não quer dizer "post de blog": um comentário, um card de produto e uma resposta em fórum são todos articles, porque o teste é se o bloco continuaria fazendo sentido se fosse retirado e exibido sozinho.
Os títulos são a outra metade da estrutura. Usuários de leitores de tela navegam por títulos mais do que por qualquer outro mecanismo, então os níveis precisam descrever uma hierarquia real: um único h1 nomeando a página, h2 para as partes principais e h3 para as subdivisões delas. Nunca escolha um nível pelo tamanho de fonte padrão, que é trabalho do CSS, e nunca pule um nível ao descer. Uma div continua sendo a resposta certa quando um contêiner existe só por causa do layout e nenhum elemento descreve o que há dentro; o problema é apenas recorrer a ela primeiro.
O head e os metadados que outras máquinas leem
O head é onde você conversa com sistemas que nunca renderizam a sua página. Um documento em produção normalmente carrega bem mais do que o mínimo:
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Refund policy — Northwind Support</title>
<meta name="description" content="How refunds are calculated, how long they take and what to do if one is late." />
<link rel="canonical" href="https://example.com/support/refunds" />
<meta property="og:type" content="article" />
<meta property="og:title" content="Refund policy" />
<meta property="og:description" content="How refunds are calculated and how long they take." />
<meta property="og:image" content="https://example.com/img/refunds-card.png" />
<meta name="twitter:card" content="summary_large_image" />
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
<meta name="theme-color" content="#0f172a" />
<link rel="preconnect" href="https://fonts.example.net" crossorigin />
</head>
O title é a string mais valiosa da página: vira a aba do navegador, o favorito, a primeira coisa que um leitor de tela anuncia no carregamento e, geralmente, a manchete do resultado de busca. Escreva do específico para o geral, com o nome do site no fim. A description não é fator de ranqueamento, mas muitas vezes é usada literalmente como snippet do resultado, então deve ser lida como uma frase, não como uma lista de palavras-chave. O link canônico resolve problemas de URL duplicada (parâmetros de rastreamento, barras finais, versões para impressão) indicando o endereço que você quer indexado.
As tags Open Graph controlam como um link aparece quando alguém o cola num chat ou numa rede social. Adicioná-las custa pouco, e elas brilham pela ausência na maioria das ferramentas internas. Resource hints como preconnect e preload cortam latência em origens de terceiros; usados sem critério, fazem o contrário, e vale entender esses trade-offs junto com as técnicas mais amplas de performance de aplicações.
Formulários, inputs e a validação que vem de graça
Um formulário é a parte funcionalmente mais densa da maioria das páginas, e o lugar onde JavaScript escrito à mão mais substitui algo que o navegador já faz melhor.
<form action="/subscribe" method="post">
<fieldset>
<legend>Delivery details</legend>
<label for="email">Email address</label>
<input id="email" name="email" type="email" autocomplete="email" required />
<label for="postcode">Postcode</label>
<input id="postcode" name="postcode" type="text" inputmode="numeric"
pattern="[0-9]{5}" autocomplete="postal-code" required
aria-describedby="postcode-hint" />
<p id="postcode-hint">Five digits, no spaces.</p>
<label for="qty">Quantity</label>
<input id="qty" name="qty" type="number" min="1" max="20" step="1" value="1" />
</fieldset>
<button type="submit">Subscribe</button>
</form>
Todo controle precisa de um nome acessível, e a forma confiável de dar esse nome é um label cujo for corresponde ao id do input. Placeholder não é label: some quando a pessoa começa a digitar, não atende aos requisitos de contraste na maioria dos layouts e algumas tecnologias assistivas o ignoram. O atributo name é o que aparece no payload enviado, então um controle sem ele não envia nada.
Escolher o type certo traz comportamentos que você teria de escrever na mão: um teclado mobile adequado, um seletor de data, um seletor de cor, a checagem de formato. Combine isso com required, pattern, min, max, step e maxlength e você tem uma validação declarativa que funciona sem uma linha de script, expõe :invalid e :user-invalid ao CSS e é acessível por teclado por construção. Quando precisar de regras próprias, a Constraint Validation API permite manter o mesmo mecanismo: chame setCustomValidity() no elemento, ou adicione novalidate ao formulário e controle as mensagens você mesmo.
Acertar os tokens de autocomplete é uma mudança pequena com efeito desproporcional na taxa de conclusão, porque permite que o navegador e o gerenciador de senhas preencham valores conhecidos. E nada disso é uma barreira de segurança: atributos podem ser editados no devtools e requisições podem ser enviadas direto para o seu endpoint, então o servidor precisa revalidar tudo e responder com um código de status preciso. A diferença entre um corpo malformado e um semanticamente inválido está na referência de códigos de status HTTP.
Imagens, mídia e entrega responsiva
As imagens costumam ser a maior parte dos bytes de uma página, então aqui as escolhas de markup dominam o tempo de carregamento.
<figure>
<img src="/img/chart-800.avif"
srcset="/img/chart-400.avif 400w,
/img/chart-800.avif 800w,
/img/chart-1600.avif 1600w"
sizes="(min-width: 60rem) 40rem, 100vw"
width="1600" height="900"
loading="lazy" decoding="async"
alt="Monthly refund volume falling from 1,200 in January to 300 in June." />
<figcaption>Refund volume after the policy change.</figcaption>
</figure>
srcset oferece ao navegador várias resoluções da mesma imagem e sizes diz qual será de fato a largura dela no layout, para que ele possa escolher antes de o CSS ser processado. Defina sempre width e height: eles deixam o navegador reservar o espaço certo antes de o arquivo chegar, o que elimina o layout shift que empurra o texto enquanto a página carrega. loading="lazy" adia imagens fora da tela, mas nunca aplique isso à imagem do topo da página: é justamente a que você quer baixar primeiro.
Quando precisar de fontes realmente diferentes, e não só de tamanhos diferentes (um recorte quadrado no mobile, ou um formato moderno com fallback), use picture com vários elementos source e um img no final. Seja qual for o contêiner, é o img que carrega o texto alt, e escrevê-lo bem é o hábito de acessibilidade de maior retorno que existe: descreva a informação que a imagem transmite no seu contexto, não o arquivo. Um enfeite decorativo recebe alt="" para ser ignorado por completo; omitir o atributo, ao contrário, faz os leitores de tela lerem o nome do arquivo em voz alta. Antes de publicar os arquivos, vale passá-los por um otimizador como o compressor de imagens ou reduzir originais grandes demais com o redimensionador de imagens.
Vídeo e áudio seguem a mesma lógica. Adicione controls, a menos que você os substitua por controles acessíveis, forneça legendas com um elemento track de kind="captions", defina um quadro poster e evite autoplay com som. Use preload="metadata" em vez do padrão, para que uma página com vários clipes não comece a baixar todos eles.
Tabelas que sobrevivem a um leitor de tela
Tabelas são para dados, nunca para layout. O markup que torna uma tabela navegável é pequeno:
<table>
<caption>Refunds by region, first half of 2026</caption>
<thead>
<tr><th scope="col">Region</th><th scope="col">Requests</th><th scope="col">Median days</th></tr>
</thead>
<tbody>
<tr><th scope="row">North</th><td>1,204</td><td>3.1</td></tr>
<tr><th scope="row">South</th><td>884</td><td>4.6</td></tr>
</tbody>
</table>
O caption dá nome à tabela, th com scope informa às tecnologias assistivas quais células são cabeçalhos e em que direção, e marcar a primeira célula de cada linha como cabeçalho de linha permite que um leitor de tela anuncie "North, median days, 3.1" em vez de um número solto. Em tabelas irregulares com células mescladas, os atributos headers e id permitem associar as células explicitamente. Se a tabela for larga, coloque-a num contêiner com rolagem que tenha tabindex="0" e um papel, para que quem usa teclado consiga alcançar a parte que transborda.
As APIs que a plataforma oferece
Além dos elementos, os navegadores expõem uma grande biblioteca padrão que vale conhecer antes de adicionar uma dependência para a mesma coisa. localStorage e sessionStorage guardam pequenos valores de texto de forma síncrona e são perfeitos para uma preferência de tema, e errados para qualquer coisa sensível, já que qualquer script da origem pode lê-los. IndexedDB cuida de armazenamento estruturado, maior e assíncrono. canvas oferece uma superfície de pixels para gráficos e processamento de imagens, enquanto o SVG inline continua nítido e permanece no DOM, onde pode receber estilos e ser tornado acessível. A History API reescreve a barra de endereço sem recarregar a página, e é assim que funcionam os routers do lado do cliente. Os Web Workers tiram cálculos pesados da thread principal. Os custom elements permitem empacotar um widget como uma tag de verdade, com estilos próprios encapsulados. A maioria é acessada via JavaScript, e os recursos da linguagem de que você vai precisar estão em fundamentos de JavaScript.
Validação, e os erros que sobrevivem ao code review
Rode o validador de markup do W3C, ou um linter equivalente no seu build, e trate a saída dele como erro de build. Como o parser conserta erros em silêncio, validar é a única forma barata de pegar problemas estruturais. Os recorrentes:
- Valores de
idduplicados, geralmente vindos de um template repetido. Associações de label e links de fragmento passam a apontar para o primeiro elemento que aparecer. - Inputs sem label, escondidos atrás de um placeholder ou de um ícone sem nome acessível.
- Níveis de título escolhidos pelo tamanho, gerando uma estrutura que salta de
h1parah4. - Elementos
divinterativos com handler de clique e sem suporte a teclado. Se o elemento se comporta como um botão, faça dele umbutton. - Texto
altque repete a legenda ou começa com "imagem de", o que os leitores de tela já anunciam. - Ausência de
widtheheightnas imagens, a causa mais comum de layout shift. - Controles interativos aninhados, como um link dentro de um botão, que produzem comportamento de teclado indefinido.
- ARIA usado para remendar markup ruim. Um papel errado é pior do que nenhum; a primeira regra do ARIA é usar o elemento nativo.
Principais pontos
- HTML é um padrão vivo sem números de versão: escreva com base na especificação do WHATWG e confira a compatibilidade no MDN em vez de esperar o próximo lançamento.
- Os elementos são uma interface para navegadores, tecnologias assistivas e crawlers ao mesmo tempo: escolha-os pelo significado, e os landmarks de acessibilidade e a estrutura do documento vêm de graça.
- O head controla como as máquinas veem a página: um título preciso, uma description real, uma URL canônica e tags Open Graph custam minutos e rendem para sempre.
- Controles de formulário nativos, tipos de input e validação por restrições entregam validação acessível e amigável ao teclado sem script, mas nunca substituem a validação no servidor.
- Imagens responsivas com
srcset, dimensões explícitas, textoalthonesto e mídia legendada são os ganhos de performance e acessibilidade mais baratos que existem. - Valide o seu markup e trate as falhas recorrentes (ids duplicados, inputs sem label, divs clicáveis, ARIA encobrindo o elemento errado) como defeitos, não como questão de estilo.
Fontes e leituras complementares
Perguntas frequentes
- O HTML5 ainda é a versão atual do HTML?
- Não. O HTML deixou de ser lançado em versões numeradas em 2019, quando o W3C e o WHATWG concordaram que o HTML Living Standard do WHATWG seria a especificação única. Ele é atualizado continuamente, então os recursos chegam sem mudança de versão. O termo HTML5 sobrevive como atalho para o conjunto moderno de elementos e as APIs do navegador que vieram junto, mas não há nenhum HTML6 a caminho.
- Quando devo usar uma div em vez de um elemento semântico?
- Use uma div quando o contêiner existir apenas por causa de estilo ou layout e nenhum elemento existente descrever o conteúdo. Se o bloco for a navegação do site, um conteúdo autônomo, o rodapé da página ou o conteúdo principal do documento, o elemento semântico correspondente dá às tecnologias assistivas um landmark de graça. Começar com uma div e acrescentar um papel ARIA depois dá mais trabalho e é mais fácil de errar.
- Ainda preciso de validação no cliente se o servidor já valida?
- Sim, mas por outro motivo. A validação por restrições do navegador dá feedback imediato e acessível pelo teclado e barra envios obviamente malformados, o que é um ganho de usabilidade. Não é um controle de segurança, porque qualquer pessoa pode desativar o JavaScript, editar os atributos ou enviar a requisição direto para o seu endpoint. Trate todo campo como não confiável no servidor e retorne um código de status preciso quando a validação falhar.
- Qual é a diferença entre srcset e o elemento picture?
- srcset com sizes deixa o navegador escolher entre versões da mesma imagem que diferem apenas na resolução; ele decide com base na largura do viewport, na densidade de pixels do dispositivo e nas próprias heurísticas. O elemento picture serve para os casos em que você precisa controlar a escolha: um recorte diferente em telas pequenas, ou um arquivo AVIF com fallback em JPEG para navegadores que não conseguem decodificá-lo.