12k
All articles

O que vai em um boilerplate HTML moderno

Crie um boilerplate HTML moderno com as cinco linhas obrigatórias, metadados úteis, favicon e tags sociais. Veja o que manter, adicionar ou remover.

OpenReplay Team
OpenReplay Team
O que vai em um boilerplate HTML moderno

Um boilerplate HTML moderno precisa de cinco coisas: o doctype do HTML5, um atributo lang em <html>, uma declaração de charset UTF-8, uma meta tag de viewport e um <title> exclusivo. Todo o resto depende de a página ser compartilhada, instalada ou adicionada aos favoritos.

A maioria das páginas começa a partir do snippet ! do Emmet, do index.html de algum framework ou de uma cópia do HTML5 Boilerplate com dez anos de idade. Ninguém volta para revisar essas linhas, então as desatualizadas acabam sendo copiadas de projeto em projeto.

Este artigo percorre o documento linha por linha. Para cada linha, ele explica o que ela faz, o que quebra sem ela e se ela é obrigatória, opcional ou pode ser removida com segurança. Para a ordem dos elementos no head e o panorama mais amplo de metadados, leia o artigo complementar sobre o que realmente pertence ao head do seu documento.

Este é o resultado do ! do Emmet:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

</body>
</html>

É um ponto de partida válido. No entanto, ele não tem description, canonical, tags sociais, ícones nem color-scheme, e sua viewport inclui um valor que você não precisa mais.

Principais conclusões

  • As linhas obrigatórias em um boilerplate HTML moderno são o doctype, lang, <meta charset="UTF-8">, a tag de viewport e <title>. Sem qualquer uma delas, a página é renderizada incorretamente, fica menos acessível ou se torna inválida.
  • O atributo lang controla a pronúncia dos leitores de tela, a hifenização via CSS e a tradução no navegador. O Google não o utiliza para detectar o idioma de uma página.
  • maximum-scale, minimum-scale e user-scalable=no impedem que usuários com baixa visão deem zoom. Remova-os onde quer que apareçam.
  • Um conjunto completo de favicons é composto por favicon.ico, favicon.svg, um apple-touch-icon.png de 180×180 e um web manifest com ícones de 192 e 512 pixels, além de um ícone maskable.
  • X-UA-Compatible, shrink-to-fit=no e type="text/javascript" não fazem nada de útil em um projeto novo e podem ser removidos.

Doctype e o atributo lang

Obrigatório. <!DOCTYPE html> coloca o navegador no modo padrão (standards mode). Sem ele, o parser HTML recorre ao quirks mode, que reproduz bugs de layout de navegadores antigos. Você passa a ter diferenças no box model e no dimensionamento de tabelas que nenhuma quantidade de depuração de CSS consegue explicar.

Obrigatório. O atributo lang em <html> cumpre três funções:

  • Informa aos leitores de tela quais regras de pronúncia aplicar.
  • Permite que o hyphens: auto do CSS separe as palavras usando o dicionário correto.
  • Ajuda os recursos de tradução do navegador a identificar o idioma da página.

Sem ele, um leitor de tela pode ler um texto em francês com fonética do inglês. O valor é uma tag de idioma BCP 47, como en, de ou pt-BR, e elementos filhos podem sobrescrevê-lo, conforme descrito na referência do lang no MDN.

O lang trata de acessibilidade e idioma, não de ranqueamento em buscas. A Pesquisa Google afirma que ignora o lang e detecta o idioma a partir do conteúdo da página.

<!DOCTYPE html>
<html lang="en">

Primeiro o charset, depois a viewport

Obrigatório. <meta charset="UTF-8"> precisa estar dentro dos primeiros 1024 bytes do documento. É por isso que ele vem primeiro no <head>, antes do <title> e de qualquer outro texto que o navegador poderia decodificar com a codificação errada. Se estiver ausente ou vier tarde demais, caracteres acentuados e emojis no seu título viram mojibake.

