Estándares Web
La web funciona porque todos los navegadores, servidores y dispositivos hablan el mismo idioma. Ese idioma lo definen los estándares web: especificaciones técnicas abiertas y públicas que garantizan que una página creada hoy se pueda ver correctamente en Chrome, Firefox, Safari o Edge, en un ordenador o en un móvil, ahora y dentro de diez años.
Organismos de Estandarización
| Organismo | Nombre completo | Tecnologías que gestiona |
|---|---|---|
| W3C | World Wide Web Consortium | HTML, CSS, XML, SVG, accesibilidad (WCAG) |
| WHATWG | Web Hypertext Application Technology Working Group | HTML Living Standard (el HTML5 moderno) |
| ECMA | European Computer Manufacturers Association | ECMAScript (JavaScript), JSON |
| IETF | Internet Engineering Task Force | HTTP, HTTPS, URL, protocolos de red |
Clasificación de Tecnologías Web
Estructura
HTML describe qué es cada parte del contenido: títulos, párrafos, listas, enlaces, imágenes. Es el esqueleto.
Presentación
CSS define el aspecto visual: colores, fuentes, tamaños, espaciado, animaciones. Es la piel.
Comportamiento
JavaScript aporta interactividad y lógica: eventos, validaciones, peticiones. Es el músculo.
HTML
HTML (HyperText Markup Language) es el lenguaje de marcas estándar para estructurar y dar significado al contenido de las páginas web. Todo lo que un navegador muestra está construido sobre HTML: textos, imágenes, formularios, vídeos, tablas y la propia estructura de la página.
Estructura Básica de un Documento
Todo documento HTML bien formado sigue una estructura fija compuesta por cuatro partes fundamentales. Veamos cada una:
1<!DOCTYPE html>
2<html lang="es">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Mi primera página</title>
7 <link rel="stylesheet" href="estilos.css">
8 </head>
9 <body>
10 <h1>Hola, mundo</h1>
11 <p>Este es mi primer documento HTML.</p>
12 </body>
13</html>DOCTYPE
La declaración <!DOCTYPE html> no es una etiqueta HTML, sino una instrucción al navegador indicando qué versión del estándar usar. En HTML5 se simplificó a esta forma mínima. Debe ser siempre la primera línea del documento.
Elemento raíz <html>
Envuelve todo el documento. El atributo lang es importante: indica el idioma principal del contenido, lo que mejora la accesibilidad (lectores de pantalla) y el SEO.
Cabecera <head>
Contiene metadatos del documento que el navegador necesita pero el usuario no ve directamente: la codificación de caracteres, el título de la pestaña, las hojas de estilo, las fuentes y los metadatos para buscadores y redes sociales.
Cuerpo <body>
Todo lo que va dentro de <body> es lo que el usuario ve en el navegador: textos, imágenes, enlaces, formularios y la estructura visual de la página.
Etiquetas Estructurales
HTML5 introdujo etiquetas semánticas para describir la función de cada sección de la página. Antes de HTML5, todo era un <div>. Ahora el navegador, los buscadores y los lectores de pantalla entienden qué es cada parte:
Vista previa — estructura semántica
Barra lateral, anuncios, info extra
| Etiqueta | Significado semántico | Uso típico |
|---|---|---|
| <header> | Encabezado de página o sección | Logo, título, menú principal |
| <nav> | Bloque de navegación | Menús, migas de pan, paginación |
| <main> | Contenido principal único de la página | Solo puede haber uno por página |
| <article> | Contenido autónomo y reutilizable | Entradas de blog, noticias, tarjetas |
| <section> | Sección temática con cabecera | Capítulos, bloques de contenido |
| <aside> | Contenido relacionado pero secundario | Barra lateral, publicidad, tips |
| <footer> | Pie de página o sección | Copyright, contacto, sitemap |
| <div> | Contenedor genérico sin semántica | Solo cuando ninguna etiqueta semántica encaja |
| <span> | Contenedor inline sin semántica | Para aplicar estilos a fragmentos de texto |
¿Por qué importa la semántica?
<article> en lugar de <div class="articulo"> no es solo estética: Google lee las etiquetas semánticas para entender tu página y posicionarla mejor. Los lectores de pantalla permiten a personas ciegas navegar directamente a la <nav> o al <main>. El código es más mantenible. Los tres motivos juntos hacen que la semántica sea obligatoriaen desarrollo profesional.Etiquetas de Texto
Las etiquetas de texto son las más usadas en HTML. No todas son iguales: algunas tienen significado semántico (describen qué es el contenido) y otras son puramente visuales (solo cambian la apariencia). En HTML moderno se prefieren siempre las semánticas.
Encabezados: <h1> — <h6>
Los encabezados crean la jerarquía de contenidos. Solo debe existir un <h1> por página (el título principal). Los motores de búsqueda los usan para indexar el contenido.
Vista previa — jerarquía de encabezados
h1 — Título principal de la página
h2 — Sección principal
h3 — Subsección
h4 — Apartado dentro de una subsección
h5 — Nivel de detalle avanzado
h6 — Nivel más bajo de jerarquía
Formato y semántica de texto
Vista previa — etiquetas de formato
Este es un párrafo normal. Es el bloque de texto más básico.
console.log("hola") — fragmento de código inline"La web es para todos." — Tim Berners-Lee
function hola() {
return "Mantiene los espacios y saltos";
}Semántica vs. presentación en texto
<strong> sobre <b> y <em> sobre <i> cuando el énfasis tenga significado real. Un lector de pantalla pronunciará <strong> con énfasis; <b> lo ignorará.Enlaces e Imágenes
Los enlaces con <a> son el tejido de la web: conectan páginas entre sí. Las imágenes con <img> son elementos vacíos (sin etiqueta de cierre).
Vista previa — enlaces
1<!-- Enlace externo en nueva pestaña -->
2<a href="https://www.w3.org" target="_blank" rel="noopener noreferrer">
3 W3C — Consorcio Web
4</a>
5
6<!-- Enlace a sección de la misma página -->
7<a href="#contacto">Ir a contacto</a>
8<section id="contacto">...</section>
9
10<!-- Imagen con atributos obligatorios -->
11<img
12 src="foto-perfil.jpg"
13 alt="Foto de perfil de Ana García, desarrolladora web"
14 width="300"
15 height="200"
16 loading="lazy"
17/>
18
19<!-- Imagen con enlace -->
20<a href="https://ejemplo.com">
21 <img src="logo.png" alt="Logo de Ejemplo Corp." width="120">
22</a>
23
24<!-- Figura con pie de foto (semántico) -->
25<figure>
26 <img src="grafico-ventas.png" alt="Gráfico de ventas del primer trimestre">
27 <figcaption>Fig. 1 — Evolución de ventas Q1 2024</figcaption>
28</figure>El atributo alt es obligatorio y crítico
alt de <img> no es opcional en la práctica: si la imagen no carga, el usuario ve el texto alternativo. Los motores de búsqueda indexan el contenido del alt para el SEO de imágenes. Los lectores de pantalla lo leen en voz alta para usuarios con discapacidad visual. Si una imagen es puramente decorativa, usa alt="" (vacío).Listas y Tablas
HTML ofrece tres tipos de listas y un sistema completo para representar datos tabulares. Las tablas deben usarse solo para datos, nunca para maquetar la página (para eso está CSS Flexbox/Grid).
Vista previa — los tres tipos de lista
<ul> — Sin orden
- HTML
- CSS
- JavaScript
Uso: ingredientes, características, opciones
<ol> — Ordenada
- Diseñar
- Programar
- Probar
Uso: pasos, rankings, instrucciones
<dl> — Definición
- HTML
- Estructura web
- CSS
- Estilos visuales
Uso: glosarios, términos técnicos
Vista previa — tabla HTML semántica
| <th> Lenguaje | <th> Tipo | <th> Año |
|---|---|---|
| HTML | Marcas | 1991 |
| CSS | Estilos | 1996 |
| JavaScript | Programación | 1995 |
| <tfoot> — Pie de tabla: totales, notas al pie | ||
Estructura: <table> > <thead> + <tbody> + <tfoot> > <tr> > <th> / <td>
1<table>
2 <caption>Comparativa de tecnologías web</caption>
3
4 <!-- Encabezado de la tabla -->
5 <thead>
6 <tr>
7 <th scope="col">Lenguaje</th>
8 <th scope="col">Tipo</th>
9 <th scope="col">Año</th>
10 </tr>
11 </thead>
12
13 <!-- Cuerpo de datos -->
14 <tbody>
15 <tr>
16 <td>HTML</td>
17 <td>Marcas</td>
18 <td>1991</td>
19 </tr>
20 <tr>
21 <!-- colspan: fusionar columnas horizontalmente -->
22 <td colspan="2">CSS + JavaScript</td>
23 <td>1995–96</td>
24 </tr>
25 </tbody>
26
27 <!-- Pie de la tabla -->
28 <tfoot>
29 <tr>
30 <td colspan="3">Fuente: MDN Web Docs</td>
31 </tr>
32 </tfoot>
33</table>Formularios
Los formularios permiten al usuario enviar información al servidor. La etiqueta <form> agrupa los controles y define cómo y dónde se envían los datos. Cada control debe tener un <label> asociado para accesibilidad.
Vista previa — controles de formulario
1<form action="/registrar" method="POST" novalidate>
2 <fieldset>
3 <legend>Datos personales</legend>
4
5 <!-- label for + input id: vinculan el texto con el control -->
6 <label for="nombre">Nombre completo *</label>
7 <input
8 type="text"
9 id="nombre"
10 name="nombre"
11 placeholder="Ej: Ana García"
12 required
13 minlength="3"
14 maxlength="100"
15 autocomplete="name"
16 />
17
18 <label for="email">Correo electrónico *</label>
19 <input type="email" id="email" name="email" required />
20
21 <label for="nivel">Nivel de experiencia</label>
22 <select id="nivel" name="nivel">
23 <option value="">-- Selecciona --</option>
24 <option value="junior">Junior</option>
25 <option value="mid">Mid</option>
26 <option value="senior">Senior</option>
27 </select>
28
29 <label for="bio">Presentación</label>
30 <textarea id="bio" name="bio" rows="4" maxlength="500"></textarea>
31
32 <!-- Grupo de checkboxes relacionados -->
33 <fieldset>
34 <legend>Tecnologías que conoces</legend>
35 <label><input type="checkbox" name="tec" value="html"> HTML</label>
36 <label><input type="checkbox" name="tec" value="css"> CSS</label>
37 <label><input type="checkbox" name="tec" value="js"> JavaScript</label>
38 </fieldset>
39 </fieldset>
40
41 <button type="submit">Registrarse</button>
42 <button type="reset">Limpiar</button>
43</form>Atributos clave de los controles de formulario
required— el campo es obligatorio (validación nativa del navegador)placeholder— texto de ejemplo dentro del campo (desaparece al escribir)name— identifica el dato al enviarlo al servidor (indispensable)id— vincula el control con su<label for="...">autocomplete— ayuda al navegador a autocompletar correctamentedisabled/readonly— deshabilita o bloquea el campo
Atributos Globales
Los atributos globales son los que puede recibir cualquier etiqueta HTML, independientemente de su tipo. Son la base sobre la que trabajan CSS y JavaScript.
| Atributo | Descripción | Ejemplo |
|---|---|---|
| id | Identificador único en el documento | <div id="menu-principal"> |
| class | Una o varias clases para CSS/JS | <p class="destacado grande"> |
| style | Estilos CSS inline (evitar en prod.) | <h1 style="color: red"> |
| title | Tooltip al pasar el ratón | <abbr title="HyperText Markup">HTML</abbr> |
| lang | Idioma del elemento | <p lang="en">Hello world</p> |
| hidden | Oculta el elemento del documento | <div hidden>No visible</div> |
| tabindex | Orden de tabulación con el teclado | <button tabindex="1"> |
| contenteditable | Permite editar el contenido | <div contenteditable="true"> |
| draggable | Permite arrastrar el elemento | <img draggable="true"> |
| data-* | Atributos personalizados para JS | <div data-id="42" data-tipo="admin"> |
| aria-* | Accesibilidad para lectores de pantalla | <button aria-label="Cerrar"> |
Multimedia en HTML
Antes de HTML5 incrustar vídeo o audio requería plugins externos como Flash o Silverlight. HTML5 incorporó soporte nativo para multimedia directamente en el navegador, sin dependencias externas y con una API JavaScript completa para controlarlo.
El elemento <video>
<video> incrusta reproductores de vídeo nativos. El navegador mostrará los controles propios del sistema operativo o los personalizados con CSS y JS. Se pueden incluir varias fuentes con <source> para compatibilidad entre navegadores, ya que no todos soportan los mismos formatos.
Atributos del elemento <video>
| Atributo | Valor | Efecto |
|---|---|---|
| controls | booleano | Muestra los controles nativos (play, volumen, progreso) |
| autoplay | booleano | Reproduce automáticamente al cargar (requiere muted) |
| muted | booleano | Silencia el vídeo (necesario para autoplay) |
| loop | booleano | Repite el vídeo indefinidamente |
| poster | URL | Imagen de portada antes de reproducir |
| preload | none / metadata / auto | Controla cuánto se descarga antes de reproducir |
| width / height | píxeles | Dimensiones del reproductor |
| playsinline | booleano | En iOS: reproduce dentro de la página, no a pantalla completa |
1<!-- Vídeo básico con controles -->
2<video controls width="640" height="360" poster="portada.jpg">
3 <!-- source: múltiples formatos para compatibilidad -->
4 <source src="video.mp4" type="video/mp4">
5 <source src="video.webm" type="video/webm">
6 <source src="video.ogv" type="video/ogg">
7
8 <!-- Fallback si el navegador no soporta <video> -->
9 <p>Tu navegador no soporta vídeo HTML5.
10 <a href="video.mp4">Descarga el vídeo aquí</a>.
11 </p>
12</video>
13
14<!-- Vídeo de fondo (autoreproducido, silenciado, en bucle) -->
15<video autoplay muted loop playsinline
16 class="video-fondo"
17 aria-hidden="true">
18 <source src="fondo.mp4" type="video/mp4">
19</video>
20
21<!-- Formato recomendado por compatibilidad -->
22<!-- MP4 (H.264): máxima compatibilidad (Chrome, Firefox, Safari, Edge) -->
23<!-- WebM (VP9): mejor compresión, todos excepto Safari antiguo -->
24<!-- OGV: solo necesario para Firefox muy antiguo (ya innecesario) -->El elemento <audio>
<audio> funciona igual que<video> pero para sonido. Sin el atributo controls el reproductor es invisible (útil para efectos de sonido controlados por JavaScript).
1<!-- Reproductor de audio con controles visibles -->
2<audio controls>
3 <source src="musica.mp3" type="audio/mpeg">
4 <source src="musica.ogg" type="audio/ogg">
5 <source src="musica.wav" type="audio/wav">
6 <p>Tu navegador no soporta audio HTML5.</p>
7</audio>
8
9<!-- Audio de fondo sin controles (gestionado por JS) -->
10<audio id="sonido-notificacion" preload="auto">
11 <source src="notif.mp3" type="audio/mpeg">
12</audio>
13
14<script>
15 // Reproducir con JavaScript
16 document.getElementById('sonido-notificacion').play();
17</script>
18
19<!-- Formatos de audio:
20 MP3 → Máxima compatibilidad
21 OGG → Open source, buena compresión
22 WAV → Sin compresión, alta calidad, archivos grandes
23 AAC → Mejor calidad que MP3 al mismo bitrate (Safari lo prefiere) -->Vista previa — reproductor de audio nativo
Así se ve un <audio controls> real en el navegador
El elemento <iframe>
<iframe> (inline frame) incrusta un documento web completo dentro de otro. Se usa habitualmente para incrustar mapas de Google, vídeos de YouTube, tweets, widgets de pago o cualquier contenido externo.
1<!-- Incrustar un vídeo de YouTube -->
2<iframe
3 width="560"
4 height="315"
5 src="https://www.youtube.com/embed/dQw4w9WgXcQ"
6 title="Vídeo de YouTube: nombre descriptivo"
7 frameborder="0"
8 allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
9 allowfullscreen
10 loading="lazy">
11</iframe>
12
13<!-- Incrustar un mapa de Google Maps -->
14<iframe
15 src="https://www.google.com/maps/embed?pb=..."
16 width="600"
17 height="450"
18 style="border:0"
19 allowfullscreen=""
20 loading="lazy"
21 referrerpolicy="no-referrer-when-downgrade"
22 title="Ubicación de la oficina en Madrid">
23</iframe>
24
25<!-- SEGURIDAD: atributo sandbox -->
26<!-- Restringe lo que el iframe puede hacer -->
27<iframe
28 src="https://externo.com/widget"
29 sandbox="allow-scripts allow-same-origin"
30 title="Widget externo seguro">
31 <!-- Sin sandbox: el iframe tiene pleno acceso -->
32 <!-- allow-scripts: puede ejecutar JS -->
33 <!-- allow-forms: puede enviar formularios -->
34 <!-- allow-popups: puede abrir ventanas -->
35 <!-- allow-same-origin: trata la URL como su mismo origen -->
36</iframe>Seguridad con iframes externos
sandbox. Un iframe malicioso puede ejecutar JavaScript en el contexto de tu página, redirigir al usuario o intentar robar información. Siempre añade el atributo title para accesibilidad.Imágenes Responsivas: <picture> y srcset
Para que las imágenes sean eficientes en pantallas de distintos tamaños y resoluciones, HTML5 ofrece srcset y el elemento<picture>. El navegador selecciona automáticamente la imagen más adecuada según el dispositivo.
1<!-- srcset: el navegador elige la imagen según el pixel density -->
2<img
3 src="foto-800.jpg" <!-- fallback para navegadores sin srcset -->
4 srcset="
5 foto-400.jpg 400w, <!-- imagen de 400px de ancho -->
6 foto-800.jpg 800w, <!-- imagen de 800px de ancho -->
7 foto-1200.jpg 1200w, <!-- imagen de 1200px de ancho -->
8 foto-2400.jpg 2400w <!-- imagen para pantallas Retina -->
9 "
10 sizes="
11 (max-width: 600px) 100vw, <!-- en móvil ocupa todo el ancho -->
12 (max-width: 900px) 50vw, <!-- en tablet ocupa la mitad -->
13 33vw <!-- en escritorio ocupa un tercio -->
14 "
15 alt="Fotografía del producto desde el frente"
16 loading="lazy"
17/>
18
19<!-- <picture>: control total sobre qué imagen se carga -->
20<picture>
21 <!-- Formato WebP para navegadores modernos (mejor compresión) -->
22 <source
23 srcset="foto.webp"
24 type="image/webp">
25
26 <!-- AVIF para navegadores muy modernos (aún mejor compresión) -->
27 <source
28 srcset="foto.avif"
29 type="image/avif"
30 media="(min-width: 800px)">
31
32 <!-- Fallback JPEG universal -->
33 <img src="foto.jpg" alt="Descripción de la imagen" width="800" height="600">
34</picture>Elementos HTML5 Avanzados
HTML5 introdujo una serie de elementos de interfaz nativos que antes requerían JavaScript o librerías externas. Son accesibles, semánticos y funcionan sin código adicional en la mayoría de casos.
Acordeón nativo: <details> y <summary>
El elemento <details> crea un widget desplegable sin necesidad de JavaScript. <summary> es el título visible que el usuario puede clicar.
Vista previa — <details> interactivo
¿Qué es HTML?▾
¿Cuál es la diferencia entre HTML y CSS?▾
¿Qué aporta JavaScript? (abierto por defecto con open)▴
Haz clic en los títulos — funciona sin JavaScript
1<!-- FAQ / acordeón completamente sin JavaScript -->
2<details>
3 <summary>¿Qué es HTML?</summary>
4 <p>HTML es el lenguaje de marcas estándar para estructurar páginas web.</p>
5</details>
6
7<!-- Abierto por defecto con el atributo open -->
8<details open>
9 <summary>Sección expandida al cargar</summary>
10 <p>Este contenido es visible desde el principio.</p>
11</details>
12
13<!-- Agrupados forman un acordeón accesible sin JS -->
14<details name="faq">
15 <summary>Pregunta 1</summary>
16 <p>Respuesta 1...</p>
17</details>
18<details name="faq">
19 <summary>Pregunta 2</summary>
20 <p>Respuesta 2...</p>
21</details>
22<!-- Con el mismo name, solo uno puede estar abierto a la vez (HTML 2024) -->Modal nativo: <dialog>
El elemento <dialog> es un modal o diálogo nativo del navegador. Gestiona automáticamente el foco, la capa backdrop y la accesibilidad.
1<!-- El diálogo empieza oculto por defecto -->
2<dialog id="mi-modal">
3 <h2>Confirmar acción</h2>
4 <p>¿Estás seguro de que quieres eliminar este elemento?</p>
5 <form method="dialog"> <!-- method="dialog" cierra el diálogo -->
6 <button value="cancelar">Cancelar</button>
7 <button value="confirmar">Sí, eliminar</button>
8 </form>
9</dialog>
10
11<button onclick="document.getElementById('mi-modal').showModal()">
12 Abrir modal
13</button>
14
15<script>
16 const modal = document.getElementById('mi-modal');
17
18 // showModal(): modal bloqueante con backdrop oscuro
19 // show(): no bloqueante, sin backdrop
20
21 modal.addEventListener('close', () => {
22 console.log('El usuario eligió:', modal.returnValue);
23 // returnValue = el 'value' del botón que cerró el modal
24 });
25</script>
26
27<!-- CSS para el backdrop del dialog -->
28<style>
29 dialog::backdrop {
30 background: rgba(0, 0, 0, 0.5);
31 backdrop-filter: blur(4px);
32 }
33 dialog {
34 border-radius: 12px;
35 border: none;
36 box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
37 padding: 2rem;
38 }
39</style>Indicadores de progreso: <progress> y <meter>
Vista previa — <progress> y <meter>
<progress value="65" max="100">— tarea en progreso (carga, descarga, proceso)
<progress> sin value — progreso indeterminado (animación de espera)
<meter value="0.7" optimum="0.8" high="0.9">— medida con rangos óptimos (uso de disco, nota, puntuación)
1<!-- progress: tarea con progreso conocido -->
2<label for="descarga">Descargando archivo:</label>
3<progress id="descarga" value="65" max="100">65%</progress>
4<!-- El texto dentro es el fallback para navegadores sin soporte -->
5
6<!-- progress indeterminado (sin value): animación de espera -->
7<progress>Cargando...</progress>
8
9<!-- meter: medición dentro de un rango con zonas óptimas -->
10<label for="almacenamiento">Uso del disco duro:</label>
11<meter
12 id="almacenamiento"
13 value="85" <!-- valor actual -->
14 min="0" <!-- mínimo posible -->
15 max="100" <!-- máximo posible -->
16 low="60" <!-- a partir de aquí empieza la zona "baja" -->
17 high="80" <!-- a partir de aquí empieza la zona "alta" -->
18 optimum="40" <!-- valor óptimo (determina qué zona es "buena") -->
19>85%</meter>
20<!-- Con optimum=40 (zona baja): bajo=verde, medio=amarillo, alto=rojo -->Autocompletado nativo: <datalist>
<datalist> conecta un campo de texto con una lista de sugerencias. A diferencia de <select>, el usuario puede escribir libremente; las opciones son sugerencias, no restricciones.
Vista previa — <datalist> con sugerencias
Escribe "Ma" para ver las sugerencias filtradas
1<!-- input + datalist: el atributo list conecta ambos -->
2<label for="ciudad">Ciudad:</label>
3<input
4 type="text"
5 id="ciudad"
6 list="ciudades" <!-- debe coincidir con el id del datalist -->
7 placeholder="Escribe o selecciona..."
8 autocomplete="off"
9/>
10
11<datalist id="ciudades">
12 <option value="Madrid">
13 <option value="Barcelona">
14 <option value="Valencia">
15 <option value="Sevilla">
16 <option value="Bilbao">
17</datalist>
18
19<!-- También funciona con inputs de tipo number, color, date... -->
20<input type="range" min="0" max="100" list="marcas-rango">
21<datalist id="marcas-rango">
22 <option value="0">Mínimo</option>
23 <option value="25">Bajo</option>
24 <option value="50">Medio</option>
25 <option value="75">Alto</option>
26 <option value="100">Máximo</option>
27</datalist>Resultado de cálculo: <output>
1<!-- output muestra el resultado de un cálculo de formulario -->
2<form oninput="resultado.value = Number(precio.value) * Number(cantidad.value)">
3 <label for="precio">Precio por unidad (€):</label>
4 <input type="number" id="precio" name="precio" value="10" min="0">
5
6 <label for="cantidad">Cantidad:</label>
7 <input type="number" id="cantidad" name="cantidad" value="1" min="1">
8
9 <label for="resultado">Total:</label>
10 <output id="resultado" name="resultado" for="precio cantidad">
11 10
12 </output> €
13</form>
14<!-- El atributo 'for' vincula el output con los inputs que usa -->Gráficos: <canvas> y SVG inline
Para gráficos y visualizaciones, HTML5 ofrece dos enfoques radicalmente distintos:
<canvas> — Mapa de bits
Lienzo en blanco controlado 100% por JavaScript. Dibuja píxel a píxel. Ideal para juegos, gráficas con Chart.js, efectos de partículas. No es accesible por defecto.
SVG inline — Vectorial
XML incrustado directamente en HTML. Vectorial, escalable sin pérdida, seleccionable, animable con CSS. Ideal para iconos, logos, gráficas estáticas y diagramas.
1<!-- Canvas: necesita JS para dibujar -->
2<canvas id="mi-canvas" width="400" height="200">
3 <!-- Fallback accesible -->
4 Gráfica de barras mostrando ventas del Q1 2024
5</canvas>
6<script>
7 const ctx = document.getElementById('mi-canvas').getContext('2d');
8 // Dibuja un rectángulo con gradiente
9 const grad = ctx.createLinearGradient(0, 0, 400, 0);
10 grad.addColorStop(0, '#3b82f6');
11 grad.addColorStop(1, '#8b5cf6');
12 ctx.fillStyle = grad;
13 ctx.fillRect(0, 0, 400, 200);
14 ctx.fillStyle = 'white';
15 ctx.font = 'bold 24px system-ui';
16 ctx.fillText('¡Hola Canvas!', 120, 110);
17</script>
18
19<!-- SVG inline: XML directamente en el HTML -->
20<svg
21 xmlns="http://www.w3.org/2000/svg"
22 width="200" height="100"
23 viewBox="0 0 200 100"
24 role="img"
25 aria-label="Círculo azul con texto">
26
27 <circle cx="100" cy="50" r="45" fill="#3b82f6" />
28 <text x="100" y="56" text-anchor="middle"
29 fill="white" font-size="16" font-weight="bold">
30 SVG
31 </text>
32</svg>La Cabecera HTML Avanzada
La etiqueta <head> es invisible para el usuario pero fundamental para los buscadores, las redes sociales, los navegadores y el rendimiento. Un <head> bien configurado es la diferencia entre una página amateur y una profesional.
Meta tags esenciales
1<head>
2 <!-- ── Obligatorios ── -->
3 <meta charset="UTF-8">
4 <meta name="viewport" content="width=device-width, initial-scale=1.0">
5 <title>Título de la página | Nombre del sitio</title>
6
7 <!-- ── SEO básico ── -->
8 <meta name="description" content="Descripción concisa de 150-160 caracteres para los buscadores">
9 <meta name="keywords" content="html, css, web, desarrollo"> <!-- obsoleto en Google -->
10 <meta name="author" content="Ana García">
11 <meta name="robots" content="index, follow"> <!-- o "noindex, nofollow" -->
12 <link rel="canonical" href="https://misitio.com/pagina-actual">
13
14 <!-- ── Open Graph (Facebook, LinkedIn, WhatsApp) ── -->
15 <meta property="og:type" content="website">
16 <meta property="og:title" content="Título para compartir en redes">
17 <meta property="og:description" content="Descripción para redes sociales (150 chars)">
18 <meta property="og:image" content="https://misitio.com/imagen-og.jpg">
19 <meta property="og:url" content="https://misitio.com/pagina-actual">
20 <meta property="og:locale" content="es_ES">
21
22 <!-- ── Twitter Cards ── -->
23 <meta name="twitter:card" content="summary_large_image">
24 <meta name="twitter:title" content="Título para Twitter">
25 <meta name="twitter:description" content="Descripción para Twitter">
26 <meta name="twitter:image" content="https://misitio.com/imagen-twitter.jpg">
27 <meta name="twitter:creator" content="@tusername">
28
29 <!-- ── Favicon y iconos de app ── -->
30 <link rel="icon" type="image/svg+xml" href="/favicon.svg">
31 <link rel="icon" type="image/png" href="/favicon-32.png" sizes="32x32">
32 <link rel="apple-touch-icon" href="/apple-icon-180.png">
33 <link rel="manifest" href="/site.webmanifest">
34 <meta name="theme-color" content="#3b82f6">
35
36 <!-- ── Rendimiento ── -->
37 <!-- Precargar recursos críticos -->
38 <link rel="preload" href="fuente-principal.woff2" as="font" type="font/woff2" crossorigin>
39 <link rel="preload" href="hero.jpg" as="image">
40
41 <!-- Preconectar a dominios externos antes de usarlos -->
42 <link rel="preconnect" href="https://fonts.googleapis.com">
43 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
44
45 <!-- ── Hoja de estilos ── -->
46 <link rel="stylesheet" href="estilos.css">
47</head>Vista previa — así se ve una tarjeta Open Graph en redes
misitio.com
Título para compartir en redes
Descripción concisa que aparece al compartir el enlace en Facebook, LinkedIn o WhatsApp.
Esta tarjeta se genera a partir de los meta tags og:*
¿Por qué importan los meta tags de Open Graph?
og:* para mostrar una tarjeta previa. Sin ellos, se muestra solo la URL en texto plano, con mucho menos impacto. Las tarjetas con imagen generan hasta un 3 veces más clics según estudios de marketing digital.Accesibilidad Web (a11y)
La accesibilidad web consiste en que todas las personas, independientemente de sus capacidades, puedan usar la web. Esto incluye personas con discapacidad visual, auditiva, motriz o cognitiva. En muchos países es un requisito legal(directiva europea 2016/2102, normativa WCAG 2.1).
✗ Inaccesible
✗ Un usuario de teclado no puede activarlo
✗ Sin alt: un lector de pantalla dirá "imagen"
✗ "Haz clic aquí" no describe el destino
✓ Accesible
✓ El botón es activable con Enter/Espacio
✓ El lector dice "Logo de TechShop"
✓ El destino queda claro sin contexto
Atributos ARIA
ARIA (Accessible Rich Internet Applications) es un conjunto de atributos que complementan el HTML con información semántica adicional para lectores de pantalla. La regla de oro: usa HTML semántico primero; ARIA solo cuando el HTML solo no sea suficiente.
1<!-- aria-label: nombre descriptivo cuando el texto visible no basta -->
2<button aria-label="Cerrar menú de navegación" onclick="cerrarMenu()">
3 ✕
4</button>
5
6<!-- aria-labelledby: usa el texto de otro elemento como nombre -->
7<h2 id="titulo-form">Datos de contacto</h2>
8<form aria-labelledby="titulo-form">...</form>
9
10<!-- aria-describedby: descripción adicional -->
11<input
12 type="password"
13 aria-describedby="ayuda-password"
14 id="password">
15<p id="ayuda-password">
16 Mínimo 8 caracteres, una mayúscula y un número.
17</p>
18
19<!-- aria-hidden: ocultar elementos decorativos del árbol de accesibilidad -->
20<span aria-hidden="true">🎉</span>
21<p>¡Enhorabuena!</p>
22
23<!-- aria-live: anunciar cambios dinámicos en voz alta -->
24<div aria-live="polite" id="estado-formulario">
25 <!-- El lector de pantalla leerá automáticamente lo que cambie aquí -->
26</div>
27
28<!-- aria-expanded: indica si un menú está abierto o cerrado -->
29<button
30 aria-expanded="false"
31 aria-controls="menu-desplegable"
32 onclick="toggleMenu(this)">
33 Menú
34</button>
35<ul id="menu-desplegable" hidden>
36 <li><a href="/">Inicio</a></li>
37</ul>
38
39<!-- role: asigna un rol semántico explícito -->
40<div role="alert">Error: el email no es válido.</div>
41<div role="navigation" aria-label="Menú de pie de página">...</div>
42<div role="main">...</div> <!-- equivale a <main> -->Navegación por teclado y foco
Orden de tabulación con tabindex
Usa Tab para navegar — el botón con tabindex="-1" se salta
1<!-- Orden de tabulación con tabindex -->
2<button tabindex="1">Primero</button>
3<button tabindex="2">Segundo</button>
4<button tabindex="-1">Excluido del tabulado</button>
5<button tabindex="0">Orden natural del DOM</button>
6
7<!-- Skip link: permite a usuarios de teclado saltar al contenido principal -->
8<!-- Visualmente oculto pero accesible con Tab -->
9<a href="#main-content" class="skip-link">
10 Saltar al contenido principal
11</a>
12<style>
13 .skip-link {
14 position: absolute;
15 top: -40px;
16 left: 0;
17 background: #000;
18 color: #fff;
19 padding: 8px;
20 z-index: 100;
21 }
22 .skip-link:focus {
23 top: 0; /* Aparece visible cuando recibe foco */
24 }
25</style>
26
27<!-- Indicador de foco visible (obligatorio WCAG 2.1) -->
28<style>
29 /* NUNCA elimines el outline de foco sin reemplazarlo -->
30 :focus {
31 outline: 3px solid #3b82f6;
32 outline-offset: 2px;
33 }
34 /* Esta regla solo aplica al teclado, no al ratón */
35 :focus:not(:focus-visible) {
36 outline: none;
37 }
38</style>Las 4 pautas WCAG (Web Content Accessibility Guidelines)
Atributos data-* y JavaScript
Los atributos data-* permiten almacenar información personalizada directamente en el HTML. JavaScript puede leerlos y modificarlos a través de la propiedad dataset. Son la forma correcta de pasar datos del servidor al cliente sin inventarse atributos.
1<!-- Tarjeta de producto con datos para JavaScript -->
2<article
3 class="tarjeta-producto"
4 data-id="42"
5 data-nombre="Monitor UltraWide"
6 data-precio="449.99"
7 data-categoria="electronica"
8 data-disponible="true">
9
10 <h2>Monitor UltraWide Pro</h2>
11 <p>449,99 €</p>
12 <button class="btn-carrito" data-producto-id="42">
13 Añadir al carrito
14 </button>
15</article>
16
17<script>
18 // Leer data attributes con dataset
19 const tarjeta = document.querySelector('.tarjeta-producto');
20
21 console.log(tarjeta.dataset.id); // "42"
22 console.log(tarjeta.dataset.nombre); // "Monitor UltraWide"
23 console.log(tarjeta.dataset.precio); // "449.99"
24
25 // data-producto-id → dataset.productoId (camelCase automático)
26 const btn = document.querySelector('.btn-carrito');
27 console.log(btn.dataset.productoId); // "42"
28
29 // Modificar
30 tarjeta.dataset.disponible = "false";
31
32 // Usar como selector CSS también
33 // document.querySelectorAll('[data-categoria="electronica"]')
34
35 // Ejemplo real: botón que añade al carrito
36 btn.addEventListener('click', function() {
37 const id = this.dataset.productoId;
38 const nombre = this.closest('[data-nombre]').dataset.nombre;
39 const precio = parseFloat(this.closest('[data-precio]').dataset.precio);
40 agregarAlCarrito({ id, nombre, precio });
41 });
42</script>
43
44<!-- También se pueden usar en CSS -->
45<style>
46 [data-disponible="false"] {
47 opacity: 0.5;
48 pointer-events: none;
49 }
50 [data-categoria="electronica"]::before {
51 content: "💻 ";
52 }
53</style>Versiones de HTML
| Versión | Año | Características clave | Estado |
|---|---|---|---|
| HTML 4.01 | 1999 | Primera versión madura. Frames (obsoleto), tablas para layout | Obsoleto |
| XHTML 1.0 | 2000 | HTML reescrito como XML. Sintaxis estricta: todo en minúsculas, cierre obligatorio | Legacy |
| HTML5 | 2014 | Etiquetas semánticas, canvas, video, audio, APIs web, formularios enriquecidos | Estándar actual |
| HTML Living Standard | 2019–hoy | Evolución continua sin versiones fijas. Mantenido por WHATWG | Vigente |
XHTML — Reglas estrictas
1<!-- XHTML: sintaxis estricta -->
2<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
4<html xmlns="http://www.w3.org/1999/xhtml">
5<head>
6 <title>XHTML</title>
7</head>
8<body>
9 <!-- Todo en minúsculas, todo cerrado -->
10 <p>Párrafo <strong>importante</strong></p>
11 <img src="foto.jpg" alt="Foto" />
12 <br />
13</body>
14</html>HTML5 — Simplificado y potente
1<!-- HTML5: DOCTYPE mínimo -->
2<!DOCTYPE html>
3<html lang="es">
4<head>
5 <meta charset="UTF-8">
6 <title>HTML5</title>
7</head>
8<body>
9 <!-- Etiquetas semánticas nuevas -->
10 <header><nav>...</nav></header>
11 <main>
12 <article>...</article>
13 <aside>...</aside>
14 </main>
15 <!-- Audio y vídeo nativo -->
16 <video src="clip.mp4" controls></video>
17 <footer>...</footer>
18</body>
19</html>¿Qué DOCTYPE usar hoy?
<!DOCTYPE html>. Es el DOCTYPE de HTML5 y del HTML Living Standard. Es la forma más corta, compatble con todos los navegadores modernos y es lo que cualquier validador o guía de estilo profesional recomienda sin excepción.Hojas de Estilo CSS
CSS (Cascading Style Sheets) es el lenguaje que controla el aspecto visual de los documentos HTML. Mientras HTML dice qué es cada elemento, CSS dice cómo se ve: colores, tipografía, tamaños, márgenes, posición, animaciones y mucho más.
✗ Sin CSS
✓ Con CSS
Título de la página
Un párrafo con tipografía, color y espaciado cuidados.
Un enlace con estilo personalizadoSintaxis Básica
Una regla CSS se compone de un selector y un bloque de declaraciones. Cada declaración tiene una propiedad y un valor, separados por dos puntos y terminados en punto y coma.
Anatomía de una regla CSS
Selector(es) — ¿A qué elementos se aplica?
Propiedad — ¿Qué se cambia?
Valor — ¿A qué se cambia?
Selectores
Los selectores son el mecanismo con el que CSS "apunta" a los elementos HTML que quiere estilizar. Dominar los selectores es dominar CSS. Existen muchos tipos, con distintos niveles de especificidad.
CSS
p {
color: steelblue;
}Selector de tipo — afecta a todos los <p>
Resultado
Este párrafo tiene color steelblue aplicado por el selector de tipo.
CSS
.destacado {
background: #fef9c3;
padding: 4px 8px;
border-radius: 4px;
}Selector de clase — afecta a elementos con class="destacado"
Resultado
Texto normal, texto destacado, texto normal.
CSS
#titulo-principal {
font-size: 2rem;
color: crimson;
}Selector de ID — solo el elemento con id="titulo-principal". Alta especificidad.
Resultado
Título con ID
CSS
button:hover {
background: #2563eb;
transform: scale(1.05);
}
li:nth-child(odd) {
background: #f1f5f9;
}Pseudoclases — estado del elemento: :hover, :focus, :nth-child(), :first-child, :not()
Resultado (pasa el ratón)
| Selector | Ejemplo | Selecciona… | Especificidad |
|---|---|---|---|
| Universal | * { } | Todos los elementos | 0,0,0 |
| Tipo | p { } | Todos los <p> | 0,0,1 |
| Clase | .btn { } | Elementos con class='btn' | 0,1,0 |
| ID | #logo { } | El elemento con id='logo' | 1,0,0 |
| Atributo | input[type='text'] | Inputs de tipo texto | 0,1,0 |
| Descendiente | nav a | Todos los <a> dentro de <nav> | 0,0,2 |
| Hijo directo | ul > li | Solo <li> hijos directos de <ul> | 0,0,2 |
| Hermano adyacente | h2 + p | <p> inmediatamente después de <h2> | 0,0,2 |
| Pseudoclase | a:hover | Enlace al pasar el ratón | 0,1,1 |
| Pseudoelemento | p::first-line | Primera línea de un párrafo | 0,0,2 |
La especificidad (specificity): quién gana cuando hay conflicto
#logo tiene especificidad 1,0,0 y .btn.activo tiene 0,2,0. Si la especificidad es igual, gana la regla que aparece después en el CSS (la cascada). !important rompe la cascada y debe evitarse salvo excepciones justificadas.Propiedades y Valores
CSS tiene más de 500 propiedades, pero un subconjunto de unas 50 cubre el 95% del trabajo diario. Las agrupamos por categorías:
Tipografía
Vista previa — propiedades de texto
font-family: GeorgiaTexto en fuente serif clásica
font-weight: 700Texto en negrita
font-size: 24pxTamaño de fuente 24px
letter-spacing: 0.2emEspaciado entre letras ampliado
line-height: 2Texto con interlineado doble. Facilita la legibilidad en párrafos largos donde el ojo necesita seguir la línea.
text-align: centerTexto centrado horizontalmente
text-transform: uppercasetexto en mayúsculas automático
Colores
Vista previa — formatos de color en CSS
Modelos de Inclusión de CSS
Hay tres formas de incluir CSS en un documento HTML. En proyectos reales siempre se usa la hoja de estilos externa.
CSS Inline — atributo style
1<p style="color: blue; font-size: 18px; margin: 10px;">
2 Texto con estilos directamente en el HTML
3</p>
4<!-- Problema: mezcla estructura con presentación.
5 No reutilizable. Difícil de mantener. -->CSS Interno — etiqueta <style>
1<head>
2 <style>
3 p { color: blue; font-size: 18px; }
4 h1 { color: navy; }
5 </style>
6</head>
7<!-- Válido para páginas únicas o prototipos rápidos.
8 No se puede reutilizar entre páginas. -->CSS Externo — archivo .css separado
1<head>
2 <!-- En el HTML: enlazar el archivo CSS -->
3 <link rel="stylesheet" href="estilos.css">
4 <link rel="stylesheet" href="componentes/boton.css">
5</head>
6
7/* En estilos.css: */
8p { color: blue; font-size: 18px; }
9h1 { color: navy; }
10
11/* Ventajas:
12 - Reutilizable en todas las páginas del sitio
13 - El navegador lo cachea (carga más rápida)
14 - Separación total entre HTML y CSS
15 - Trabajo en equipo sin conflictos */Modelo de Caja (Box Model)
En CSS, todo elemento HTML es una caja rectangular. El modelo de caja define cómo se calcula el espacio que ocupa cada elemento y cómo se relaciona con los demás. Entenderlo es la clave para maquetar sin frustraciones.
Vista previa — modelo de caja interactivo
width × height
El texto, imagen u otros elementos que contiene la caja
Espacio interno entre el contenido y el borde
Línea visible o invisible que rodea el elemento
Espacio externo que separa la caja de otras cajas
1/* Tamaño real sin box-sizing (comportamiento por defecto) */
2.caja {
3 width: 200px; /* ancho del contenido */
4 padding: 20px; /* +40px (20 a cada lado) */
5 border: 2px solid; /* +4px (2 a cada lado) */
6 margin: 16px; /* espacio fuera de la caja */
7}
8/* Ancho TOTAL ocupado: 200 + 40 + 4 = 244px ← confuso */
9
10/* ─────────────────────────────────────────── */
11/* La solución: box-sizing: border-box */
12/* ─────────────────────────────────────────── */
13
14/* Reset global recomendado en todos los proyectos */
15*, *::before, *::after {
16 box-sizing: border-box;
17}
18
19.caja {
20 width: 200px; /* Ahora 200px es el ancho TOTAL: contenido + padding + border */
21 padding: 20px; /* El contenido se reduce, pero la caja siempre mide 200px */
22 border: 2px solid;
23 margin: 16px; /* El margin NUNCA entra en el cálculo del ancho */
24}
25/* Ancho TOTAL ocupado: 200px exactos ← predecible */
26
27/* Propiedades de caja más usadas */
28.ejemplo {
29 width: 300px;
30 height: auto; /* Se adapta al contenido */
31 min-width: 100px;
32 max-width: 600px;
33
34 padding: 16px; /* todos los lados */
35 padding: 8px 16px; /* vertical | horizontal */
36 padding: 4px 8px 12px 16px; /* top | right | bottom | left */
37
38 margin: 0 auto; /* centrar horizontalmente */
39 margin-top: 2rem;
40
41 border: 2px solid #3b82f6;
42 border-radius: 8px; /* esquinas redondeadas */
43
44 overflow: hidden; /* ocultar lo que desborda */
45}Regla de oro: siempre box-sizing: border-box
box-sizing: border-box globalmente. Deberías hacerlo también en tus proyectos desde el principio. Hace que el comportamiento del tamaño de los elementos sea predecible: el width que defines es el que obtienes, sin sorpresas.Flexbox — Diseño en una dimensión
Flexbox (Flexible Box Layout) es el sistema de maquetación CSS pensado para distribuir elementos en una sola dirección: fila o columna. Es perfecto para alinear ítems dentro de un contenedor, crear barras de navegación, tarjetas horizontales o centrar elementos vertical y horizontalmente.
El eje principal y el eje cruzado
flex-direction: row — eje principal horizontal →
flex-direction: column — eje principal vertical ↓
justify-content — Alineación en el eje principal
Vista previa — justify-content
flex-start
center
flex-end
space-between
space-around
space-evenly
align-items — Alineación en el eje cruzado
Vista previa — align-items (alturas distintas para apreciar el efecto)
flex-start
center
flex-end
stretch (por defecto)
baseline
flex-wrap, flex-grow y flex-shrink
flex-wrap: nowrap (por defecto) — los ítems se comprimen
flex-wrap: wrap — los ítems saltan a la siguiente línea
flex-grow — reparto del espacio sobrante
El ítem con grow:2 recibe el doble del espacio libre
1/* ── Contenedor flex ── */
2.contenedor {
3 display: flex; /* activa flexbox */
4 flex-direction: row; /* row | row-reverse | column | column-reverse */
5 flex-wrap: wrap; /* nowrap | wrap | wrap-reverse */
6 justify-content: center; /* alineación en el eje principal */
7 align-items: center; /* alineación en el eje cruzado */
8 align-content: flex-start; /* cuando hay varias líneas (con wrap) */
9 gap: 16px; /* espacio entre ítems (row-gap + column-gap) */
10}
11
12/* Shorthand: flex-flow = flex-direction + flex-wrap */
13.contenedor {
14 flex-flow: row wrap;
15}
16
17/* ── Ítems flex ── */
18.item {
19 flex-grow: 1; /* cuánto crece respecto al espacio libre (0 = no crece) */
20 flex-shrink: 1; /* cuánto se encoge si no hay espacio (0 = no se encoge) */
21 flex-basis: 200px; /* tamaño base antes de repartir espacio libre */
22 align-self: flex-end; /* sobrescribe align-items para este ítem */
23 order: 2; /* cambia el orden visual sin cambiar el HTML */
24}
25
26/* Shorthand: flex = grow + shrink + basis */
27.item { flex: 1 1 200px; } /* equivale a las tres propiedades separadas */
28.item { flex: 1; } /* grow:1, shrink:1, basis:0 */
29.item { flex: auto; } /* grow:1, shrink:1, basis:auto */
30.item { flex: none; } /* grow:0, shrink:0, basis:auto (tamaño fijo) */
31
32/* ── Caso de uso clásico: centrar algo en la pantalla ── */
33.centrar-en-pantalla {
34 display: flex;
35 justify-content: center;
36 align-items: center;
37 min-height: 100vh;
38}
39
40/* ── Caso de uso: navbar con logo a la izquierda y menú a la derecha ── */
41.navbar {
42 display: flex;
43 justify-content: space-between;
44 align-items: center;
45 padding: 0 2rem;
46 height: 64px;
47}
48
49/* ── Caso de uso: tarjetas que se adaptan al ancho disponible ── */
50.grid-tarjetas {
51 display: flex;
52 flex-wrap: wrap;
53 gap: 1.5rem;
54}
55.tarjeta {
56 flex: 1 1 280px; /* crece, se encoge, mínimo 280px */
57 max-width: 400px;
58}Ejemplo completo: navbar con Flexbox
Navbar con justify-content: space-between — logo, menú y botón en sus lugares
CSS Grid — Diseño en dos dimensiones
CSS Grid es el sistema de maquetación más potente de CSS. A diferencia de Flexbox, controla el diseño en dos ejes simultáneamente: filas y columnas. Es ideal para layouts de página completos, galerías, dashboards y cualquier estructura bidimensional compleja.
Terminología de Grid
Grid container
El elemento con display:grid
Grid item
Cada hijo directo del container
Grid line
Las líneas que forman la cuadrícula (numeradas)
Grid cell
La intersección de una fila y columna
grid-template-columns y la unidad fr
La propiedad grid-template-columns define el número y tamaño de las columnas. La unidad fr (fracción) reparte el espacio disponible proporcionalmente, similar a flex-grow.
Vista previa — variantes de grid-template-columns
grid-template-columns: repeat(3, 1fr) — 3 columnas iguales
grid-template-columns: 1fr 2fr 1fr — columna central doble de ancho
grid-template-columns: 200px 1fr — sidebar fijo + contenido flexible
grid-template-columns: repeat(4, 1fr) — 4 columnas iguales
grid-template-areas — Layout visual con nombres
La propiedad grid-template-areas permite definir el layout de la página usando un mapa visual de texto. Es una de las características más potentes y legibles de CSS Grid.
Vista previa — layout completo con grid-areas
200px fijo
1fr — espacio restante
1/* ── Definir el layout completo de una página ── */
2.pagina {
3 display: grid;
4 grid-template-columns: 240px 1fr;
5 grid-template-rows: 64px 1fr 48px;
6 grid-template-areas:
7 "header header" /* header ocupa las 2 columnas */
8 "sidebar main " /* sidebar izquierda + main derecha */
9 "footer footer"; /* footer ocupa las 2 columnas */
10 min-height: 100vh;
11 gap: 0;
12}
13
14/* Asignar cada elemento a su área */
15.pagina > header { grid-area: header; }
16.pagina > nav { grid-area: sidebar; }
17.pagina > main { grid-area: main; }
18.pagina > footer { grid-area: footer; }
19
20/* Punto "." en las areas = celda vacía */
21.dashboard {
22 grid-template-areas:
23 "nav nav nav "
24 "stats stats chart "
25 ". tabla tabla ";
26}
27
28
29/* ── Posicionamiento manual con líneas ── */
30.elemento-grande {
31 /* grid-column: línea-inicio / línea-fin */
32 grid-column: 1 / 3; /* ocupa desde la línea 1 hasta la 3 */
33 grid-row: 2 / 4;
34
35 /* Shorthand con span: "empieza aquí y ocupa N celdas" */
36 grid-column: 2 / span 2; /* empieza en col 2 y ocupa 2 columnas */
37 grid-column: span 3; /* ocupa 3 columnas (posición auto) */
38}
39
40
41/* ── repeat() y minmax() ── */
42.galeria {
43 display: grid;
44 /* repite 3 columnas de entre 200px y 1fr */
45 grid-template-columns: repeat(3, minmax(200px, 1fr));
46 gap: 1.5rem;
47}
48
49/* ── auto-fill y auto-fit: columnas responsivas sin media queries ── */
50.tarjetas-auto {
51 display: grid;
52 /* "Crea tantas columnas de 280px como quepan" */
53 grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
54 gap: 1.5rem;
55}
56/* auto-fill: reserva huecos vacíos */
57/* auto-fit: colapsa los huecos vacíos y expande los ítems existentes */
58
59
60/* ── Grid de 12 columnas clásico ── */
61.container {
62 display: grid;
63 grid-template-columns: repeat(12, 1fr);
64 gap: 1.5rem;
65}
66.col-4 { grid-column: span 4; }
67.col-6 { grid-column: span 6; }
68.col-12 { grid-column: span 12; }¿Flexbox o Grid? — Cuándo usar cada uno
| Criterio | Flexbox | Grid |
|---|---|---|
| Dimensiones | 1D — fila o columna | 2D — filas Y columnas |
| Ideal para… | Componentes: navbar, botones, chips, listas | Layouts: páginas, secciones, dashboards |
| Control del espacio | Los ítems deciden su tamaño | El contenedor decide el layout |
| Orden visual | Fácil con order | Fácil con grid-area y posicionamiento |
| Soporte | 100% navegadores modernos | 100% navegadores modernos |
| Ejemplo típico | display:flex en nav, card, form | display:grid en body layout, galería |
Regla práctica: no es un concurso
Diseño Responsivo y Media Queries
El diseño responsivo hace que una misma página se vea correctamente en móviles, tablets y escritorios. La base son las media queries, reglas CSS que se activan solo cuando se cumple una condición del dispositivo.
Breakpoints y Mobile First
Breakpoints más comunes en la industria
1/* ━━━ ESTRATEGIA MOBILE FIRST ━━━
2 Se empieza diseñando para móvil.
3 Las media queries añaden complejidad hacia arriba. */
4
5/* ── Estilos base (móvil, < 640px) ── */
6.tarjetas {
7 display: grid;
8 grid-template-columns: 1fr; /* 1 columna en móvil */
9 gap: 1rem;
10 padding: 1rem;
11}
12
13.navbar {
14 flex-direction: column; /* menú vertical en móvil */
15}
16
17h1 { font-size: 1.5rem; }
18
19
20/* ── Tablet (≥ 640px) ── */
21@media (min-width: 640px) {
22 .tarjetas {
23 grid-template-columns: repeat(2, 1fr); /* 2 columnas */
24 gap: 1.25rem;
25 padding: 1.5rem;
26 }
27 h1 { font-size: 2rem; }
28}
29
30
31/* ── Escritorio (≥ 1024px) ── */
32@media (min-width: 1024px) {
33 .tarjetas {
34 grid-template-columns: repeat(3, 1fr); /* 3 columnas */
35 gap: 1.5rem;
36 padding: 2rem;
37 }
38 .navbar {
39 flex-direction: row; /* menú horizontal en escritorio */
40 }
41 h1 { font-size: 2.5rem; }
42}
43
44
45/* ── Escritorio grande (≥ 1280px) ── */
46@media (min-width: 1280px) {
47 .tarjetas {
48 grid-template-columns: repeat(4, 1fr); /* 4 columnas */
49 }
50}
51
52
53/* ── Media queries especiales ── */
54
55/* Orientación */
56@media (orientation: landscape) {
57 .hero { min-height: 60vh; }
58}
59
60/* Preferencia del sistema: modo oscuro */
61@media (prefers-color-scheme: dark) {
62 body { background: #0f172a; color: #f1f5f9; }
63}
64
65/* Preferencia de movimiento reducido (accesibilidad) */
66@media (prefers-reduced-motion: reduce) {
67 *, *::before, *::after {
68 animation-duration: 0.01ms !important;
69 transition-duration: 0.01ms !important;
70 }
71}
72
73/* Impresión */
74@media print {
75 nav, aside, .btn { display: none; }
76 body { font-size: 12pt; color: black; }
77}
78
79/* Rango: entre 768px y 1024px */
80@media (min-width: 768px) and (max-width: 1023px) {
81 .solo-tablet { display: block; }
82}Vista previa — grid responsivo según el ancho disponible
Móvil (1 col)
Tablet (2 cols)
Escritorio (3 cols)
El viewport meta tag es indispensable para el responsive
<meta name="viewport" content="width=device-width, initial-scale=1.0">en el <head>, los móviles renderizan la página a 980px de ancho y la escalan hacia abajo, haciendo que las media queries no funcionen como esperas. Siempre debe ser la primera o segunda etiqueta del <head>.Variables CSS (Custom Properties)
Las variables CSS (oficialmente custom properties) permiten definir valores reutilizables que se pueden cambiar desde un solo lugar. Son el equivalente CSS de las variables de programación: si el color de marca cambia, se actualiza en un único punto y afecta a todo el proyecto.
Vista previa — cambio de tema con variables
Tema Azul
Tema Rojo
El mismo CSS, dos temas distintos simplemente cambiando el valor de --color-primario
1/* ── Las variables se declaran con -- y se usan con var() ── */
2
3/* :root aplica al elemento raíz (html): variables globales */
4:root {
5 /* Paleta de colores */
6 --color-primario: #2563eb;
7 --color-secundario: #7c3aed;
8 --color-acento: #10b981;
9 --color-peligro: #dc2626;
10
11 /* Fondos y superficies */
12 --color-fondo: #ffffff;
13 --color-superficie: #f8fafc;
14 --color-borde: #e2e8f0;
15
16 /* Texto */
17 --color-texto: #0f172a;
18 --color-texto-suave: #64748b;
19
20 /* Tipografía */
21 --fuente-base: 'Inter', system-ui, sans-serif;
22 --fuente-codigo: 'Fira Code', 'Cascadia Code', monospace;
23 --tamanyo-base: 1rem; /* 16px */
24 --escala-tipo: 1.25; /* major third */
25
26 /* Espaciado (escala de 4px) */
27 --espacio-1: 0.25rem; /* 4px */
28 --espacio-2: 0.5rem; /* 8px */
29 --espacio-3: 0.75rem; /* 12px */
30 --espacio-4: 1rem; /* 16px */
31 --espacio-6: 1.5rem; /* 24px */
32 --espacio-8: 2rem; /* 32px */
33
34 /* Bordes */
35 --radio-sm: 4px;
36 --radio-md: 8px;
37 --radio-lg: 16px;
38 --radio-pill: 9999px;
39
40 /* Sombras */
41 --sombra-sm: 0 1px 3px rgba(0,0,0,0.12);
42 --sombra-md: 0 4px 12px rgba(0,0,0,0.15);
43 --sombra-lg: 0 10px 40px rgba(0,0,0,0.20);
44
45 /* Transiciones */
46 --transicion: 200ms ease;
47}
48
49/* ── Usar las variables ── */
50.boton {
51 background-color: var(--color-primario);
52 color: var(--color-fondo);
53 padding: var(--espacio-3) var(--espacio-6);
54 border-radius: var(--radio-md);
55 font-family: var(--fuente-base);
56 box-shadow: var(--sombra-sm);
57 transition: box-shadow var(--transicion),
58 background-color var(--transicion);
59}
60
61.boton:hover {
62 box-shadow: var(--sombra-md);
63}
64
65/* ── Modo oscuro con variables ── */
66@media (prefers-color-scheme: dark) {
67 :root {
68 --color-fondo: #0f172a;
69 --color-superficie: #1e293b;
70 --color-borde: #334155;
71 --color-texto: #f1f5f9;
72 --color-texto-suave: #94a3b8;
73 /* Los colores primarios pueden mantenerse o ajustarse */
74 --color-primario: #60a5fa; /* más claro para mejor contraste en oscuro */
75 }
76 /* Automáticamente TODOS los elementos que usen estas variables se actualizan */
77}
78
79/* ── Variables locales (solo afectan a su subtree) ── */
80.tarjeta-alerta {
81 --color-borde-local: var(--color-peligro);
82 border: 2px solid var(--color-borde-local);
83 /* Esta variable solo existe dentro de .tarjeta-alerta */
84}
85
86/* ── Valor de fallback en var() ── */
87.elemento {
88 color: var(--color-no-definida, #000000);
89 /* Si --color-no-definida no existe, usa #000000 */
90 font-size: var(--tamanyo-titulo, var(--tamanyo-base, 1rem));
91 /* Fallbacks anidados */
92}
93
94/* ── Modificar variables con JavaScript ── */
95/*
96 document.documentElement.style.setProperty('--color-primario', '#ff6b00');
97 Cambia el tema entero en tiempo real
98*/Transiciones y Animaciones
CSS puede animar propiedades de forma fluida sin necesidad de JavaScript. Hay dos sistemas: las transiciones (para cambios provocados por eventos como :hover) y las animaciones (@keyframes), que se ejecutan de forma autónoma.
transition — Cambios suaves entre estados
Vista previa — pasa el ratón sobre cada botón
1/* ── Sintaxis de transition ── */
2.elemento {
3 /* transition: propiedad duración función-timing retraso */
4 transition: background-color 300ms ease 0ms;
5
6 /* Múltiples propiedades */
7 transition:
8 background-color 300ms ease,
9 transform 200ms cubic-bezier(0.34, 1.56, 0.64, 1),
10 box-shadow 300ms ease;
11
12 /* Todas las propiedades animables (con cuidado: puede ser lento) */
13 transition: all 300ms ease;
14}
15
16/* ── Funciones de timing (easing) ── */
17.elemento {
18 transition-timing-function: ease; /* rápido-lento-rápido (por defecto) */
19 transition-timing-function: ease-in; /* empieza lento */
20 transition-timing-function: ease-out; /* termina lento */
21 transition-timing-function: ease-in-out; /* empieza y termina lento */
22 transition-timing-function: linear; /* velocidad constante */
23
24 /* cubic-bezier: control total de la curva */
25 /* Herramienta: https://cubic-bezier.com */
26 transition-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1); /* rebote */
27}
28
29/* ── Ejemplos prácticos ── */
30
31/* Botón con efecto lift */
32.boton {
33 background: #3b82f6;
34 color: white;
35 padding: 12px 24px;
36 border-radius: 8px;
37 border: none;
38 cursor: pointer;
39 box-shadow: 0 2px 6px rgba(59,130,246,0.3);
40 transform: translateY(0);
41 transition:
42 box-shadow 200ms ease,
43 transform 200ms ease,
44 background 200ms ease;
45}
46.boton:hover {
47 background: #2563eb;
48 box-shadow: 0 8px 20px rgba(59,130,246,0.4);
49 transform: translateY(-2px);
50}
51.boton:active {
52 transform: translateY(0px);
53 box-shadow: 0 2px 6px rgba(59,130,246,0.3);
54}
55
56/* Menú desplegable con transición de opacidad + transform */
57.menu-dropdown {
58 opacity: 0;
59 transform: translateY(-8px) scale(0.95);
60 pointer-events: none;
61 transition:
62 opacity 200ms ease,
63 transform 200ms ease;
64}
65.menu-trigger:hover + .menu-dropdown,
66.menu-dropdown:hover {
67 opacity: 1;
68 transform: translateY(0) scale(1);
69 pointer-events: auto;
70}@keyframes — Animaciones autónomas
Vista previa — animaciones con @keyframes
pulse
spin
bounce
fadeIn
skeleton
1/* ── Definir un keyframe ── */
2@keyframes nombre-animacion {
3 from { opacity: 0; transform: translateY(-20px); }
4 to { opacity: 1; transform: translateY(0); }
5}
6
7/* Con porcentajes para mayor control */
8@keyframes rebote {
9 0% { transform: translateY(0); }
10 40% { transform: translateY(-30px); }
11 60% { transform: translateY(-15px); }
12 80% { transform: translateY(-5px); }
13 100% { transform: translateY(0); }
14}
15
16/* ── Aplicar la animación ── */
17.elemento {
18 /* animation: nombre duración timing iteraciones dirección fill-mode retraso */
19 animation: nombre-animacion 0.5s ease-out 1 normal forwards 0s;
20
21 /* Propiedades por separado */
22 animation-name: fadeIn;
23 animation-duration: 0.6s;
24 animation-timing-function: ease-out;
25 animation-iteration-count: 1; /* número o infinite */
26 animation-direction: normal; /* normal | reverse | alternate | alternate-reverse */
27 animation-fill-mode: forwards; /* none | forwards | backwards | both */
28 animation-delay: 0.2s;
29 animation-play-state: running; /* running | paused */
30}
31
32/* ── Animaciones comunes listas para usar ── */
33
34/* Fade In desde abajo */
35@keyframes fadeInUp {
36 from { opacity: 0; transform: translateY(20px); }
37 to { opacity: 1; transform: translateY(0); }
38}
39
40/* Spinner de carga */
41@keyframes spin {
42 to { transform: rotate(360deg); }
43}
44.spinner {
45 width: 32px; height: 32px;
46 border: 3px solid rgba(59,130,246,0.2);
47 border-top-color: #3b82f6;
48 border-radius: 50%;
49 animation: spin 0.8s linear infinite;
50}
51
52/* Skeleton loading (shimmer) */
53@keyframes shimmer {
54 from { transform: translateX(-100%); }
55 to { transform: translateX(100%); }
56}
57.skeleton {
58 background: #e2e8f0;
59 border-radius: 4px;
60 position: relative;
61 overflow: hidden;
62}
63.skeleton::after {
64 content: "";
65 position: absolute;
66 inset: 0;
67 background: linear-gradient(90deg, transparent, rgba(255,255,255,0.6), transparent);
68 animation: shimmer 1.5s infinite;
69}
70
71/* Efecto pulse para notificaciones */
72@keyframes pulse {
73 0%, 100% { transform: scale(1); opacity: 1; }
74 50% { transform: scale(1.2); opacity: 0.7; }
75}
76.badge-notificacion {
77 animation: pulse 2s ease-in-out infinite;
78}
79
80/* Entrada de elementos de lista escalonada */
81.lista-item {
82 animation: fadeInUp 0.4s ease-out both;
83}
84.lista-item:nth-child(1) { animation-delay: 0.0s; }
85.lista-item:nth-child(2) { animation-delay: 0.1s; }
86.lista-item:nth-child(3) { animation-delay: 0.2s; }
87.lista-item:nth-child(4) { animation-delay: 0.3s; }
88
89/* Siempre respeta las preferencias de accesibilidad */
90@media (prefers-reduced-motion: reduce) {
91 .lista-item, .spinner, .badge-notificacion {
92 animation: none;
93 }
94}Pseudoelementos ::before y ::after
Los pseudoelementos ::before y::after insertan contenido decorativo antes o después del contenido real de un elemento, sin tocar el HTML. Son herramientas muy potentes para iconos, efectos visuales y decoraciones.
Vista previa — usos de ::before y ::after
Punto indicador de estado
Separador de texto (líneas con ::before y ::after)
Tooltip puro CSS con ::before y ::after
Comillas decorativas en blockquote
"La web es para todos. No para unos pocos."— Tim Berners-Lee
1/* REGLA DE ORO: ::before y ::after SIEMPRE necesitan content */
2.elemento::before { content: ""; } /* vacío para decoración pura */
3.elemento::after { content: "►"; } /* texto/emoji */
4.elemento::before { content: attr(data-tooltip); } /* valor de un atributo */
5
6/* ── Separador "o continúa con" ── */
7.separador {
8 display: flex;
9 align-items: center;
10 gap: 1rem;
11 text-align: center;
12}
13.separador::before,
14.separador::after {
15 content: "";
16 flex: 1;
17 height: 1px;
18 background: #e2e8f0;
19}
20
21/* ── Tooltip puro CSS ── */
22[data-tooltip] {
23 position: relative;
24}
25[data-tooltip]::before { /* El texto */
26 content: attr(data-tooltip);
27 position: absolute;
28 bottom: calc(100% + 8px);
29 left: 50%;
30 transform: translateX(-50%) translateY(4px);
31 background: #0f172a;
32 color: white;
33 padding: 6px 10px;
34 border-radius: 6px;
35 font-size: 0.8rem;
36 white-space: nowrap;
37 opacity: 0;
38 pointer-events: none;
39 transition: opacity 200ms ease, transform 200ms ease;
40}
41[data-tooltip]::after { /* La flechita */
42 content: "";
43 position: absolute;
44 bottom: calc(100% + 2px);
45 left: 50%;
46 transform: translateX(-50%) translateY(4px);
47 border: 5px solid transparent;
48 border-top-color: #0f172a;
49 opacity: 0;
50 pointer-events: none;
51 transition: opacity 200ms ease, transform 200ms ease;
52}
53[data-tooltip]:hover::before,
54[data-tooltip]:hover::after {
55 opacity: 1;
56 transform: translateX(-50%) translateY(0);
57}
58
59/* ── Punto de estado animado ── */
60.activo::before {
61 content: "";
62 display: inline-block;
63 width: 8px;
64 height: 8px;
65 border-radius: 50%;
66 background: #10b981;
67 margin-right: 8px;
68 animation: pulse 2s infinite;
69}
70
71/* ── Comillas en blockquote ── */
72blockquote {
73 position: relative;
74 padding: 1.5rem 1.5rem 1.5rem 3rem;
75 background: #f8fafc;
76 border-radius: 8px;
77}
78blockquote::before {
79 content: '"';
80 position: absolute;
81 top: -0.25rem;
82 left: 0.5rem;
83 font-size: 4rem;
84 color: #3b82f6;
85 font-family: Georgia, serif;
86 line-height: 1;
87 opacity: 0.5;
88}
89
90/* ── Contador automático con CSS ── */
91ol.capitulos { counter-reset: capitulo; }
92ol.capitulos li { counter-increment: capitulo; }
93ol.capitulos li::before {
94 content: "Capítulo " counter(capitulo) " — ";
95 font-weight: 700;
96 color: #3b82f6;
97}Funciones CSS Modernas
CSS incluye funciones matemáticas y de valor que permiten hacer cálculos, crear tipografía fluida y escribir código más expresivo y mantenible.
calc() — Cálculos mezclando unidades
Vista previa — calc()
width: calc(100% - 240px) — ancho total menos sidebar
padding: calc(var(--espacio-4) * 2)
clamp() — Tipografía fluida
clamp(mínimo, preferido, máximo) devuelve el valor preferido pero nunca baja del mínimo ni sube del máximo. Ideal para tipografía responsiva sin media queries.
Vista previa — clamp() para tipografía fluida
font-size: clamp(1.2rem, 4vw, 2.5rem)
font-size: clamp(0.875rem, 2vw, 1.125rem) — nunca menor a 14px, nunca mayor a 18px
Redimensiona la ventana del navegador para ver el efecto fluido
1/* ── calc(): operaciones con cualquier unidad CSS ── */
2.contenido-principal {
3 width: calc(100% - 280px); /* total - sidebar */
4 margin: calc(var(--nav-h) + 2rem) auto 0;
5 padding: calc(1rem * 1.5);
6 /* Operadores: + - * / (espacios obligatorios alrededor de + y -) */
7}
8
9/* ── min(): el valor más pequeño ── */
10.contenedor {
11 width: min(100%, 1200px); /* nunca mayor a 1200px ni mayor al 100% */
12 /* Equivale a: width:100%; max-width:1200px */
13}
14
15/* ── max(): el valor más grande ── */
16.caja {
17 padding: max(1rem, 3vw); /* al menos 1rem, puede crecer con el viewport */
18}
19
20/* ── clamp(): mínimo, preferido, máximo ── */
21.tipografia-fluida {
22 /* clamp(min, ideal, max) — no necesita media queries */
23 font-size: clamp(1rem, 2.5vw, 1.5rem);
24
25 /* H1 fluido: mínimo 1.8rem, ideal 5vw, máximo 3.5rem */
26 font-size: clamp(1.8rem, 5vw, 3.5rem);
27
28 /* Espaciado fluido */
29 padding: clamp(1rem, 5vw, 3rem);
30 gap: clamp(0.75rem, 2vw, 1.5rem);
31}
32
33/* ── Escala tipográfica fluida completa ── */
34:root {
35 --text-sm: clamp(0.8rem, 1.5vw, 0.9rem);
36 --text-base: clamp(1rem, 2vw, 1.1rem);
37 --text-lg: clamp(1.1rem, 2.5vw, 1.3rem);
38 --text-xl: clamp(1.3rem, 3vw, 1.8rem);
39 --text-2xl: clamp(1.6rem, 4vw, 2.5rem);
40 --text-3xl: clamp(2rem, 5vw, 3.5rem);
41 --text-4xl: clamp(2.5rem, 6vw, 5rem);
42}
43
44/* ── Funciones de color modernas ── */
45.elemento {
46 /* oklch: espacio de color perceptualmente uniforme (recomendado 2024) */
47 color: oklch(0.7 0.2 250); /* luminosidad, croma, tono */
48 background: oklch(0.2 0.05 250 / 0.8); /* con transparencia */
49
50 /* color-mix: mezclar dos colores */
51 background: color-mix(in oklch, #3b82f6 70%, #8b5cf6 30%);
52
53 /* light-dark(): automático según el tema del sistema */
54 color: light-dark(#0f172a, #f1f5f9);
55 background: light-dark(#ffffff, #0f172a);
56}
57
58/* ── Funciones de transformación ── */
59.transformaciones {
60 /* Se pueden combinar en una sola propiedad */
61 transform:
62 translateX(20px)
63 translateY(-10px)
64 rotate(45deg)
65 scale(1.2)
66 skewX(5deg);
67}
68
69/* ── env(): variables del entorno (safe area en iPhone) ── */
70.contenedor-movil {
71 padding-bottom: env(safe-area-inset-bottom);
72 padding-left: env(safe-area-inset-left);
73 padding-right: env(safe-area-inset-right);
74}CSS Moderno: Selectores y Features de 2023–2024
En los últimos años CSS ha recibido las actualizaciones más importantes de su historia. Estas características tienen soporte en todos los navegadores modernos y cambian radicalmente la forma de escribir CSS.
:is(), :where() y :not() — Selectores lógicos
1/* ── :is() — Agrupa selectores con especificidad del selector más específico ── */
2
3/* Antes: repetición */
4h1 a, h2 a, h3 a, h4 a, h5 a, h6 a { color: inherit; }
5
6/* Ahora con :is() */
7:is(h1, h2, h3, h4, h5, h6) a {
8 color: inherit;
9 text-decoration: none;
10}
11
12/* Combinaciones complejas simplificadas */
13:is(article, section, aside) :is(h2, h3) {
14 font-size: 1.4rem;
15}
16
17/* ── :where() — Como :is() pero con especificidad CERO ── */
18/* Útil para reset/base styles que sean fáciles de sobrescribir */
19:where(ul, ol) {
20 padding-left: 1.5rem;
21 list-style: disc;
22}
23/* Esta regla tiene especificidad 0, cualquier otra la puede sobrescribir */
24
25/* ── :not() — Excluir elementos ── */
26/* Todos los botones excepto los deshabilitados */
27button:not([disabled]):not(.btn-texto) {
28 cursor: pointer;
29 background: #3b82f6;
30 color: white;
31}
32
33/* Links que no sean navegación */
34a:not([class]):not([role]) {
35 text-decoration: underline;
36 color: #3b82f6;
37}
38
39/* ── :has() — El selector "padre" (revolucionario) ── */
40/* Un formulario que contiene un input inválido */
41form:has(input:invalid) {
42 border-color: #dc2626;
43}
44
45/* Una tarjeta que contiene una imagen → más padding arriba */
46.tarjeta:has(img) {
47 padding-top: 0;
48}
49
50/* Párrafo que sigue a una imagen */
51figure:has(img) + p {
52 margin-top: 0.5rem;
53 font-style: italic;
54 color: #64748b;
55}
56
57/* Nav que está siendo hover → cambia el logo */
58nav:has(a:hover) .logo {
59 color: #3b82f6;
60}
61
62/* ── :focus-visible — Foco solo para teclado ── */
63/* Solo muestra el anillo de foco cuando se navega con teclado, no con ratón */
64button:focus-visible {
65 outline: 3px solid #3b82f6;
66 outline-offset: 2px;
67}
68button:focus:not(:focus-visible) {
69 outline: none; /* sin outline al hacer clic */
70}
71
72/* ── Nesting nativo CSS (sin preprocesador) ── */
73/* Desde 2023, soporte nativo en todos los navegadores modernos */
74.boton {
75 background: #3b82f6;
76 color: white;
77 padding: 10px 20px;
78 border-radius: 8px;
79 transition: all 200ms;
80
81 /* Anidado nativo */
82 &:hover {
83 background: #2563eb;
84 transform: translateY(-1px);
85 }
86
87 &:active {
88 transform: translateY(0);
89 }
90
91 &.boton-grande {
92 font-size: 1.2rem;
93 padding: 14px 28px;
94 }
95
96 /* Elemento hijo */
97 & .icono {
98 margin-right: 8px;
99 }
100}Container Queries — El futuro del responsive
Las container queries permiten aplicar estilos basados en el tamaño del contenedor padre, no del viewport. Son la respuesta a uno de los problemas más antiguos del responsive: componentes que se adaptan a su contexto, no a la pantalla.
1/* ── Declarar el contenedor ── */
2.tarjeta-wrapper {
3 container-type: inline-size; /* activa las container queries */
4 container-name: tarjeta; /* nombre opcional para referencia */
5}
6
7/* Shorthand */
8.tarjeta-wrapper {
9 container: tarjeta / inline-size;
10}
11
12/* ── Aplicar estilos según el tamaño del contenedor ── */
13@container tarjeta (min-width: 400px) {
14 .tarjeta {
15 display: grid;
16 grid-template-columns: 200px 1fr;
17 gap: 1rem;
18 }
19}
20
21@container tarjeta (min-width: 600px) {
22 .tarjeta {
23 grid-template-columns: 250px 1fr;
24 }
25 .tarjeta h2 {
26 font-size: 1.5rem;
27 }
28}
29
30/* Sin container query: la misma tarjeta en 1 columna si su contenedor es estrecho */
31@container (max-width: 399px) {
32 .tarjeta {
33 display: flex;
34 flex-direction: column;
35 }
36}
37
38/* ── Unidades de contenedor: cqw, cqh ── */
39.tarjeta h2 {
40 /* 5% del ancho del contenedor padre */
41 font-size: clamp(1rem, 5cqw, 2rem);
42}
43
44
45/* ── @layer: organizar la cascada en capas ── */
46/* Muy útil para evitar problemas de especificidad con frameworks */
47@layer reset, base, componentes, utilidades;
48
49@layer reset {
50 *, *::before, *::after { box-sizing: border-box; }
51 * { margin: 0; }
52}
53
54@layer base {
55 body { font-family: system-ui; line-height: 1.5; }
56 h1 { font-size: 2rem; }
57}
58
59@layer componentes {
60 .boton { background: blue; color: white; }
61}
62
63@layer utilidades {
64 .hidden { display: none !important; }
65 /* Las utilidades ganan porque su capa va al final */
66}Comparativa: Media Queries vs Container Queries
| Aspecto | Media Query | Container Query |
|---|---|---|
| Se basa en… | Tamaño del VIEWPORT | Tamaño del CONTENEDOR PADRE |
| Ideal para… | Layout de página general | Componentes reutilizables |
| Contexto | Global — siempre igual | Contextual — depende de dónde se usa |
| Soporte | Universal | Todos los navegadores modernos (2023) |
| Ejemplo | @media (min-width: 768px) | @container (min-width: 400px) |
Organización del CSS: BEM y Buenas Prácticas
En proyectos reales el CSS puede volverse imposible de mantener. Las metodologías de nomenclatura y organización resuelven este problema definiendo reglas claras para nombrar clases y estructurar los archivos.
BEM — Block Element Modifier
Anatomía de BEM
bloque
Componente independiente con significado propio. Ej: .tarjeta, .menu, .boton
bloque__elemento
Parte interna del bloque, sin significado fuera. Ej: .tarjeta__imagen, .menu__item
bloque--modificador
Variante o estado del bloque/elemento. Ej: .boton--primario, .tarjeta--destacada
1<!-- HTML con nomenclatura BEM -->
2<article class="tarjeta tarjeta--destacada">
3 <div class="tarjeta__cabecera">
4 <img class="tarjeta__imagen" src="foto.jpg" alt="...">
5 <span class="tarjeta__badge tarjeta__badge--nuevo">Nuevo</span>
6 </div>
7 <div class="tarjeta__cuerpo">
8 <h2 class="tarjeta__titulo">Monitor UltraWide Pro</h2>
9 <p class="tarjeta__descripcion">Panel IPS 144Hz 34"</p>
10 <div class="tarjeta__precio">
11 <span class="tarjeta__precio-antiguo">499€</span>
12 <strong class="tarjeta__precio-actual">449€</strong>
13 </div>
14 </div>
15 <footer class="tarjeta__pie">
16 <button class="boton boton--primario boton--grande">
17 <span class="boton__icono">🛒</span>
18 <span class="boton__texto">Añadir al carrito</span>
19 </button>
20 </footer>
21</article>1/* ── CSS con BEM: sin anidamiento excesivo, sin guerras de especificidad ── */
2
3/* BLOQUE */
4.tarjeta {
5 border-radius: 12px;
6 overflow: hidden;
7 box-shadow: 0 2px 8px rgba(0,0,0,0.1);
8 background: white;
9}
10
11/* MODIFICADOR del bloque */
12.tarjeta--destacada {
13 box-shadow: 0 8px 24px rgba(59,130,246,0.25);
14 border: 2px solid #3b82f6;
15}
16
17/* ELEMENTOS */
18.tarjeta__cabecera {
19 position: relative;
20}
21.tarjeta__imagen {
22 width: 100%;
23 aspect-ratio: 16 / 9;
24 object-fit: cover;
25}
26.tarjeta__badge {
27 position: absolute;
28 top: 12px; right: 12px;
29 padding: 4px 10px;
30 border-radius: 9999px;
31 font-size: 0.75rem;
32 font-weight: 700;
33 background: #e2e8f0;
34 color: #475569;
35}
36
37/* MODIFICADOR del elemento */
38.tarjeta__badge--nuevo {
39 background: #dcfce7;
40 color: #166534;
41}
42.tarjeta__badge--oferta {
43 background: #fef2f2;
44 color: #991b1b;
45}
46
47.tarjeta__cuerpo { padding: 1rem; }
48.tarjeta__titulo { font-size: 1.1rem; font-weight: 700; }
49.tarjeta__precio { display: flex; gap: 0.5rem; align-items: baseline; }
50.tarjeta__precio-antiguo { color: #94a3b8; text-decoration: line-through; }
51.tarjeta__precio-actual { font-size: 1.4rem; font-weight: 700; color: #16a34a; }Reglas de oro para CSS mantenible
!important salvo en utilidades deliberadas, escribe un comentario antes de cada componente y usa variables CSS para todos los valores que se repitan más de dos veces.Validación de Documentos
Validar un documento significa comprobar automáticamente que cumple el estándar definido. Aunque los navegadores modernos son tolerantes con los errores HTML, publicar código inválido provoca problemas de accesibilidad, SEO y mantenimiento.
Validación HTML
El validador oficial del W3C comprueba etiquetas mal cerradas, atributos inválidos, elementos anidados incorrectamente y otros errores de conformidad.
validator.w3.org
Validación CSS
El validador CSS del W3C detecta propiedades inexistentes, valores incorrectos, errores de sintaxis y uso de propiedades sin prefijos necesarios.
jigsaw.w3.org/css-validator
Herramientas integradas
VS Code con la extensión HTML CSS Support y ESLint con plugins específicos detectan errores en tiempo real mientras escribes, sin salir del editor.
VS Code + Prettier + ESLint
Errores comunes que detecta el validador
</div> de más o de menos, atributo alt ausente en imágenes, uso de <b> o <i> en lugar de <strong> y <em>, elementos anidados incorrectamente (un <p> dentro de otro <p>), o valores de atributo sin comillas.Ejercicios Prácticos
Construir la estructura semántica de una página web
Crea un documento HTML5 completo y bien formado para la página de inicio de un blog de tecnología llamado "CodeNotes". La página debe incluir: Estructura semántica obligatoria: - header: logo (texto) + nav con 4 enlaces (Inicio, Artículos, Sobre mí, Contacto) - main con dos secciones: - section "Artículos recientes" con 3 article (título h2, fecha con <time>, resumen, enlace "Leer más") - section "Categorías" con una lista no ordenada de 5 categorías (HTML, CSS, JavaScript, Python, DevOps) - aside: "Acerca del autor" con nombre, descripción y un enlace al perfil - footer: copyright + enlace a política de privacidad Requisitos técnicos: 1. DOCTYPE correcto y atributo lang="es" 2. meta charset y viewport obligatorios 3. Todos los enlaces con href (pueden ser "#") 4. Al menos 3 atributos aria-label o aria-labelledby 5. Un elemento <time datetime="..."> en cada artículo 6. Código indentado con 2 espacios
Formulario de registro con validación nativa HTML5
Crea un formulario HTML5 completo de registro para una academia online. El formulario debe contener: Campos obligatorios (required): - Nombre completo (text, min 3 caracteres) - Email (email) - Contraseña (password, min 8 caracteres) - Fecha de nacimiento (date, máximo hoy) Campos opcionales: - Teléfono (tel, con placeholder "+34 XXX XXX XXX") - Nivel de programación (select: Ninguno / Básico / Intermedio / Avanzado) - Cursos de interés (checkboxes: HTML/CSS, JavaScript, Python, React, al menos 1) - Cómo nos conociste (radio: Redes sociales / Amigo/familiar / Búsqueda web / Publicidad) - Comentarios (textarea, máximo 500 caracteres) - Acepto los términos (checkbox, required) Requisitos técnicos: 1. Todo con label correctamente vinculado (for + id) 2. fieldset y legend para agrupar checkboxes y radios 3. Usar el atributo autocomplete en los campos apropiados 4. El botón submit y un botón reset 5. El formulario debe poder enviarse sin JavaScript usando solo la validación nativa
Hoja de estilos CSS con selectores y modelo de caja
Dado el siguiente HTML de una tarjeta de producto, escribe el CSS necesario para que tenga el aspecto indicado: ```html <article class="tarjeta" id="producto-destacado"> <span class="badge">Novedad</span> <img src="producto.jpg" alt="Producto"> <div class="info"> <h2 class="nombre">Monitor UltraWide Pro</h2> <p class="descripcion">Panel IPS, 144Hz, 34 pulgadas</p> <div class="precio-area"> <del class="precio-anterior">499.99€</del> <strong class="precio-actual">449.99€</strong> </div> <button class="btn-comprar">Añadir al carrito</button> </div> </article> ``` Requisitos CSS (usar selectores específicos, no inline): 1. .tarjeta: ancho 300px, border-radius 12px, sombra, overflow hidden, box-sizing border-box 2. #producto-destacado: border de 2px azul (solo este) 3. .badge: posición absolute, top derecho, fondo azul, texto blanco, font-size pequeño 4. img dentro de .tarjeta: ancho 100%, height 200px, object-fit cover 5. .info: padding 16px 6. .nombre: font-size 1.2rem, margin-bottom 8px 7. .descripcion: color gris, font-size 0.9rem 8. .precio-anterior: color gris, text-decoration line-through 9. .precio-actual: color verde, font-size 1.4rem 10. .btn-comprar: width 100%, padding 12px, fondo azul, color blanco, border none, border-radius 8px, cursor pointer 11. .btn-comprar:hover: fondo azul más oscuro + transform scale(1.02) 12. Aplicar box-sizing: border-box globalmente
Identificar especificidad y la cascada CSS
Para cada uno de los siguientes pares de reglas CSS que apuntan al mismo elemento, determina cuál gana y por qué. Calcula la especificidad de cada selector y razona la respuesta. El HTML es: `<p id="intro" class="texto destacado">Párrafo</p>` Caso 1: ```css p { color: blue; } .texto { color: red; } ``` Caso 2: ```css .texto.destacado { color: green; } #intro { color: orange; } ``` Caso 3: ```css #intro { color: purple; } #intro { color: teal; } ``` Caso 4: ```css .texto { color: pink !important; } #intro { color: gold; } ``` Caso 5: ```css body p.texto { color: navy; } p#intro { color: lime; } ``` Para cada caso indica: la especificidad de cada selector (formato id,clase,tipo) y el color resultante.
Página multimedia accesible con vídeo, audio y figura
Crea una página HTML5 de una filmoteca online para la película "El Laberinto del Fauno" que incluya: 1. Head completo: - charset, viewport y title - Meta description y meta author - Al menos 3 meta tags Open Graph (og:title, og:description, og:image) 2. Sección de la película con: - <figure> con <img> responsive (srcset con 3 tamaños: 400w, 800w, 1200w) y <figcaption> - <video> con dos fuentes (mp4 y webm), poster, controls, y subtítulos con <track> - <audio> con la banda sonora, controls y texto de fallback 3. Sección de información con: - <details>/<summary> para mostrar/ocultar la sinopsis (cerrado por defecto) - <details>/<summary> para mostrar el reparto (abierto por defecto con open) - <progress> mostrando el porcentaje de visualización (75%) - <meter> mostrando la puntuación 8.4/10 con zonas optimum=9, high=7, low=4 4. Formulario de valoración con: - <datalist> para el campo de país del usuario - <output> que muestre el resultado de multiplicar puntuación × popularidad
Auditoría de accesibilidad de una página web
La siguiente página tiene al menos 8 problemas de accesibilidad. Identifícalos todos, explica qué norma WCAG viola cada uno y escribe el código corregido: ```html <!DOCTYPE html> <html> <head> <title>Tienda</title> </head> <body> <div class="header"> <img src="logo.png"> <div class="menu"> <span onclick="irA('/')">Inicio</span> <span onclick="irA('/productos')">Productos</span> </div> </div> <div class="contenido"> <div class="titulo">Nuestros Productos</div> <div class="producto"> <img src="monitor.jpg"> <div style="font-size:20px;font-weight:bold">Monitor UltraWide</div> <span style="color:lightgray;font-size:10px">449.99€</span> <div onclick="añadirCarrito(42)" style="background:blue;color:white;padding:5px"> 🛒 </div> </div> </div> <div class="footer"> <a href="#">Política de privacidad</a> <a href="#">Haz clic aquí</a> </div> </body> </html> ``` Para cada problema indica: qué está mal, qué pauta WCAG viola y cómo se corrige.
Construir un componente de tarjeta con todos los elementos aprendidos
Crea una tarjeta de perfil de desarrollador usando HTML puro que integre todos los elementos avanzados vistos. La tarjeta debe incluir: HTML semántico y estructura: - article con data-* attributes (id, especialidad, disponible) - header del article con img responsiva (srcset), h2 y role apropiado - Información con etiquetas semánticas de texto correctas Elementos interactivos nativos: - details/summary para "Ver experiencia completa" - progress para el nivel de cada habilidad (HTML 95%, CSS 87%, JS 78%) - meter para el "índice de empleabilidad" con zonas optimum/high/low - datalist vinculado a un input de búsqueda de tecnologías Accesibilidad completa: - Todos los images con alt descriptivo - Botones con aria-label cuando sea necesario - aria-live para el estado de disponibilidad - tabindex correcto en todos los elementos interactivos - Estructura de encabezados coherente Meta información en el head (al menos 5 meta tags diferentes incluyendo Open Graph)
Layout de página completo con Grid + Flexbox
Construye el layout de una aplicación de gestión de tareas (estilo Trello simplificado) usando Grid para el layout general y Flexbox para los componentes internos. El resultado debe tener: Estructura Grid de la página: - Header fijo arriba (64px) con logo y nav - Sidebar izquierda (260px) con menú de proyectos - Área principal de contenido (resto del espacio) - Sin footer Dentro del área principal: - Grid de 3 columnas de igual ancho con gap de 24px - Cada columna es una lista de tareas (Pendiente / En progreso / Completado) - Cada columna tiene un header con título y botón de añadir Tarjeta de tarea (Flexbox column): - Avatar de usuario (32px círculo) - Título de la tarea - Tags de colores (flex row, wrap) - Fecha límite y número de comentarios (flex row, space-between) Requisitos CSS: 1. Variables CSS para todos los colores, espaciados y radios 2. El sidebar y el header deben ser position: sticky 3. Las columnas deben ser scrollables independientemente (overflow-y: auto) 4. Efecto hover en las tarjetas (translateY + sombra) 5. Mobile: 1 columna con scroll horizontal entre listas (overflow-x: auto) 6. Diseño completo en un único archivo CSS bien organizado con comentarios
Sistema de diseño con variables CSS y modo oscuro
Crea un sistema de diseño completo usando custom properties CSS que soporte automáticamente modo claro y oscuro. El sistema debe incluir: Variables a definir en :root: - Paleta completa: 9 tonos para cada color base (primario, gris, verde, rojo, amarillo) Ej: --color-primario-50 al --color-primario-900 - Variables semánticas que referencian la paleta: --color-bg-base, --color-bg-muted, --color-bg-raised --color-text-primary, --color-text-secondary, --color-text-disabled --color-border, --color-border-strong --color-action-bg, --color-action-hover, --color-action-text - Sistema de espaciado de 10 pasos (4px base) - Sistema tipográfico con 7 tamaños usando clamp() - 3 niveles de sombra - Variables de animación (duración y easing) Modo oscuro con @media (prefers-color-scheme: dark): Solo redefine las variables semánticas. La paleta permanece igual. Demostración: crea al menos 3 componentes que usen exclusivamente las variables: 1. Un botón con variantes (primario, secundario, peligro, ghost) 2. Una tarjeta de perfil de usuario 3. Un campo de formulario con estado normal, focus y error
Animación de carga con @keyframes — Skeleton screen
Implementa un componente de skeleton loading para una tarjeta de artículo que aparece mientras el contenido real carga. El efecto "shimmer" debe parecerse al de Facebook/LinkedIn. El skeleton debe imitar la estructura de una tarjeta real: - Una imagen rectangular (16:9) en la parte superior - Una línea de título (ancho: 80% del contenedor) - Dos líneas de descripción (ancho: 100% y 60%) - Una línea de metadatos con un círculo (avatar) y dos líneas cortas El efecto shimmer: - Un gradiente brillante que viaja de izquierda a derecha - Duración: 1.5s, en bucle infinito - El fondo base debe ser gris claro, el brillo un blanco semitransparente Después de 3 segundos, el skeleton debe transicionar (fade out) a la tarjeta real con contenido.
Recrear una UI real — tarjeta de perfil de GitHub
Usando solo HTML semántico y CSS (sin frameworks), recrea una tarjeta de perfil similar a la barra lateral de GitHub. La tarjeta debe tener: Estructura HTML (semántica): - article con role y aria adecuados - figure + img para el avatar (200x200, border-radius 50%) - Nombre completo (h1), nombre de usuario con @, bio de 2 líneas - Lista de metadatos: organización (🏢), ubicación (📍), web (🔗), correo (📧) Cada item usa <dt> y <dd> dentro de un <dl> o <ul> - Contadores de seguidores/seguidos con <a> (valor + label) - Lista de tecnologías como tags/badges CSS requerido: - Variables CSS para todos los valores reutilizables - Tipografía: usar clamp() para los tamaños - Hover en el avatar: sutil scale + sombra - Hover en los contadores: underline + color change - El nombre de usuario en color secundario - Los badges de tecnología con colores distintos por categoría - Responsive: en móvil ocupa el 100% del ancho; en tablet/escritorio max-width: 296px - Todo con BEM o similar nomenclatura consistente