Ya tienes texto y enlaces en la ficha. La página funciona, pero aún parece un documento de texto: le faltan imágenes para poner cara a los héroes, listas para organizar la información en grupos, y tablas para mostrar datos que se relacionan entre sí. Esos tres elementos son el vocabulario que cierra el HTML básico.
Seguimos con la ficha de Tracer. Al terminar el capítulo tendrá imagen, lista de habilidades y una tabla de estadísticas del equipo: Tracer, Reinhardt y Mercy.
Imágenes: <img>, src y alt#
La imagen en HTML es un elemento vacío —sin contenido y sin cierre— que le dice al navegador dónde está el fichero y qué muestra:
<img src="tracer.png" alt="Tracer apuntando con su pistola de pulso" />
<!-- src="tracer.png" es la ruta al fichero de imagen
alt="..." describe lo que muestra la imagen para quien no puede verla -->src(source) es la ruta al fichero: puede ser relativa (tracer.png,img/heroes/tracer.webp) o una URL completa. Sinsrc, no hay imagen.altes el texto alternativo. Es el atributo más importante de<img>y, sin embargo, el que más se descuida.
El alt tiene dos modos, y elegir bien importa#
La regla de fondo es que el alt debe transmitir la misma información que transmitiría la
imagen si se pudiera ver. De ahí que haya dos situaciones distintas:
Alt informativo — cuando la imagen aporta algo que no está ya en el texto:
<img src="tracer.png" alt="Tracer apuntando con su pistola de pulso" />
<!-- alt informativo: describe la imagen porque esa información no está en otro sitio -->El alt describe lo que muestra la imagen. Si no carga (red lenta, ruta rota, imagen
bloqueada), aparece ese texto. Si el usuario tiene lector de pantalla, lo oye en voz alta.
Alt decorativo — cuando la información ya está en el texto cercano:
<!-- el nombre ya está aquí en texto -->
<h1>Tracer</h1>
<!-- la descripción también ya está -->
<p>Heroína de daño. Rápida y escurridiza.</p>
<!-- alt="" vacío: la imagen es decorativa, no repite información -->
<img src="tracer.png" alt="" />Aquí el <h1> y el <p> ya describen a Tracer: la imagen es decorativa. Con alt=""
vacío el lector de pantalla sabe que debe saltársela. Si pusiéramos el mismo texto del
encabezado, el usuario lo oiría dos veces. Y si omitiéramos el atributo alt por completo,
muchos lectores de pantalla leerían la ruta del fichero, que no informa de nada.
La pregunta que guía la decisión es: ¿quitando la imagen, pierde el usuario alguna
información que no esté en el texto? Si sí, alt informativo. Si no, alt="".
width y height: reservar el hueco#
<img src="tracer.png" alt="..." width="120" height="120" />Cuando el navegador no sabe el tamaño de la imagen antes de descargarla, dibuja la página
sin ese hueco. Cuando llega la imagen, la inserta y recoloca todo lo que había debajo: ese
salto brusco del contenido se llama layout shift y es molesto. Con width y height en
el HTML, el navegador reserva el espacio desde el principio aunque la imagen tarde.
Los formatos más comunes son jpg (fotos), png (transparencias), svg (iconos y
gráficos vectoriales) y webp (más compacto que jpg o png para fotos). En este capítulo
no entran los detalles de cada uno; lo que importa es que <img> funciona igual con todos.
Listas: <ul>, <ol> y <li>#
Una lista en HTML no es solo “varios elementos seguidos”: es una agrupación con un significado. Hay dos tipos, y la elección entre ellos es la primera decisión que hay que tomar:
<ul>(unordered list) — lista sin orden: los elementos forman un conjunto en el que cambiar el orden no cambia el significado.<ol>(ordered list) — lista con orden: la secuencia importa. Cambiar el orden cambia el resultado.
En ambas, cada elemento de la lista va en un <li> (list item):
<!-- ul: las habilidades de Tracer en cualquier orden son las mismas habilidades -->
<ul>
<li>Parpadeo</li>
<li>Retroceso</li>
<li>Bomba de pulso</li>
</ul>
<!-- ol: los pasos de la combo de Reinhardt en este orden concreto -->
<ol>
<li>Golpe de martillo</li>
<li>Embestida</li>
<li>Terremoto</li>
</ol>El navegador pinta <ul> con viñetas y <ol> con números, pero eso es el estilo por
defecto: el CSS puede cambiarlo. La distinción que importa es la del significado, no el
aspecto.
Una lista puede anidarse dentro de otro <li> para crear subniveles:
<!-- lista exterior: las categorías de héroe -->
<ul>
<!-- un elemento de la lista exterior... -->
<li>Héroes de daño
<!-- ...que contiene a su vez otra lista (anidada) -->
<ul>
<!-- elemento de la lista interior -->
<li>Tracer</li>
<!-- otro elemento de la lista interior -->
<li>Soldier: 76</li>
<!-- cierra la lista interior -->
</ul>
<!-- cierra el elemento "Héroes de daño" -->
</li>
<!-- otro elemento de la lista exterior con su sublista -->
<li>Héroes de apoyo
<!-- segunda lista anidada -->
<ul>
<!-- elemento de la sublista -->
<li>Mercy</li>
<!-- otro elemento de la sublista -->
<li>Ana</li>
<!-- cierra la segunda sublista -->
</ul>
<!-- cierra el elemento "Héroes de apoyo" -->
</li>
<!-- cierra la lista exterior -->
</ul>Listas de descripción: parejas de término y dato#
A veces lo que tienes no es una secuencia de elementos, sino parejas de un
término y su valor: las estadísticas de un héroe (Partidas: 120, Victorias: 78),
las características de un producto o los campos de una ficha. Para eso hay una
tercera lista, la lista de descripción <dl> (description list):
<!-- description list: contenedor de parejas término–valor -->
<dl>
<!-- dt = el término (la etiqueta) -->
<dt>Partidas</dt>
<!-- dd = el valor que corresponde al término de arriba -->
<dd>120</dd>
<!-- otro término -->
<dt>Victorias</dt>
<!-- su valor -->
<dd>78</dd>
<!-- tercer término -->
<dt>Winrate</dt>
<!-- su valor -->
<dd>65%</dd>
<!-- cierra la lista de descripción -->
</dl>Cada término va en un <dt> (description term) y su valor en el <dd>
(description details) que lo sigue. Un mismo <dt> puede tener varios <dd> si
un término tiene varios valores. Frente a escribir “Partidas: 120” en un párrafo
suelto, el <dl> deja explícito en el HTML qué es etiqueta y qué es dato: un
lector de pantalla los anuncia como pares, y tú puedes estilar el término y el
valor por separado.
Tablas: datos que se relacionan entre sí#
Una tabla sirve para presentar datos tabulares: información que tiene sentido precisamente porque cruza filas y columnas. La tabla de estadísticas del equipo —héroe, rol, partidas, victorias— es el ejemplo perfecto.
Lo que no es una tabla: un bloque de texto con columnas por aspecto. Las tablas se usaron durante años para maquetar páginas enteras (columnas, cabeceras, pie). Eso ya no se hace; hoy existe CSS para eso. Una tabla HTML es solo para datos.
Las piezas de una tabla#
<!-- contenedor de toda la tabla -->
<table>
<!-- título de la tabla, asociado a ella -->
<caption>Estadísticas del equipo — temporada actual</caption>
<!-- zona de cabeceras de columna -->
<thead>
<!-- una fila (table row) -->
<tr>
<!-- celda de cabecera de columna (scope="col") -->
<th scope="col">Héroe</th>
<!-- cabecera de columna -->
<th scope="col">Rol</th>
<!-- cabecera de columna -->
<th scope="col">Partidas</th>
<!-- cabecera de columna -->
<th scope="col">Victorias</th>
<!-- cabecera de columna -->
<th scope="col">Winrate</th>
<!-- cierra la fila de cabeceras -->
</tr>
<!-- cierra la cabecera -->
</thead>
<!-- zona del cuerpo: los datos -->
<tbody>
<!-- fila de datos de un héroe -->
<tr>
<!-- cabecera de fila (scope="row"): nombra la fila, no es un dato -->
<th scope="row">Tracer</th>
<!-- celda de dato (table data) -->
<td>Daño</td>
<!-- celda de dato -->
<td>120</td>
<!-- celda de dato -->
<td>78</td>
<!-- celda de dato -->
<td>65 %</td>
<!-- cierra la fila de Tracer -->
</tr>
<!-- otra fila de datos -->
<tr>
<!-- cabecera de fila -->
<th scope="row">Reinhardt</th>
<!-- celda de dato -->
<td>Tanque</td>
<!-- celda de dato -->
<td>90</td>
<!-- celda de dato -->
<td>51</td>
<!-- celda de dato -->
<td>57 %</td>
<!-- cierra la fila de Reinhardt -->
</tr>
<!-- otra fila de datos -->
<tr>
<!-- cabecera de fila -->
<th scope="row">Mercy</th>
<!-- celda de dato -->
<td>Apoyo</td>
<!-- celda de dato -->
<td>200</td>
<!-- celda de dato -->
<td>130</td>
<!-- celda de dato -->
<td>65 %</td>
<!-- cierra la fila de Mercy -->
</tr>
<!-- cierra el cuerpo -->
</tbody>
<!-- cierra la tabla -->
</table>Pieza a pieza:
<table>— el contenedor de todo.<caption>— el título de la tabla, asociado a ella por el navegador (no es solo texto encima). El lector de pantalla lo anuncia antes de entrar en la tabla.<thead>y<tbody>— separan las cabeceras del cuerpo de datos. No es obligatorio, pero hace el código más fácil de leer y permite que el navegador repita la cabecera si la tabla se imprime en varias páginas.<tr>(table row) — una fila.<th>(table header) — una celda de encabezado: nombra la columna o la fila. El atributoscopedice a qué aplica:scope="col"para cabeceras de columna,scope="row"para cabeceras de fila. Con él, el lector de pantalla asocia cada dato a su cabecera sin ambigüedad.<td>(table data) — una celda de dato.
Pruébalo#
La ficha de Tracer con los tres elementos. La imagen usa un placeholder porque en el
playground no hay ficheros de imagen. Edita el alt, cambia un número de la tabla o añade
un <li>, y mira el resultado.
Comprueba lo que sabes#
Pregunta 1 de 5
Una imagen tiene un pie de foto justo debajo que ya dice "Tracer, heroína de daño". ¿Qué alt le pones a la imagen?
Tu turno#
Amplía la ficha de Tracer con imagen, lista y tabla, aquí mismo en el playground. La imagen puede
ser un placeholder o un nombre de fichero de ejemplo: lo que importa es practicar los atributos
—alt, width, height— y decidir entre <ul> y <ol> con criterio. Edita el HTML en la
pestaña izquierda y comprueba el resultado en el preview. Cuando creas que lo tienes, despliega
las soluciones y compáralas con la tuya.
Ejercicio · en esta página
La ficha de Tracer: imagen, lista y tabla
Partiendo del esqueleto que ya conoces, amplía la ficha de Tracer añadiendo: (a) una imagen con el alt correcto, (b) una lista de sus habilidades eligiendo ul u ol según si el orden importa, y (c) una tabla de estadísticas del equipo con Tracer, Reinhardt y Mercy. Para la imagen usa la URL https://placehold.co/120x120 o el nombre "tracer.png" como ejemplo (el fichero no existe; lo que importa es practicar los atributos).
Paso 1: La información se ve
- La imagen aparece en la página (o el texto del alt si la URL no carga).
- Las habilidades están en la página, aunque sea como párrafos con guiones.
- Las estadísticas están en la página, aunque sea como texto suelto.
Paso 2: Elementos correctos
- <img> con un alt que describe la imagen.
- Lista real: <ul> con <li>, no <p> con guiones.
- Tabla con <thead>, <tbody>, <th> para cabeceras y <td> para datos.
Paso 3: Criterio aplicado
- alt="" vacío cuando el texto cercano ya da la información (alt decorativo).
- width y height en <img> para reservar el hueco antes de que cargue.
- <ul> o <ol> elegido con criterio: <ul> si el orden no importa, <ol> si sí importa.
- <th scope="col"> en cabeceras de columna, <th scope="row"> en cabeceras de fila.
- <caption> que describe de qué trata la tabla.
Ver soluciones
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8" />
<title>Tracer — Overwatch Team Builder</title>
</head>
<body>
<h1>Tracer</h1>
<p>Heroína de daño. Rápida y escurridiza: su trabajo es presionar la retaguardia rival.</p>
<img src="https://placehold.co/120x120" />
<h2>Habilidades</h2>
<p>- Parpadeo</p>
<p>- Retroceso</p>
<p>- Bomba de pulso</p>
<h2>Estadísticas del equipo</h2>
<p>Tracer: Daño, 120 partidas, 78 victorias</p>
<p>Reinhardt: Tanque, 90 partidas, 51 victorias</p>
<p>Mercy: Apoyo, 200 partidas, 130 victorias</p>
</body>
</html> Por qué este nivel
- La información está: se ve la imagen (o el placeholder), las habilidades y las estadísticas. Cumple el mínimo.
- La imagen no tiene alt. Si la URL no carga, el navegador puede mostrar la ruta del fichero; un lector de pantalla no sabe qué hay ahí.
- Las habilidades son párrafos con guiones: visualmente se parecen a una lista, pero el navegador no sabe que son una lista. No tienen estructura semántica.
- Las estadísticas son párrafos de texto: para el navegador son frases sueltas, no datos relacionados. No hay forma de navegar por columnas ni de saber que "120" es el número de partidas.
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8" />
<title>Tracer — Overwatch Team Builder</title>
</head>
<body>
<h1>Tracer</h1>
<p>Heroína de daño. Rápida y escurridiza: su trabajo es presionar la retaguardia rival.</p>
<img src="https://placehold.co/120x120" alt="Foto de Tracer, heroína de daño" />
<h2>Habilidades</h2>
<ul>
<li>Parpadeo</li>
<li>Retroceso</li>
<li>Bomba de pulso</li>
</ul>
<h2>Estadísticas del equipo</h2>
<table>
<thead>
<tr>
<th>Héroe</th>
<th>Rol</th>
<th>Partidas</th>
<th>Victorias</th>
</tr>
</thead>
<tbody>
<tr>
<td>Tracer</td>
<td>Daño</td>
<td>120</td>
<td>78</td>
</tr>
<tr>
<td>Reinhardt</td>
<td>Tanque</td>
<td>90</td>
<td>51</td>
</tr>
<tr>
<td>Mercy</td>
<td>Apoyo</td>
<td>200</td>
<td>130</td>
</tr>
</tbody>
</table>
</body>
</html> Por qué es mejor que el anterior
- La imagen tiene alt descriptivo: "Foto de Tracer, heroína de daño". Si no carga, ese texto aparece; si el usuario tiene lector de pantalla, lo oye.
- Las habilidades son una <ul> con <li>: el navegador sabe que son una lista y cuántos elementos tiene. Un lector de pantalla anuncia "lista de 3 elementos".
- La tabla tiene <thead> con <th> para los nombres de columna y <tbody> con <td> para los datos. Sin embargo, los <th> del <thead> no llevan scope="col": sin ese atributo, los lectores de pantalla no garantizan la asociación entre dato y cabecera. El nivel excelente lo añade.
- Mismo contenido que el nivel OK, pero ahora la estructura dice qué es cada cosa.
<!doctype html>
<html lang="es">
<head>
<!-- Para el navegador: codificación, título de pestaña. No se ve en la página. -->
<meta charset="utf-8" />
<title>Tracer, heroína de daño · Overwatch Team Builder</title>
</head>
<body>
<!-- A partir de aquí, lo que se ve en la ventana. -->
<h1>Tracer</h1>
<p>Heroína de daño. Rápida y escurridiza: su trabajo es presionar la retaguardia rival.</p>
<!--
alt decorativo: la información ya está en el <h1> ("Tracer") y en el <p>.
La imagen solo decora: con alt="" el lector de pantalla la salta sin leerla.
-->
<img
src="https://placehold.co/120x120"
alt=""
width="120"
height="120"
/>
<h2>Habilidades</h2>
<!--
ul, no ol: el orden de las habilidades no importa para entenderlas.
Si fuera la secuencia de una combo, usaríamos ol.
-->
<ul>
<li>Parpadeo</li>
<li>Retroceso</li>
<li>Bomba de pulso</li>
</ul>
<h2>Estadísticas del equipo</h2>
<table>
<caption>Estadísticas de partidas — temporada actual</caption>
<thead>
<tr>
<th scope="col">Héroe</th>
<th scope="col">Rol</th>
<th scope="col">Partidas</th>
<th scope="col">Victorias</th>
<th scope="col">Winrate</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Tracer</th>
<td>Daño</td>
<td>120</td>
<td>78</td>
<td>65 %</td>
</tr>
<tr>
<th scope="row">Reinhardt</th>
<td>Tanque</td>
<td>90</td>
<td>51</td>
<td>57 %</td>
</tr>
<tr>
<th scope="row">Mercy</th>
<td>Apoyo</td>
<td>200</td>
<td>130</td>
<td>65 %</td>
</tr>
</tbody>
</table>
</body>
</html> Por qué es mejor que el anterior
- El alt es vacío (alt=""). El <h1> ya dice "Tracer" y el <p> la describe: la imagen no añade información nueva, así que es decorativa. Con alt="" el lector de pantalla la salta; si repitiéramos el texto, el usuario lo oiría dos veces.
- width="120" height="120" en la imagen: el navegador reserva el hueco antes de que llegue la imagen y la página no salta cuando carga. Sin estos atributos, el contenido de abajo se recoloca al aparecer la imagen.
- <ul> para las habilidades porque el orden no cambia su significado. Si fuera la secuencia de una combo, usaríamos <ol>.
- <th scope="col"> en las cabeceras de columna y <th scope="row"> en la primera celda de cada fila: ahora el lector de pantalla sabe que "Tracer" nombra la fila, no es un dato.
- <caption> describe la tabla antes de que el usuario la explore: "Estadísticas de partidas — temporada actual" ya dice todo lo que necesita saber para decidir si le interesa.