Obrigatório. A tag de viewport instrui os navegadores móveis a montar o layout da página na largura do dispositivo. Sem ela, os celulares renderizam um layout com largura de desktop (geralmente cerca de 980px) e o reduzem, de modo que suas media queries nunca são acionadas. width=device-width é suficiente por si só, e initial-scale=1 geralmente é desnecessário. Consulte a referência de viewport no MDN para a lista completa de valores.

<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">

Opcional, teste antes. <meta name="text-scale" content="scale"> é definida no rascunho do CSS Fonts Level 5, e apenas navegadores baseados em Chromium a implementam. Ela faz o tamanho de fonte raiz seguir o tamanho de texto que o usuário escolhe nas configurações do sistema operacional ou do navegador. Como resultado, apenas textos em rem e em aumentam, e componentes com altura fixa podem transbordar. Aumente o tamanho do texto do sistema e verifique seus layouts antes de colocá-la em produção.

Title, description e canonical

Obrigatório. O <title> dá nome à página nas abas do navegador, nos favoritos, no histórico e nos resultados de busca. Se você deixar o padrão Document, todas as abas abertas parecerão idênticas.

Opcional, mas recomendado. <meta name="description"> oferece um resumo para os snippets de busca. O Google pode reescrevê-la caso encontre conteúdo na página que corresponda melhor à consulta, e ele trunca os snippets com base na largura de exibição. Isso torna pouco confiáveis as regras baseadas em contagem de caracteres.

Opcional, mas recomendado. <link rel="canonical"> indica a URL preferencial quando o mesmo conteúdo pode ser acessado por mais de um endereço, por exemplo, com query strings ou variações com e sem barra final. Para mais detalhes, veja tags HTML relacionadas a SEO e como otimizá-las.

<title>Page title – Site name</title>
<meta name="description" content="One or two sentences describing this page.">
<link rel="canonical" href="https://example.com/page">

Tags Open Graph e Twitter Card

Opcional, obrigatório para tudo o que for compartilhado. As tags Open Graph controlam a pré-visualização do link em redes sociais e aplicativos de mensagens. Sem elas, um link compartilhado aparece como uma URL crua ou como um card montado a partir do que o scraper encontrar.

O protocolo Open Graph lista og:title, og:type, og:image e og:url como propriedades obrigatórias e determina que uma página sem tipo deve ser tratada como website. Defina og:url com o mesmo valor da sua URL canônica, para que links com parâmetros de rastreamento não sejam armazenados em cache como páginas separadas. Para og:image, a Meta recomenda uma imagem de pelo menos 1200×630 pixels, mantendo uma proporção próxima de 1,91:1.

O X lê suas próprias tags twitter:*, escritas com name em vez de property, e recorre aos valores og:* correspondentes quando elas estão ausentes. Na prática, a única tag específica do X de que você precisa é twitter:card, e apenas se quiser o card com imagem grande.

<meta property="og:title" content="Page title">
<meta property="og:description" content="Description for link previews.">
<meta property="og:image" content="https://example.com/og-image.jpg">
<meta property="og:url" content="https://example.com/page">
<meta name="twitter:card" content="summary_large_image">

Para gerar essas tags para cada página, use o gerador de meta tags.

O conjunto de favicons

Opcional, mas esperado. Uma configuração completa de favicons tem quatro partes. Sem elas, você fica com um ícone de aba em branco, um bloco em estilo de captura de tela nas telas iniciais do iOS e ícones genéricos de instalação no Android.

