12k
All articles

Qué incluir en un boilerplate HTML moderno

Crea una plantilla HTML moderna con las cinco líneas obligatorias, metadatos útiles, favicon y etiquetas sociales. Descubre qué conservar o eliminar.

OpenReplay Team
OpenReplay Team
Qué incluir en un boilerplate HTML moderno

Un boilerplate HTML moderno necesita cinco elementos: el doctype de HTML5, un atributo lang en <html>, una declaración de charset UTF-8, una etiqueta meta viewport y un <title> único. Todo lo demás depende de si la página se va a compartir, instalar o guardar en marcadores.

La mayoría de las páginas parten del snippet ! de Emmet, del index.html de algún framework o de una copia de HTML5 Boilerplate con diez años de antigüedad. Nadie vuelve a revisar esas líneas, así que las obsoletas se copian de un proyecto a otro.

Este artículo recorre el documento línea por línea. Para cada línea explica qué hace, qué se rompe si falta y si es obligatoria, opcional o se puede eliminar sin problema. Para el orden de los elementos del head y una visión más amplia de los metadatos, consulta el artículo complementario sobre qué debe incluirse realmente en el head de tu documento.

Esto es lo que genera el snippet ! de 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>

Es un punto de partida válido. Sin embargo, no incluye description, canonical, etiquetas para redes sociales, iconos ni color-scheme, y su viewport contiene un valor que ya no necesitas.

Conclusiones clave

  • Las líneas obligatorias en un boilerplate HTML moderno son el doctype, lang, <meta charset="UTF-8">, la etiqueta viewport y <title>. Si falta cualquiera de ellas, la página se renderiza de forma incorrecta, pierde accesibilidad o deja de ser válida.
  • El atributo lang controla la pronunciación de los lectores de pantalla, la división silábica de CSS y la traducción del navegador. Google no lo utiliza para detectar el idioma de una página.
  • maximum-scale, minimum-scale y user-scalable=no impiden hacer zoom a los usuarios con baja visión. Elimínalos dondequiera que aparezcan.
  • Un conjunto completo de favicons consta de favicon.ico, favicon.svg, un apple-touch-icon.png de 180×180 y un web manifest con iconos de 192 y 512 píxeles, además de uno maskable.
  • X-UA-Compatible, shrink-to-fit=no y type="text/javascript" no aportan nada útil en un proyecto nuevo y se pueden eliminar.

El doctype y el atributo lang

Obligatorio. <!DOCTYPE html> pone el navegador en modo estándar. Sin él, el parser de HTML recurre al modo quirks, que reproduce los errores de maquetación de los navegadores antiguos. El resultado son diferencias en el modelo de caja y en el dimensionado de tablas que ninguna depuración de CSS logra explicar.

Obligatorio. El atributo lang en <html> cumple tres funciones:

  • Indica a los lectores de pantalla qué reglas de pronunciación aplicar.
  • Permite que hyphens: auto de CSS divida las palabras usando el diccionario correcto.
  • Ayuda a las funciones de traducción del navegador a determinar el idioma de la página.

Sin él, un lector de pantalla podría leer un texto en francés con fonética inglesa. El valor es una etiqueta de idioma BCP 47, como en, de o pt-BR, y los elementos hijos pueden sobrescribirlo, tal como describe la referencia de lang en MDN.

lang se ocupa de la accesibilidad y del idioma, no del posicionamiento en buscadores. Google Search indica que ignora lang y detecta el idioma a partir del contenido de la página.

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

Primero el charset, luego el viewport

Obligatorio. <meta charset="UTF-8"> debe aparecer dentro de los primeros 1024 bytes del documento. Por eso va en primer lugar dentro de <head>, antes de <title> y de cualquier otro texto que el navegador pudiera decodificar con la codificación equivocada. Si falta o aparece demasiado tarde, los caracteres acentuados y los emojis del título se convierten en mojibake.

Obligatorio. La etiqueta viewport indica a los navegadores móviles que maqueten la página al ancho del dispositivo. Sin ella, los teléfonos renderizan una maquetación de ancho de escritorio (normalmente en torno a 980px) y la reducen, de modo que tus media queries nunca se activan. width=device-width es suficiente por sí solo, y initial-scale=1 suele ser innecesario. Consulta la referencia de viewport en MDN para ver la lista completa de valores.

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

Opcional, pruébalo antes. <meta name="text-scale" content="scale"> está definido en el borrador de CSS Fonts Level 5, y solo lo implementan los navegadores basados en Chromium. Hace que el tamaño de fuente raíz siga el tamaño de texto que el usuario elige en la configuración del sistema operativo o del navegador. Como consecuencia, solo crece el texto definido en rem y em, y los componentes de altura fija pueden desbordarse. Aumenta el tamaño de texto del sistema y revisa tus maquetaciones antes de publicarlo.

