Desarrollo web y móvil
Fundamentos de HTML: estructura semántica, formularios, multimedia y accesibilidad
Principiante11 min de lecturaActualizado
Esta página se tradujo del inglés con ayuda de IA y se cotejó con el original. Leer la versión en inglés
El HTML moderno es un estándar vivo, no una versión numerada, y los elementos que eliges son una interfaz para navegadores, tecnologías de apoyo y motores de búsqueda a la vez. Esta guía recorre el esqueleto del documento, los elementos semánticos y lo que indica cada uno, la validación nativa de formularios, las imágenes y el contenido multimedia responsive, las tablas accesibles y los metadatos del head que leen otras máquinas.
Lo que aprenderás
- Construir un esqueleto de documento válido y explicar qué hacen realmente el doctype, el charset y el atributo lang
- Elegir los elementos semánticos por su significado y relacionarlos con los landmarks de accesibilidad que exponen
- Usar controles de formulario nativos, tipos de input y validación por restricciones antes de recurrir a JavaScript
- Servir imágenes responsive y contenido multimedia accesible con srcset, picture, texto alternativo y subtítulos
- Escribir metadatos en el head que controlen el título, la URL canónica, las vistas previas en redes sociales y el diseño móvil
HTML tiene fama de ser la parte fácil del desarrollo front-end, y en cierto sentido es justo: nada lanza una excepción, nada deja de compilar y una página con etiquetas mal cerradas se muestra igualmente. Esa tolerancia es precisamente lo que facilita escribirlo mal. El parser repara tus errores en silencio, de modo que un documento puede carecer por completo de estructura y verse bien en tu pantalla, mientras un lector de pantalla lo anuncia como un muro de texto indiferenciado y un rastreador tiene dificultades para averiguar de qué trata la página.
Lo útil es pensar en el marcado como una interfaz con varios consumidores. El navegador lo usa para construir el DOM y el árbol de accesibilidad. Las tecnologías de apoyo recorren ese árbol por landmarks, encabezados y controles. Los motores de búsqueda extraen de él la estructura y los metadatos. Tu propio JavaScript lo consulta. Elegir los elementos con criterio sirve a los cuatro a la vez. Esta guía cubre las partes del lenguaje en las que ese criterio compensa: el esqueleto del documento, el vocabulario semántico, el head, los formularios, el contenido multimedia y las reglas de accesibilidad que se pueden cumplir solo con marcado.
La especificación de referencia
No va a llegar ningún HTML6. Desde 2019 existe una única especificación, el HTML Living Standard del WHATWG, que se actualiza de forma continua en lugar de publicarse por versiones. Cuando hoy alguien dice «HTML5», se refiere al conjunto de elementos moderno y a las API del navegador que llegaron con él, no a una edición congelada. Para la consulta diaria, la documentación de MDN se lee mejor que la propia especificación e incluye tablas de compatibilidad de navegadores que la especificación no tiene.
Un documento mínimo y correcto tiene este aspecto:
<!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 línea se gana su sitio. <!DOCTYPE html> ya no es una declaración de versión: es el interruptor que pone al navegador en modo estándar en lugar del modo quirks que conserva para documentos escritos en los años noventa. El atributo lang indica a los lectores de pantalla qué reglas de pronunciación usar y ayuda a las herramientas de traducción; si lo omites, una página en inglés se lee en voz alta con fonética francesa en un sistema configurado en francés. La declaración de codificación debe aparecer en los primeros 1024 bytes del archivo, por eso va la primera, y siempre debería ser UTF-8. La meta etiqueta viewport impide que los navegadores móviles muestren la página con un ancho de escritorio simulado y luego alejen el zoom.
Fíjate también en lo que falta: los elementos vacíos no necesitan barra de cierre, los valores de atributo solo necesitan comillas cuando contienen espacios o caracteres especiales, y las etiquetas <html>, <head> y <body> son técnicamente opcionales porque el parser las deduce. Escríbelas de todos modos. Las reglas de inferencia son sutiles, y ser explícito cuesta tres líneas.
Elementos con significado
Los elementos de seccionamiento y agrupación existen para que la forma de un documento sea legible por máquinas. La mayoría se corresponde directamente con un landmark de accesibilidad, que es lo que permite a quien usa un lector de pantalla saltar directamente a la navegación o al contenido principal en lugar de escuchar desde el principio cada vez.
| Elemento | Úsalo para | Landmark que expone |
|---|---|---|
header | Contenido introductorio de la página o de una sección | banner, cuando es hijo directo de body |
nav | Un bloque de enlaces de navegación principales | navigation |
main | El contenido principal, una vez por página | main |
article | Un elemento autónomo que tendría sentido sindicado | article |
section | Una agrupación temática, normalmente con encabezado | region, solo cuando tiene nombre accesible |
aside | Contenido tangencial, como una barra lateral o una cita destacada | complementary |
footer | Información de cierre de la página o de una sección | contentinfo, cuando es hijo directo de body |
figure / figcaption | Contenido multimedia con su pie de figura | ninguno |
Dos de ellos se usan mal con frecuencia. section no es una caja genérica: solo se convierte en una región navegable cuando tiene nombre accesible, normalmente proporcionado por un encabezado referenciado con aria-labelledby. Y article no significa «entrada de blog»: un comentario, una ficha de producto y una respuesta en un foro son todos artículos, porque la prueba es si el bloque seguiría teniendo sentido extraído y mostrado por separado.
Los encabezados son la otra mitad de la estructura. Quienes usan lectores de pantalla navegan por encabezados más que por cualquier otro mecanismo, así que los niveles deben describir una jerarquía real: un único h1 que nombra la página, h2 para sus partes principales y h3 para las subdivisiones de estas. Nunca elijas un nivel por su tamaño de letra por defecto, para eso está CSS, y nunca te saltes un nivel al bajar. Un div sigue siendo la respuesta correcta cuando un contenedor existe solo por maquetación y ningún elemento describe lo que hay dentro; el problema es únicamente recurrir a él primero.
El head y los metadatos que leen otras máquinas
El head es donde hablas con sistemas que nunca renderizan tu página. Un documento en producción suele llevar bastante más que lo 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>
El title es la cadena más valiosa de la página: se convierte en la pestaña del navegador, en el marcador, en lo primero que anuncia un lector de pantalla al cargar y, normalmente, en el titular del resultado de búsqueda. Escríbelo de lo específico a lo general, con el nombre del sitio al final. La descripción no es un factor de posicionamiento, pero a menudo se usa tal cual como fragmento del resultado, así que debe leerse como una frase y no como una lista de palabras clave. El enlace canónico resuelve los problemas de URL duplicadas (parámetros de seguimiento, barras finales, versiones para imprimir) indicando la dirección que quieres que se indexe.
Las etiquetas Open Graph controlan cómo se muestra un enlace cuando alguien lo pega en un chat o una red social. Cuestan muy poco y brillan por su ausencia en la mayoría de las herramientas internas. Los resource hints como preconnect y preload recortan latencia hacia orígenes de terceros; usados sin criterio consiguen lo contrario, y conviene entender sus compromisos junto con las técnicas más amplias de rendimiento de aplicaciones.
Formularios, campos y la validación que te llega gratis
Un formulario es la parte funcionalmente más densa de la mayoría de las páginas, y el lugar donde el JavaScript hecho a mano sustituye con más frecuencia algo que el navegador ya hace mejor.
<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 control necesita un nombre accesible, y la forma fiable de dárselo es un label cuyo for coincida con el id del campo. Un placeholder no es una etiqueta: desaparece en cuanto se empieza a escribir, no cumple los requisitos de contraste en la mayoría de los diseños y algunas tecnologías de apoyo lo ignoran. El atributo name es lo que aparece en los datos enviados, así que un control sin él no envía nada.
Elegir el type adecuado te da comportamiento que de otro modo tendrías que programar: un teclado móvil apropiado, un selector de fecha, un selector de color, la comprobación del formato. Combínalo con required, pattern, min, max, step y maxlength y tendrás una validación declarativa que funciona sin una sola línea de script, expone :invalid y :user-invalid a CSS y es accesible con teclado por diseño. Cuando necesites reglas propias, la Constraint Validation API te permite conservar el mismo mecanismo: llama a setCustomValidity() sobre el elemento, o añade novalidate al formulario y gestiona tú los mensajes.
Acertar con los tokens de autocomplete es un cambio pequeño con un efecto desproporcionado en la tasa de formularios completados, porque permite al navegador y al gestor de contraseñas rellenar los valores conocidos. Y nada de esto es una barrera de seguridad: los atributos se pueden editar en las devtools y las peticiones se pueden enviar directamente a tu endpoint, así que el servidor debe revalidarlo todo y responder con un código de estado preciso. La diferencia entre un cuerpo mal formado y uno semánticamente inválido se explica en la referencia de códigos de estado HTTP.
Imágenes, multimedia y entrega responsive
Las imágenes suelen ser la mayor parte de los bytes de una página, así que aquí las decisiones de marcado son las que más pesan en el tiempo de carga.
<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 ofrece al navegador varias resoluciones de la misma imagen y sizes le dice qué ancho tendrá realmente en la maquetación, para que pueda elegir antes de haber analizado el CSS. Define siempre width y height: permiten al navegador reservar el hueco correcto antes de que llegue el archivo, lo que elimina el desplazamiento de diseño que mueve el texto mientras la página carga. loading="lazy" aplaza las imágenes fuera de pantalla, pero no lo apliques nunca a la imagen de la parte superior de la página: es justo la que quieres descargar primero.
Cuando necesites fuentes realmente distintas y no solo tamaños distintos (un recorte cuadrado en móvil, o un formato moderno con respaldo), usa picture con varios elementos source y un img final. Sea cual sea el contenedor, el img lleva el texto alt, y escribirlo bien es el hábito de accesibilidad más rentable que existe: describe la información que la imagen transmite en su contexto, no el archivo. Un adorno decorativo lleva alt="" para que se omita por completo; si en cambio omites el atributo, los lectores de pantalla leen en voz alta el nombre del archivo. Antes de publicar recursos, merece la pena pasarlos por un optimizador como el compresor de imágenes o reducir los originales demasiado grandes con el redimensionador de imágenes.
El vídeo y el audio siguen la misma lógica. Añade controls salvo que los sustituyas por controles accesibles, proporciona subtítulos con un elemento track de kind="captions", incluye un fotograma poster y evita autoplay con sonido. Usa preload="metadata" en lugar del valor por defecto para que una página con varios clips no empiece a descargarlos todos.
Tablas que sobreviven a un lector de pantalla
Las tablas son para datos, nunca para maquetar. El marcado que hace navegable una tabla es breve:
<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>
El caption da nombre a la tabla, th con scope indica a las tecnologías de apoyo qué celdas son encabezados y en qué dirección, y marcar la primera celda de cada fila como encabezado de fila permite a un lector de pantalla anunciar «North, median days, 3.1» en lugar de un número suelto. En tablas irregulares con celdas combinadas, los atributos headers e id permiten asociar las celdas de forma explícita. Si una tabla es ancha, envuélvela en un contenedor con desplazamiento que tenga tabindex="0" y un rol, para que quienes usan el teclado puedan llegar a la parte que desborda.
Las API que te da la plataforma
Además de los elementos, los navegadores exponen una amplia biblioteca estándar que conviene conocer antes de añadir una dependencia para lo mismo. localStorage y sessionStorage guardan valores de texto pequeños de forma síncrona y son perfectos para una preferencia de tema, e inadecuados para cualquier dato sensible, ya que cualquier script del mismo origen puede leerlos. IndexedDB se encarga del almacenamiento estructurado, más grande y asíncrono. canvas ofrece una superficie de píxeles para gráficos y procesamiento de imágenes, mientras que el SVG inline se mantiene nítido y sigue en el DOM, donde se le pueden aplicar estilos y hacerlo accesible. La History API reescribe la barra de direcciones sin recargar, que es como funcionan los routers del lado del cliente. Los Web Workers sacan los cálculos costosos del hilo principal. Los custom elements permiten empaquetar un widget como una etiqueta real con sus propios estilos encapsulados. A la mayoría se accede mediante JavaScript, y las características del lenguaje que necesitas para usarlas se explican en fundamentos de JavaScript.
Validar, y los errores que sobreviven a la revisión
Pasa tu marcado por el validador del W3C, o por un linter equivalente integrado en tu build, y trata sus avisos como errores de build. Como el parser repara los errores en silencio, validar es la única forma barata de detectar problemas estructurales. Los más habituales:
- Valores de
idduplicados, normalmente por una plantilla repetida. Las asociaciones de etiquetas y los enlaces a fragmentos apuntan entonces al primer elemento que aparezca. - Campos sin etiqueta, escondidos tras un placeholder o un icono sin nombre accesible.
- Niveles de encabezado elegidos por tamaño, que producen un esquema que salta de
h1ah4. - Elementos
divinteractivos con un manejador de clic y sin soporte de teclado. Si se comporta como un botón, conviértelo en unbutton. - Texto
altque repite el pie de figura o empieza por «imagen de», algo que los lectores de pantalla ya anuncian. - Falta de
widthyheighten las imágenes, la causa más común de desplazamiento de diseño. - Controles interactivos anidados, como un enlace dentro de un botón, cuyo comportamiento con teclado no está definido.
- ARIA usado para parchear un mal marcado. Un rol incorrecto es peor que ningún rol; la primera regla de ARIA es usar el elemento nativo.
Puntos clave
- HTML es un estándar vivo sin números de versión: escribe siguiendo la especificación del WHATWG y comprueba la compatibilidad en MDN en lugar de esperar la próxima versión.
- Los elementos son una interfaz para navegadores, tecnologías de apoyo y rastreadores a la vez: elígelos por su significado y obtendrás gratis los landmarks de accesibilidad y el esquema del documento.
- El head controla cómo ven la página las máquinas: un título preciso, una descripción real, una URL canónica y etiquetas Open Graph cuestan minutos y rinden para siempre.
- Los controles de formulario nativos, los tipos de input y la validación por restricciones ofrecen una validación accesible y cómoda con teclado sin script, pero nunca sustituyen la validación en el servidor.
- Las imágenes responsive con
srcset, dimensiones explícitas, textoalthonesto y contenido multimedia subtitulado son las mejoras de rendimiento y accesibilidad más baratas que hay. - Valida tu marcado y trata los fallos recurrentes (ids duplicados, campos sin etiqueta, divs clicables, ARIA tapando el elemento equivocado) como defectos, no como opiniones de estilo.
Fuentes y lecturas recomendadas
Preguntas frecuentes
- ¿Sigue siendo HTML5 la versión actual de HTML?
- No. HTML dejó de publicarse en versiones numeradas en 2019, cuando el W3C y el WHATWG acordaron que el HTML Living Standard del WHATWG sería la única especificación. Se actualiza de forma continua, así que las novedades llegan sin cambio de versión. El término HTML5 sobrevive como forma abreviada de referirse al conjunto de elementos moderno y a las API del navegador que llegaron con él, pero no hay ningún HTML6 a la vista.
- ¿Cuándo debo usar un div en lugar de un elemento semántico?
- Usa un div cuando el contenedor solo exista por estilo o maquetación y ningún elemento existente describa su contenido. Si el bloque es la navegación del sitio, un contenido autónomo, el pie de página o el contenido principal del documento, el elemento semántico correspondiente da gratis un landmark a las tecnologías de apoyo. Empezar con un div y añadir después un rol ARIA supone más trabajo y es más fácil equivocarse.
- ¿Necesito validación en el cliente si el servidor ya valida?
- Sí, pero por otro motivo. La validación por restricciones del navegador ofrece feedback inmediato y accesible por teclado, y frena los envíos claramente mal formados, lo que mejora la usabilidad. No es un control de seguridad, porque cualquiera puede desactivar JavaScript, editar los atributos o enviar la petición directamente a tu endpoint. Trata cada campo como no fiable en el servidor y devuelve un código de estado preciso cuando falle.
- ¿Qué diferencia hay entre srcset y el elemento picture?
- srcset junto con sizes deja que el navegador elija entre versiones de la misma imagen que solo se diferencian en resolución; el navegador decide según el ancho del viewport, la densidad de píxeles del dispositivo y sus propias heurísticas. El elemento picture sirve para los casos en que tienes que controlar tú la elección: un recorte distinto en pantallas pequeñas, o un archivo AVIF con un JPEG de respaldo para los navegadores que no pueden decodificarlo.