Proyecto: un blog con HTML semántico
En esta lección
- Paso 1: planificar
- Paso 2: el <head> con metadatos
- Paso 3: la cabecera y la navegación
- Paso 4: la lista de entradas (página de inicio)
- Paso 5: la entrada completa
- Paso 6: los comentarios y el formulario
- Paso 7: el pie de página
- Paso 8: datos estructurados para buscadores
- El código final
- Paso 9: comprobar el resultado
- Ideas para ampliarlo
- Errores frecuentes
- Resumen
Un blog es el mejor ejercicio para practicar HTML semántico, porque casi todas las etiquetas del curso tienen aquí un sitio natural: cabecera, navegación, artículos, imágenes con pie, citas, notas laterales, fechas, un formulario y un pie de página. En este proyecto vas a construir paso a paso Cuaderno de Monte, un blog ficticio de senderismo, con dos páginas: la lista de entradas y una entrada completa con comentarios. Al final tendrás el código completo y una lista de comprobación.
No hace falta CSS para que la estructura sea correcta; al final añadiremos unos estilos mínimos solo para que se vea decente. Si necesitas repasar alguna etiqueta, tienes HTML semántico y el head y los metadatos.
Paso 1: planificar
Antes de escribir, dibuja en papel qué lleva cada página. Es una costumbre barata que evita rehacer el HTML después.
Página de inicio (index.html) Entrada (articulo.html)
- cabecera con nombre y menú - cabecera con nombre y menú
- lista de entradas recientes - artículo
- cada entrada: título, fecha, - título, autor, fecha
resumen, enlace - imagen con pie
- barra lateral con categorías - texto con subtítulos y cita
- pie - nota lateral
- comentarios + formulario
- pie
Para cada bloque pregúntate qué es, no cómo se verá: una lista de entradas es una lista de artículos independientes; la barra lateral es contenido relacionado pero prescindible. Eso te dice qué etiqueta usar.
La estructura de carpetas es sencilla:
cuaderno-de-monte/
index.html
articulo.html
css/estilos.css
img/valle.jpg
Paso 2: el <head> con metadatos
El <head> es lo primero que leen los buscadores y las redes sociales. Este es el de la página de la entrada:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Mi primera noche en un refugio de montaña | Cuaderno de Monte</title>
<meta name="description" content="Qué llevar, qué sobra y qué aprendí en mi primera noche en un refugio de montaña. Consejos prácticos para principiantes.">
<link rel="canonical" href="https://cuadernodemonte.example/primera-noche-en-refugio/">
<!-- Para redes sociales (Open Graph) -->
<meta property="og:type" content="article">
<meta property="og:title" content="Mi primera noche en un refugio de montaña">
<meta property="og:description" content="Qué llevar, qué sobra y qué aprendí en mi primera noche en un refugio.">
<meta property="og:image" content="https://cuadernodemonte.example/img/valle-1200x630.jpg">
<meta property="og:url" content="https://cuadernodemonte.example/primera-noche-en-refugio/">
<meta name="twitter:card" content="summary_large_image">
<link rel="stylesheet" href="css/estilos.css">
</head>
Los puntos importantes:
lang="es"en<html>, para lectores de pantalla y traductores.- Un
<title>distinto en cada página y unadescriptionde unos 150 caracteres. Son el título y el texto que Google muestra en sus resultados. canonicalindica la dirección oficial de la página, para que no se indexen duplicados.- Las etiquetas
og:controlan cómo se ve el enlace al compartirlo. La imagen suele medir 1200 x 630 píxeles. Más detalle en SEO en HTML.
Paso 3: la cabecera y la navegación
En todas las páginas se repite el mismo bloque:
<body>
<header>
<p class="marca"><a href="index.html">Cuaderno de Monte</a></p>
<nav aria-label="Principal">
<ul>
<li><a href="index.html" aria-current="page">Blog</a></li>
<li><a href="rutas.html">Rutas</a></li>
<li><a href="sobre-mi.html">Sobre mí</a></li>
</ul>
</nav>
</header>
Detalles que marcan la diferencia:
- El nombre del blog va en un
<p>y no en un<h1>: en cada página, el<h1>debe ser el título de ese contenido. - La navegación es una lista (
<ul>) dentro de<nav>, conaria-labelpara distinguirla de otras navegaciones. aria-current="page"marca el enlace de la página actual para los lectores de pantalla y te permite darle estilo.
Paso 4: la lista de entradas (página de inicio)
Cada entrada de la lista es un <article>: un trozo de contenido que tendría sentido por sí solo, por ejemplo en un lector de noticias.
<main>
<h1>Últimas entradas</h1>
<article>
<h2><a href="articulo.html">Mi primera noche en un refugio de montaña</a></h2>
<p><time datetime="2026-09-14">14 de septiembre de 2026</time> · 5 min de lectura</p>
<p>Qué llevar, qué sobra y lo que aprendí al dormir a 2.100 metros.</p>
<p><a href="articulo.html">Leer la entrada completa</a></p>
</article>
<article>
<h2><a href="#">Cinco rutas fáciles para empezar en otoño</a></h2>
<p><time datetime="2026-09-02">2 de septiembre de 2026</time> · 4 min de lectura</p>
<p>Senderos cortos, bien señalizados y con buenas vistas.</p>
<p><a href="#">Leer la entrada completa</a></p>
</article>
</main>
Observa:
- Un solo
<h1>por página, y los títulos de las entradas son<h2>. - El título es el propio enlace: así la persona que navega con lector de pantalla oye una lista de títulos claros.
- La fecha va dentro de
<time>. El texto es lo que lee la persona (“14 de septiembre de 2026”) y el atributodatetimees el valor que entienden las máquinas, siempre en formatoAAAA-MM-DD. - El texto del último enlace, “Leer la entrada completa”, es descriptivo. Evita enlaces que solo digan “Pincha aquí”.
Si quieres una barra lateral con categorías, va fuera de <main> o dentro de él como <aside>, con su propio título y una lista de enlaces.
Paso 5: la entrada completa
Esta es la parte central del proyecto. Todo el contenido de la entrada va dentro de un <article>, y su cabecera lleva el título, el autor y la fecha:
<main>
<article>
<header>
<h1>Mi primera noche en un refugio de montaña</h1>
<p>
Por <a href="sobre-mi.html">Marta Ibáñez</a> ·
<time datetime="2026-09-14">14 de septiembre de 2026</time> ·
5 min de lectura
</p>
</header>
Un <header> dentro de un <article> es la cabecera de esa entrada, no la de la página. Es normal tener varios <header> en un documento.
La imagen con <figure>
<figure>
<img src="img/valle.jpg" width="800" height="400"
alt="Un valle con montañas al atardecer y un sendero en primer plano">
<figcaption>El valle desde el refugio, poco antes de cenar.</figcaption>
</figure>
<figure> agrupa la imagen con su pie <figcaption>. El alt describe lo que se ve, para quien no puede verla; el figcaption es el pie visible para todos, así que no repitas el mismo texto. Los atributos width y height reservan el hueco y evitan que la página “salte” al cargar la imagen. Si la tienes en varios tamaños, mira imágenes responsive.
El texto con subtítulos
<p>Llegué a las seis de la tarde con las piernas cargadas y una mochila demasiado pesada.</p>
<h2>Lo que metí en la mochila</h2>
<p>Aprendí rápido qué sobraba: el segundo par de botas y casi toda la comida.</p>
Los subtítulos son <h2>, y si hay subapartados, <h3>. No saltes niveles (de <h2> a <h4>) y no elijas un nivel por su tamaño: el tamaño se cambia con CSS.
La cita con <blockquote>
<blockquote cite="https://cuadernodemonte.example/entrevista-guarda/">
<p>En la montaña, lo que no llevas no pesa.</p>
<footer>— El guarda del refugio</footer>
</blockquote>
<blockquote> es para citas largas de otra persona. El atributo cite guarda la URL de la fuente (no se ve, pero la usan las máquinas) y el autor visible va aparte, en un <footer> o en un <cite>. Para una cita corta dentro de una frase, usa <q>.
La nota lateral con <aside>
<aside>
<h2>Para saber más</h2>
<p>Consulta la <a href="material.html">lista de material imprescindible</a> antes de salir.</p>
</aside>
</article>
Un <aside> contiene algo relacionado con el contenido, pero prescindible: si lo quitas, el artículo se entiende igual. Una pregunta útil para decidir: “¿se podría mover a un cuadro al margen?”.
Paso 6: los comentarios y el formulario
Los comentarios son una sección (<section>) con su propio título, porque es un bloque temático dentro de la página. Cada comentario es un <article>, ya que es contenido independiente escrito por otra persona:
<section aria-labelledby="comentarios">
<h2 id="comentarios">Comentarios (1)</h2>
<article class="comentario">
<h3>Luis</h3>
<p><time datetime="2026-09-15T09:30">15 de septiembre, 9:30</time></p>
<p>Yo también llevé de más la primera vez.</p>
</article>
<form action="/comentarios" method="post">
<h3>Deja tu comentario</h3>
<p>
<label for="nombre">Nombre</label><br>
<input id="nombre" name="nombre" autocomplete="nickname" required>
</p>
<p>
<label for="texto">Comentario</label><br>
<textarea id="texto" name="texto" rows="4" maxlength="1000" required></textarea>
</p>
<p><button type="submit">Publicar</button></p>
</form>
</section>
</main>
Lo esencial del formulario:
- Cada campo tiene su
<label>enlazado conforeid: así se puede pulsar la etiqueta para enfocar el campo y los lectores de pantalla la anuncian. requiredymaxlengthhacen una primera validación sin JavaScript; el servidor tiene que repetirla siempre. Tienes más en validar formularios.autocompleteayuda al navegador a rellenar los datos.aria-labelledbyen la<section>la nombra con el texto del<h2>, de modo que sea un punto de referencia para quien navega con lector de pantalla.
Paso 7: el pie de página
<footer>
<p>© 2026 Cuaderno de Monte · <a href="privacidad.html">Política de privacidad</a></p>
</footer>
</body>
</html>
Un <footer> colgado directamente del <body> es el pie de la página. Suele llevar el copyright, el aviso legal y enlaces secundarios.
Paso 8: datos estructurados para buscadores
Un último extra para que Google entienda que la página es un artículo: un bloque JSON-LD dentro del <head> (explicado en datos estructurados):
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "Mi primera noche en un refugio de montaña",
"datePublished": "2026-09-14",
"author": { "@type": "Person", "name": "Marta Ibáñez" },
"image": "https://cuadernodemonte.example/img/valle-1200x630.jpg"
}
</script>
Los datos tienen que coincidir con lo que se ve en la página.
El código final
Esta es la página articulo.html completa, con todo lo anterior junto:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Mi primera noche en un refugio de montaña | Cuaderno de Monte</title>
<meta name="description" content="Qué llevar, qué sobra y qué aprendí en mi primera noche en un refugio de montaña. Consejos prácticos para principiantes.">
<link rel="canonical" href="https://cuadernodemonte.example/primera-noche-en-refugio/">
<meta property="og:type" content="article">
<meta property="og:title" content="Mi primera noche en un refugio de montaña">
<meta property="og:description" content="Qué llevar, qué sobra y qué aprendí en mi primera noche en un refugio.">
<meta property="og:image" content="https://cuadernodemonte.example/img/valle-1200x630.jpg">
<meta property="og:url" content="https://cuadernodemonte.example/primera-noche-en-refugio/">
<meta name="twitter:card" content="summary_large_image">
<link rel="stylesheet" href="css/estilos.css">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "Mi primera noche en un refugio de montaña",
"datePublished": "2026-09-14",
"author": { "@type": "Person", "name": "Marta Ibáñez" },
"image": "https://cuadernodemonte.example/img/valle-1200x630.jpg"
}
</script>
</head>
<body>
<header>
<p class="marca"><a href="index.html">Cuaderno de Monte</a></p>
<nav aria-label="Principal">
<ul>
<li><a href="index.html" aria-current="page">Blog</a></li>
<li><a href="rutas.html">Rutas</a></li>
<li><a href="sobre-mi.html">Sobre mí</a></li>
</ul>
</nav>
</header>
<main>
<article>
<header>
<h1>Mi primera noche en un refugio de montaña</h1>
<p>
Por <a href="sobre-mi.html">Marta Ibáñez</a> ·
<time datetime="2026-09-14">14 de septiembre de 2026</time> ·
5 min de lectura
</p>
</header>
<figure>
<img src="img/valle.jpg" width="800" height="400"
alt="Un valle con montañas al atardecer y un sendero en primer plano">
<figcaption>El valle desde el refugio, poco antes de cenar.</figcaption>
</figure>
<p>Llegué a las seis de la tarde con las piernas cargadas y una mochila demasiado pesada.</p>
<h2>Lo que metí en la mochila</h2>
<p>Aprendí rápido qué sobraba: el segundo par de botas y casi toda la comida.</p>
<blockquote cite="https://cuadernodemonte.example/entrevista-guarda/">
<p>En la montaña, lo que no llevas no pesa.</p>
<footer>— El guarda del refugio</footer>
</blockquote>
<aside>
<h2>Para saber más</h2>
<p>Consulta la <a href="material.html">lista de material imprescindible</a> antes de salir.</p>
</aside>
</article>
<section aria-labelledby="comentarios">
<h2 id="comentarios">Comentarios (1)</h2>
<article class="comentario">
<h3>Luis</h3>
<p><time datetime="2026-09-15T09:30">15 de septiembre, 9:30</time></p>
<p>Yo también llevé de más la primera vez.</p>
</article>
<form action="/comentarios" method="post">
<h3>Deja tu comentario</h3>
<p>
<label for="nombre">Nombre</label><br>
<input id="nombre" name="nombre" autocomplete="nickname" required>
</p>
<p>
<label for="texto">Comentario</label><br>
<textarea id="texto" name="texto" rows="4" maxlength="1000" required></textarea>
</p>
<p><button type="submit">Publicar</button></p>
</form>
</section>
</main>
<footer>
<p>© 2026 Cuaderno de Monte · <a href="privacidad.html">Política de privacidad</a></p>
</footer>
</body>
</html>
Y unos estilos mínimos para css/estilos.css:
body {
font-family: Georgia, serif;
line-height: 1.6;
max-width: 42rem;
margin: 0 auto;
padding: 0 16px;
}
nav ul { display: flex; gap: 16px; list-style: none; padding: 0; }
img { max-width: 100%; height: auto; }
blockquote { padding-left: 16px; border-left: 4px solid #2f6f4f; }
aside { background: #f3efe6; padding: 8px 16px; border-radius: 6px; }
Paso 9: comprobar el resultado
- Valida el HTML en el validador del W3C (validator.w3.org): no debe dar errores.
- Desactiva el CSS (o léelo sin estilos) y comprueba que el orden de lectura tiene sentido.
- Navega solo con el teclado (Tab): todos los enlaces y campos deben ser alcanzables, con el foco visible.
- Revisa los títulos: un único
<h1>y ningún nivel saltado. - Pega la URL en un comprobador de tarjetas de redes sociales para ver cómo se comparte. Para eso la web debe estar publicada: mira publicar tu web.
Ideas para ampliarlo
- Una página de categorías con una lista de entradas por tema.
- Paginación con
<nav aria-label="Paginación">y enlaces “Anterior” y “Siguiente”. - Un enlace “Saltar al contenido” al principio de la página.
- Un feed RSS enlazado con
<link rel="alternate" type="application/rss+xml">. - Estilos en dos columnas con Grid, dejando el
<aside>a un lado.
Errores frecuentes
- Usar
<div>para todo. Si algo es un artículo, una cita o una fecha, tiene su etiqueta. Un<div>no dice nada a buscadores ni a lectores de pantalla. - Dos
<h1>o ninguno. Debe haber uno por página, con el título de lo principal. - Elegir un título por su tamaño. El nivel indica la jerarquía; el tamaño es cosa del CSS.
- Poner la fecha solo como texto. Con
<time datetime="...">las máquinas la entienden. - Repetir el mismo
<title>ydescriptionen todas las páginas. Cada una necesita los suyos. - Imágenes sin
alto conalt="imagen". Describe lo que se ve y por qué importa. - Usar
<section>sin título. Una<section>debe tener un encabezado; si no, probablemente necesitas un<div>.
Resumen
| Parte del blog | Etiqueta | Detalle clave |
|---|---|---|
| Cabecera del sitio | <header> + <nav> | aria-label y aria-current="page" |
| Contenido principal | <main> | Uno por página |
| Entrada del blog | <article> | Título con <h1> o <h2> según la página |
| Fecha | <time datetime="..."> | Formato AAAA-MM-DD |
| Imagen con pie | <figure> + <figcaption> | alt, width y height |
| Cita | <blockquote cite> | Autor aparte |
| Nota relacionada | <aside> | Prescindible |
| Comentarios | <section> + <article> | Con aria-labelledby |
| Formulario | <form> + <label> | required, autocomplete |
| Redes y buscadores | <meta> y JSON-LD | description, og:, canonical |
En la siguiente lección practicarás con otro proyecto: un formulario de inscripción accesible. Y cuando quieras ponerle estilo a tu blog, pasa al curso de CSS.
Pruébalo tú
Cambia el código y pulsa Ejecutar (o Ctrl + Enter).
Pon a prueba lo que has aprendido
¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.