Title, description y canonical

Obligatorio. <title> da nombre a la página en las pestañas del navegador, los marcadores, el historial y los resultados de búsqueda. Si dejas el valor predeterminado Document, todas las pestañas que abras se verán idénticas.

Opcional, pero recomendado. <meta name="description"> ofrece un resumen para los fragmentos de los resultados de búsqueda. Google puede reescribirlo si encuentra contenido de la página que se ajuste mejor a la consulta, y trunca los fragmentos en función del ancho de visualización. Por eso, las reglas basadas en número de caracteres no son fiables.

Opcional, pero recomendado. <link rel="canonical"> indica la URL preferida cuando el mismo contenido es accesible desde más de una dirección, por ejemplo, con query strings o variantes con y sin barra final. Para más detalles, consulta las etiquetas HTML relacionadas con el SEO y cómo optimizarlas.

<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">

Etiquetas Open Graph y Twitter Card

Opcionales, pero obligatorias para cualquier contenido que la gente comparta. Las etiquetas Open Graph controlan la vista previa de los enlaces en las redes sociales y en las aplicaciones de mensajería. Sin ellas, un enlace compartido aparece como una URL sin más o como una tarjeta construida con lo que encuentre el scraper.

El protocolo Open Graph define og:title, og:type, og:image y og:url como propiedades obligatorias, y establece que una página sin tipo debe tratarse como website. Asigna a og:url el mismo valor que tu URL canónica, para que los enlaces con parámetros de seguimiento no se almacenen en caché como páginas distintas. Para og:image, Meta recomienda una imagen de al menos 1200×630 píxeles, con una proporción cercana a 1,91:1.

X lee sus propias etiquetas twitter:*, escritas con name en lugar de property, y recurre a los valores og:* equivalentes cuando faltan. En la práctica, la única etiqueta específica de X que necesitas es twitter:card, y solo si quieres la tarjeta con imagen 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 generar estas etiquetas para cada página, utiliza el generador de etiquetas meta.

El conjunto de favicons

Opcional, pero esperado. Una configuración completa de favicons consta de cuatro partes. Si las omites, obtendrás un icono de pestaña en blanco, un mosaico con aspecto de captura de pantalla en la pantalla de inicio de iOS e iconos de instalación genéricos en Android.

Mantén ambos elementos <link rel="icon">, el de favicon.ico y el de favicon.svg. Así, los navegadores que no pueden usar favicons SVG, incluido Safari anterior a la versión 26, siguen recibiendo el archivo 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 más sobre cómo generar cada archivo, consulta cómo añadir un favicon a tu sitio web.

color-scheme y theme-color

Opcional, recomendado si tu sitio tiene modo oscuro. <meta name="color-scheme" content="light dark"> indica al navegador que la página admite ambos modos antes de que llegue el CSS. De este modo, los fondos predeterminados, los controles de formulario y las barras de desplazamiento siguen la preferencia del usuario. Sin esta etiqueta, un sitio con tema oscuro puede mostrar un destello de lienzo blanco durante la carga. Consulta MDN sobre color-scheme.

Opcional. theme-color tiñe la interfaz del navegador y acepta un atributo media, lo que permite definir valores distintos para el modo claro y el oscuro. Funciona en Chromium para Android, pero MDN indica que la compatibilidad entre navegadores es limitada, así que considéralo un extra agradable y no algo que todos los navegadores vayan a mostrar.

<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)">

Un valor predeterminado para movimiento reducido

Opcional, muy recomendado. Un valor predeterminado para movimiento reducido es una regla de tu hoja de estilos base, no una etiqueta del head. Tiene su lugar en el boilerplate porque nadie vuelve atrás para añadirlo más tarde. Un bloque prefers-reduced-motion: reduce acorta las animaciones y transiciones, y desactiva el desplazamiento suave para los usuarios que han pedido a su sistema operativo que reduzca el movimiento.

@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;
  }
}

Las duraciones se fijan en 0.01ms en lugar de 0 para que animationend y transitionend se sigan disparando. Así, el JavaScript que espera esos eventos no se queda bloqueado. Es un valor predeterminado poco sutil. Cada componente puede volver a habilitar movimientos suaves, como los fundidos de opacidad, que no provocan problemas vestibulares.

¿Qué líneas deberías eliminar de los boilerplates antiguos?