Mantenha os dois elementos <link rel="icon">, o do favicon.ico e o do favicon.svg. Assim, navegadores que não conseguem usar favicons SVG, incluindo o Safari anterior à versão 26, ainda recebem o arquivo ICO.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
  <style>
    circle { fill: #1a1a2e; }
    @media (prefers-color-scheme: dark) {
      circle { fill: #f5f5f5; }
    }
  </style>
  <circle cx="16" cy="16" r="14"/>
</svg>
{
  "name": "Site name",
  "short_name": "Site",
  "icons": [
    { "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" },
    { "src": "/icon-maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
  ]
}

Para saber mais sobre como gerar cada arquivo, veja como adicionar um favicon ao seu site.

color-scheme e theme-color

Opcional, recomendado se você oferece modo escuro. <meta name="color-scheme" content="light dark"> informa ao navegador que a página suporta ambos os modos antes mesmo de qualquer CSS chegar. Com isso, fundos padrão, controles de formulário e barras de rolagem passam a seguir a preferência do usuário. Sem ela, um site com tema escuro pode exibir um flash de tela branca durante o carregamento. Veja o MDN sobre color-scheme.

Opcional. theme-color colore a interface do navegador e aceita um atributo media, permitindo definir valores separados para os modos claro e escuro. Funciona no Chromium para Android, mas o MDN classifica o suporte dos navegadores como limitado, então trate-a como um extra interessante, e não como algo que todo navegador vai exibir.

<meta name="color-scheme" content="light dark">
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#111111" media="(prefers-color-scheme: dark)">

Um padrão de movimento reduzido

Opcional, fortemente recomendado. Um padrão de movimento reduzido é uma regra para a sua folha de estilos base, não uma tag do head. Ele pertence ao boilerplate porque ninguém volta para adicioná-lo depois. Um bloco prefers-reduced-motion: reduce encurta animações e transições e desativa a rolagem suave para usuários que pediram ao sistema operacional menos movimento.

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

As durações são definidas como 0.01ms em vez de 0 para que animationend e transitionend continuem sendo disparados. Dessa forma, o JavaScript que aguarda esses eventos não fica travado. Esse é um padrão genérico. Componentes individuais podem voltar a usar movimentos suaves, como transições de opacidade, que não causam problemas vestibulares.

Quais linhas você deve remover de boilerplates antigos?

Remover. Quatro linhas presentes em boilerplates HTML antigos vêm de templates da era do IE e dos primeiros starters responsivos, e nenhuma delas faz nada de útil hoje:

  • X-UA-Compatible selecionava os modos de documento do Internet Explorer. Nenhum navegador que você suporta a lê.
  • maximum-scale, minimum-scale e user-scalable=no impedem que pessoas com baixa visão usem o gesto de pinça para dar zoom, o que vai contra o WCAG 1.4.4 Redimensionar texto. Remova-os de todas as tags de viewport.
  • shrink-to-fit=no contornava um comportamento antigo de escala do Safari no iOS e não faz mais nada.
  • type="text/javascript" repete o padrão. A especificação HTML orienta os autores a omitir o type em scripts clássicos.
<!-- Before -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, shrink-to-fit=no">
<script type="text/javascript" src="/app.js"></script>

<!-- After -->
<meta name="viewport" content="width=device-width">
<script type="module" src="/app.js"></script>

Um script com type="module" é adiado (deferred) por padrão, portanto é executado após o parsing sem bloquear a renderização. Se um script não puder ser um módulo, adicione defer a ele.

O boilerplate HTML completo

O boilerplate HTML moderno completo abaixo reúne todas as linhas obrigatórias e recomendadas deste artigo. Acrescente extras específicos do seu site, como feeds ou preloads, conforme necessário. O skip link e o landmark <main> ficam no body, e o artigo 10 elementos HTML essenciais aborda os elementos semânticos que vêm em seguida.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width">
  <!-- Optional, test with large OS text first: <meta name="text-scale" content="scale"> -->
  <title>Page title – Site name</title>
  <meta name="description" content="One or two sentences describing this page.">
  <link rel="canonical" href="https://example.com/page">

  <meta name="color-scheme" content="light dark">
  <meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
  <meta name="theme-color" content="#111111" media="(prefers-color-scheme: dark)">

  <link rel="stylesheet" href="/styles.css">
  <script type="module" src="/app.js"></script>

  <link rel="icon" href="/favicon.ico">
  <link rel="icon" href="/favicon.svg" type="image/svg+xml">
  <link rel="apple-touch-icon" href="/apple-touch-icon.png">
  <link rel="manifest" href="/site.webmanifest">

  <meta property="og:title" content="Page title">
  <meta property="og:description" content="Description for link previews.">
  <meta property="og:image" content="https://example.com/og-image.jpg">
  <meta property="og:url" content="https://example.com/page">
  <meta name="twitter:card" content="summary_large_image">
</head>
<body>
  <a href="#main">Skip to content</a>
  <main id="main"></main>
</body>
</html>
LinhaO que fazO que quebra sem elaVeredito
DoctypeModo padrãoLayout em quirks modeObrigatória
langPronúncia, hifenização, traduçãoVoz errada no leitor de telaObrigatória
charsetDeclara UTF-8Caracteres corrompidosObrigatória
viewportLayout na largura do dispositivoLayout de desktop reduzido nos celularesObrigatória
<title>Dá nome à páginaAbas e favoritos idênticosObrigatória
Description, canonicalSnippet e URL preferencialSnippets mais fracos, URLs duplicadasOpcional
Open Graph, twitter:cardPré-visualizações de linksCards crus ou genéricosOpcional
Conjunto de faviconsÍcones de aba e da tela inicialÍcones em branco ou em estilo de captura de telaOpcional
color-schemeModo escuro nativoFlash branco, controles clarosOpcional
X-UA-Compatible, limites de zoom, shrink-to-fit, type="text/javascript"Soluções legadasNadaRemover

Apenas cinco linhas de um boilerplate HTML são obrigatórias. Todo o resto é opcional, dependendo de a página ser compartilhada, instalada ou tematizada, ou é uma linha legada a ser removida. Abra o index.html do seu projeto atual, confira cada linha com a tabela acima e remova tudo o que estiver na linha “Remover” antes de copiar o arquivo para o próximo projeto.

Perguntas frequentes

A meta charset é a mesma coisa que a meta http-equiv Content-Type?

Sim. Segundo a especificação HTML do WHATWG, a versão http-equiv Content-Type cumpre a mesma função que o atributo charset. Ambas contam como uma declaração de codificação de caracteres e ambas precisam estar dentro dos primeiros 1024 bytes. Um documento não deve conter as duas, e os validadores sinalizam isso como erro. Use a forma mais curta, meta charset='UTF-8'. Se o servidor enviar um charset no cabeçalho HTTP Content-Type, certifique-se de que ele também indique UTF-8.

As tags meta e link precisam de barra no final em HTML?

Não. Meta, link, img e os demais elementos vazios (void elements) não têm tag de fechamento, e o parser HTML ignora a barra final neles. Uma tag escrita com a barra e a mesma tag escrita sem ela produzem o mesmo elemento. A barra é válida, mas não tem efeito, então é uma questão de estilo. O único caso em que ela importa é em uma página servida como XHTML com um MIME type XML.

Posso omitir as tags html, head e body?

Sim. A especificação HTML marca as tags html, head e body como opcionais em muitos casos, e o parser cria esses elementos mesmo quando as tags estão ausentes. Ainda assim, um boilerplate deve escrevê-las explicitamente. O atributo lang precisa de uma tag de abertura html explícita para ser aplicado, e tags explícitas mantêm a separação entre head e body fácil de ler. Se você omitir a tag html, também perde o lang.

Como altero o valor padrão de lang no snippet ! do Emmet?

Defina a variável lang na configuração emmet.variables do VS Code. No settings.json, adicione uma chave lang com um valor como pt-BR ao objeto emmet.variables, e o Emmet passará a escrever esse código de idioma sempre que expandir o snippet !. A mesma configuração tem uma variável charset, que você deve manter como UTF-8. A linha de viewport ainda contém initial-scale=1.0, então remova-a manualmente ou defina um snippet personalizado.

Open-source session replay

Gain control over your UX

See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.