HTML (HyperText Markup Language) es el lenguaje de marcado estándar para estructurar el contenido en la web. Define el significado semántico de cada fragmento de una página, habilita la entrada de datos mediante formularios, incrusta contenido multimedia accesible y proporciona puntos de anclaje sobre los que CSS aplica estilos y JavaScript añade comportamiento. El HTML moderno es una especificación viva mantenida por el WHATWG que incorpora continuamente APIs declarativas para reducir la cantidad de código necesario y mejorar la accesibilidad por defecto.
¿Qué aporta HTML moderno?
HTML ha evolucionado desde la simple maquetación de documentos hasta convertirse en la base de aplicaciones web complejas. Las versiones actuales del estándar incluyen mecanismos declarativos que antes requerían JavaScript y librerías externas.
El ecosistema moderno destaca por los siguientes frentes.
- Semántica explícita: elementos como
<article>,<section>,<nav>,<aside>,<figure>y el reciente<search>comunican el rol de cada bloque a navegadores, buscadores y tecnologías de asistencia. - Formularios nativos potentes: tipos especializados (
email,url,tel,date,color,search), atributos de validación (required,minlength,pattern) y elementos como<datalist>reducen la dependencia de JavaScript. - APIs declarativas:
<dialog>, el atributopopover, la Popover API, la View Transitions API y la Close Watcher API permiten construir modales, menús, tarjetas emergentes y transiciones de ruta con muy pocas líneas de código. - Web Components: Custom Elements, Shadow DOM y Declarative Shadow DOM encapsulan componentes reutilizables y permiten renderizado desde el servidor sin sacrificar el aislamiento de estilos.
- Accesibilidad ARIA: los roles y atributos ARIA completan la semántica cuando un componente no puede expresarse con elementos nativos, siempre respetando la regla de oro de no usar ARIA si existe un elemento HTML equivalente.
Estructura mínima de un documento HTML5
Todo documento parte de una estructura canónica que el navegador espera encontrar en ese orden: declaración de tipo, raíz <html>, cabecera <head> con metadatos y cuerpo <body> con el contenido visible.
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Título de la página</title>
<meta name="description" content="Descripción breve para buscadores y redes sociales">
</head>
<body>
<!-- Contenido visible -->
</body>
</html>
El diagrama siguiente muestra la jerarquía canónica del documento y cómo las distintas ramas nutren al árbol DOM que el navegador construye para renderizar la página.
flowchart TB
Doctype["DOCTYPE html"] --> HTML["html (lang)"]
HTML --> Head["head"]
HTML --> Body["body"]
Head --> Meta["meta charset, viewport, description"]
Head --> Title["title"]
Head --> Link["link hojas estilos"]
Head --> Script["script type module"]
Body --> Header["header"]
Body --> Main["main"]
Body --> Footer["footer"]
Main --> Section["section"]
Section --> Article["article"]
Section --> Aside["aside"]
Del servidor al DOM: flujo de renderizado
Cuando el usuario solicita una URL, el navegador descarga el HTML, crea el árbol DOM y el árbol CSSOM, los combina en el render tree, calcula el layout y pinta los píxeles. Cada etiqueta del documento ocupa una posición concreta en el árbol DOM.
flowchart LR
A["Usuario solicita URL"] --> B["Servidor envia HTML"]
B --> C["Navegador parsea HTML"]
C --> D["Construye árbol DOM"]
C --> E["Descarga CSS"]
E --> F["Construye CSSOM"]
D --> G["Combina DOM y CSSOM"]
F --> G
G --> H["Render tree"]
H --> I["Layout y paint"]
I --> J["Página visible"]
El DOM es el puente que conecta HTML con CSS y JavaScript. Cada elemento es un nodo accesible desde scripts mediante document.querySelector y se estiliza mediante selectores de CSS. La siguiente representación muestra un fragmento de DOM tree elemental.
graph TD
html["html"] --> head["head"]
html --> body["body"]
head --> meta["meta"]
head --> title["title"]
body --> header["header"]
body --> main["main"]
body --> footer["footer"]
header --> h1["h1"]
header --> nav["nav"]
main --> section1["section"]
section1 --> article1["article"]
article1 --> h2["h2"]
article1 --> p["p"]
Atributos, herencia y cascada
Los atributos modifican el comportamiento de los elementos. Además, muchos atributos HTML y propiedades CSS aplicadas sobre un elemento se heredan a sus descendientes, lo que permite definir estilos globales sin repetirlos.
flowchart TB
Root["html (lang=es)"] --> Body["body"]
Body --> Main["main"]
Main --> Section["section"]
Section --> P["p"]
P --> EM["em"]
Root -. hereda lang .-> EM
Body -. hereda color, font-family .-> P
P -. hereda color .-> EM
Main -. contexto semántico .-> Section
Los atributos globales (id, class, lang, dir, tabindex, hidden, inert, data-*) aplican a casi cualquier elemento y conviven con los atributos específicos de cada etiqueta.
Jerarquía de encabezados y semántica de página
Los encabezados <h1> a <h6> definen el índice lógico del documento. Una página pública bien estructurada tiene un <h1> único, y los subtítulos se anidan respetando la jerarquía sin saltos.
flowchart TB
H1["h1 Título principal"] --> H2a["h2 Sección A"]
H1 --> H2b["h2 Sección B"]
H2a --> H3a1["h3 Subsección A1"]
H2a --> H3a2["h3 Subsección A2"]
H2b --> H3b1["h3 Subsección B1"]
H3a1 --> H4["h4 Detalle"]
Los elementos semánticos (<header>, <nav>, <main>, <section>, <article>, <aside>, <footer>, <search>) envuelven la estructura visible y exponen landmarks a los lectores de pantalla.
Formularios y ciclo de validación
Los formularios son la principal vía de entrada de datos del usuario. El navegador valida en cliente antes de enviar al servidor y expone una segunda capa de validación obligatoria en backend.
flowchart LR
A["Usuario rellena formulario"] --> B["Validación nativa del navegador"]
B -->|Datos válidos| C["Envio HTTP POST"]
B -->|Errores| A
C --> D["Servidor recibe y valida"]
D -->|Datos válidos| E["Procesa y responde 200"]
D -->|Errores| F["Devuelve 400 con mensajes"]
F --> A
E --> G["Confirmación al usuario"]
Atributos clave:
required: bloquea el envío si el campo está vacío.minlengthymaxlength: longitud mínima y máxima de caracteres.pattern: expresión regular que debe cumplir el valor.type: determina las reglas de validación (email,url,tel,number,date,search,color).autocomplete: activa el autorrelleno del navegador.novalidateen<form>: desactiva la validación nativa para implementarla por JavaScript.
APIs declarativas: dialog, popover y view transitions
El HTML moderno cubre casos de uso que antes exigían librerías externas.
- Elemento
<dialog>: modales nativos con gestión automática de foco, escape y backdrop (showModal()yclose()). - Atributo
popovery Popover API: tooltips, menús contextuales y tarjetas emergentes conpopovertargetypopovertargetaction. - View Transitions API: transiciones animadas declarativas entre estados del DOM o rutas de navegación (
document.startViewTransition). - Close Watcher API: gestión uniforme del cierre con Escape o back button en móviles.
- Interest Invoker API: declaración de intención al hover o al focus para anticipar recursos con
interesttarget. - Priority Hints: atributo
fetchprioritypara influir en el orden de carga de recursos críticos.
<button popovertarget="menu">Abrir menú</button>
<div id="menu" popover>
<nav>
<a href="/inicio">Inicio</a>
<a href="/servicios">Servicios</a>
</nav>
</div>
<dialog id="modal">
<form method="dialog">
<p>¿Confirmas la acción?</p>
<button value="ok">Aceptar</button>
<button value="cancel">Cancelar</button>
</form>
</dialog>
Multimedia responsive
La carga eficiente de imágenes combina <picture> con varios <source>, el atributo srcset con descriptores de ancho, sizes para indicar el espacio ocupado y loading="lazy" para diferir imágenes bajo el pliegue.
<picture>
<source type="image/avif" srcset="portada.avif 1x, portada-2x.avif 2x">
<source type="image/webp" srcset="portada.webp 1x, portada-2x.webp 2x">
<img src="portada.jpg" alt="Vista de la ciudad al amanecer" loading="lazy" decoding="async" fetchpriority="high">
</picture>
Para vídeo, el elemento <video> admite controls, poster, múltiples <source> y pistas <track> para subtítulos y descripciones.
Tablas accesibles
Las tablas de datos se marcan con <table>, <caption>, <thead>, <tbody>, <tfoot>, <th> con atributo scope y <td>. En tablas complejas se relacionan cabeceras y celdas mediante headers e id.
<table>
<caption>Ventas mensuales por región</caption>
<thead>
<tr>
<th scope="col">Región</th>
<th scope="col">Enero</th>
<th scope="col">Febrero</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Norte</th>
<td>120</td>
<td>150</td>
</tr>
</tbody>
</table>
Accesibilidad moderna con ARIA
ARIA añade semántica cuando HTML nativo no cubre el caso. Los patrones modernos de autoría recomiendan:
- Preferir elementos nativos (
<button>,<nav>,<dialog>) arole="button"sobre<div>. - Utilizar
aria-labelyaria-labelledbypara exponer nombres accesibles cuando el texto visible no basta. - Usar
aria-live(polite o assertive) en regiones dinámicas como notificaciones, chats o formularios asíncronos. - Marcar imágenes decorativas con
alt=""y contenido fuera de flujo conaria-hidden="true"solo si es realmente decorativo. - Emplear el nuevo atributo
inertpara neutralizar completamente subárboles del DOM mientras un modal está abierto. - Respetar el orden de tabulación nativo; recurrir a
tabindex="-1"únicamente para focos programáticos.
Web Components: custom elements y shadow DOM
Los web components permiten definir elementos personalizados totalmente encapsulados, renderizables desde el servidor con Declarative Shadow DOM y reutilizables entre proyectos sin dependencias externas.
<user-card>
<template shadowrootmode="open">
<style>
.card { padding: 1rem; border: 1px solid #ccc; }
</style>
<div class="card">
<slot name="nombre">Sin nombre</slot>
</div>
</template>
<span slot="nombre">Ana Ruiz</span>
</user-card>
El navegador instancia el shadow root declarado en el servidor sin esperar a JavaScript, lo que mejora el tiempo de primer contenido visible y facilita el SEO de componentes.
El siguiente diagrama ilustra la relación entre el light DOM (contenido del documento principal) y el shadow DOM (árbol encapsulado interno del componente), con sus slots y estilos aislados.
flowchart TB
Host["user-card (host)"] --> Light["Light DOM<br/>span slot=nombre"]
Host --> Shadow["Shadow DOM (shadow root)"]
Shadow --> Style["style scoped"]
Shadow --> Card["div card"]
Card --> SlotNode["slot name=nombre"]
Light -. proyectado .-> SlotNode
Style -. aislamiento .-> Card
Metadatos, SEO y Open Graph
Los metadatos del <head> configuran cómo se presenta la página en buscadores y redes sociales.
<meta property="og:title" content="Título atractivo para compartir">
<meta property="og:description" content="Descripción breve para redes sociales">
<meta property="og:image" content="https://ejemplo.com/og/imagen.webp">
<meta property="og:url" content="https://ejemplo.com/articulo">
<link rel="canonical" href="https://ejemplo.com/articulo">
Las metaetiquetas Open Graph y Twitter Card controlan la previsualización, rel="canonical" indica la URL autoritativa y los datos estructurados con JSON-LD mejoran los fragmentos enriquecidos.
Buenas prácticas profesionales
Un documento HTML listo para producción cumple varios requisitos.
- Declaración
<!DOCTYPE html>en la primera línea. <html lang="es">para indicar el idioma.<meta charset="UTF-8">y<meta name="viewport" content="width=device-width, initial-scale=1">en el<head>.- Título único y descriptivo por página.
- Metadatos Open Graph para compartir en redes sociales.
- Imágenes con
altdescriptivo oalt=""si son decorativas,loading="lazy"bajo el pliegue. - Formularios con etiquetas
<label>asociadas porfor/idy validación nativa. - Tablas con
<caption>,<thead>,<tbody>,<th scope>y relaciónheaders/iden casos complejos. - Estructura semántica con
<header>,<main>,<footer>,<nav>,<section>,<article>,<aside>y<search>donde proceda. - Validación con el servicio W3C antes de publicar.