Eliminar. Hay cuatro líneas en los boilerplates HTML antiguos que proceden de plantillas de la época de IE y de los primeros starters responsive, y ninguna de ellas sirve hoy para nada útil:

  • X-UA-Compatible seleccionaba los modos de documento de Internet Explorer. Ningún navegador al que des soporte lo lee.
  • maximum-scale, minimum-scale y user-scalable=no impiden que las personas con baja visión amplíen la página con el gesto de pellizcar, lo que va en contra del criterio 1.4.4 Cambio de tamaño del texto de las WCAG. Elimínalos de todas las etiquetas viewport.
  • shrink-to-fit=no servía para sortear un antiguo comportamiento de escalado de Safari en iOS y ya no tiene ningún efecto.
  • type="text/javascript" repite el valor predeterminado. La especificación de HTML indica a los autores que omitan type en los scripts clásicos.
<!-- 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>

Un script con type="module" se difiere de forma predeterminada, por lo que se ejecuta después del análisis sin bloquear el renderizado. Si un script no puede ser un módulo, añádele defer.

El boilerplate HTML completo

El boilerplate HTML moderno completo que aparece a continuación reúne todas las líneas obligatorias y recomendadas de este artículo. Añade encima los extras específicos de tu sitio, como feeds o preloads, según los necesites. El skip link y el landmark <main> van en el body, y el artículo 10 elementos HTML esenciales trata los elementos semánticos que vienen a continuación.

<!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>
LíneaQué haceQué se rompe si faltaVeredicto
DoctypeModo estándarMaquetación en modo quirksObligatorio
langPronunciación, división silábica, traducciónVoz incorrecta del lector de pantallaObligatorio
charsetDeclara UTF-8Caracteres corruptosObligatorio
viewportMaquetación al ancho del dispositivoMaquetación de escritorio reducida en teléfonosObligatorio
<title>Da nombre a la páginaPestañas y marcadores idénticosObligatorio
Description, canonicalFragmento y URL preferidaFragmentos más pobres, URL duplicadasOpcional
Open Graph, twitter:cardVistas previas de enlacesTarjetas vacías o genéricasOpcional
Conjunto de faviconsIconos de pestaña y de pantalla de inicioIconos en blanco o con aspecto de capturaOpcional
color-schemeModo oscuro nativoDestello blanco, controles clarosOpcional
X-UA-Compatible, límites de zoom, shrink-to-fit, type="text/javascript"Soluciones heredadasNadaEliminar

Solo cinco líneas de un boilerplate HTML son obligatorias. Todo lo demás es opcional, según si la página se comparte, se instala o tiene temas, o bien es una línea heredada que hay que eliminar. Abre el index.html de tu proyecto actual, compara cada línea con la tabla anterior y elimina todo lo que figure en la fila de eliminar antes de copiar el archivo a tu próximo proyecto.

Preguntas frecuentes

¿Es meta charset lo mismo que meta http-equiv Content-Type?

Sí. Según la especificación HTML de WHATWG, la versión http-equiv Content-Type cumple la misma función que el atributo charset. Ambas cuentan como declaración de codificación de caracteres y ambas deben estar dentro de los primeros 1024 bytes. Un documento no debe contener las dos, y los validadores lo marcan como error. Usa la forma más corta, meta charset='UTF-8'. Si el servidor envía un charset en su cabecera HTTP Content-Type, asegúrate de que también indique UTF-8.

¿Las etiquetas meta y link necesitan una barra final en HTML?

No. Meta, link, img y el resto de elementos vacíos (void elements) no tienen etiqueta de cierre, y el parser de HTML ignora la barra final en ellos. Una etiqueta escrita con la barra y la misma etiqueta sin ella producen el mismo elemento. La barra es válida pero no tiene ningún efecto, así que es una cuestión de estilo. El único caso en que importa es una página servida como XHTML con un tipo MIME de XML.

¿Puedo omitir las etiquetas html, head y body?

Sí. La especificación de HTML marca las etiquetas html, head y body como opcionales en muchos casos, y el parser crea esos elementos aunque falten las etiquetas. Aun así, un boilerplate debería escribirlas. El atributo lang necesita una etiqueta de apertura html explícita en la que colocarse, y las etiquetas explícitas mantienen clara la separación entre head y body. Si omites la etiqueta html, también pierdes lang.

¿Cómo cambio el valor predeterminado de lang en el snippet ! de Emmet?

Define la variable lang en el ajuste emmet.variables de VS Code. En settings.json, añade una clave lang con un valor como de al objeto emmet.variables, y Emmet escribirá ese código de idioma cada vez que expanda el snippet !. El mismo ajuste incluye una variable charset, que debes dejar en UTF-8. La línea del viewport sigue conteniendo initial-scale=1.0, así que elimínalo a mano o define un 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.