Apuntes DAM
Tema 2 de 9LMSGI · 1º DAM/DAW

Lenguajes de Marcas

Lenguajes de Marcas para la Web

Aprende HTML para estructurar contenido, CSS para diseñar y JavaScript para interactividad en páginas web.

60 min lecturaIntermedioRevisado el

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

OrganismoNombre completoTecnologías que gestiona
W3CWorld Wide Web ConsortiumHTML, CSS, XML, SVG, accesibilidad (WCAG)
WHATWGWeb Hypertext Application Technology Working GroupHTML Living Standard (el HTML5 moderno)
ECMAEuropean Computer Manufacturers AssociationECMAScript (JavaScript), JSON
IETFInternet Engineering Task ForceHTTP, 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:

estructura-basica.html
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

<header> — Encabezado: logo, título, menú principal
<nav> — Barra de navegación (enlaces a secciones)
<main>
<article> — Contenido principal autónomo
<section> — Sección temática
<aside>
Barra lateral, anuncios, info extra
<footer> — Pie: copyright, contacto, enlaces legales
EtiquetaSignificado semánticoUso típico
<header>Encabezado de página o secciónLogo, título, menú principal
<nav>Bloque de navegaciónMenús, migas de pan, paginación
<main>Contenido principal único de la páginaSolo puede haber uno por página
<article>Contenido autónomo y reutilizableEntradas de blog, noticias, tarjetas
<section>Sección temática con cabeceraCapítulos, bloques de contenido
<aside>Contenido relacionado pero secundarioBarra lateral, publicidad, tips
<footer>Pie de página o secciónCopyright, contacto, sitemap
<div>Contenedor genérico sin semánticaSolo cuando ninguna etiqueta semántica encaja
<span>Contenedor inline sin semánticaPara aplicar estilos a fragmentos de texto

¿Por qué importa la semántica?

Usar <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

<p>

Este es un párrafo normal. Es el bloque de texto más básico.

<strong>Texto de gran importancia — semántico, negrita por defecto
<em>Texto con énfasis — semántico, cursiva por defecto
<b>Texto en negrita — solo visual, sin semántica extra
<i>Texto en cursiva — solo visual (o términos técnicos/extranjeros)
<u>Texto subrayado — úsalo con cuidado (se confunde con enlace)
<mark>Texto resaltado — como un rotulador amarillo
<del>Texto eliminado — precio tachado, cambio de contenido
<ins>Texto insertado — contrario de <del>
<small>Texto pequeño — letra pequeña legal, notas al pie
<sup><sub>x2 + H2O — superíndice y subíndice
<code>console.log("hola") — fragmento de código inline
<blockquote>
"La web es para todos." — Tim Berners-Lee
<pre>
function hola() {
  return "Mantiene los espacios y saltos";
}

Semántica vs. presentación en texto

Prefiere siempre <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).

enlaces-imagenes.html
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

El atributo 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

  1. Diseñar
  2. Programar
  3. 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

<caption> — Leyenda de la tabla (mejora accesibilidad)
<th> Lenguaje<th> Tipo<th> Año
HTMLMarcas1991
CSSEstilos1996
JavaScriptProgramación1995
<tfoot> — Pie de tabla: totales, notas al pie

Estructura: <table> > <thead> + <tbody> + <tfoot> > <tr> > <th> / <td>

tabla-completa.html
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

formulario-completo.html
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 correctamente
  • disabled / 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.

AtributoDescripciónEjemplo
idIdentificador único en el documento<div id="menu-principal">
classUna o varias clases para CSS/JS<p class="destacado grande">
styleEstilos CSS inline (evitar en prod.)<h1 style="color: red">
titleTooltip al pasar el ratón<abbr title="HyperText Markup">HTML</abbr>
langIdioma del elemento<p lang="en">Hello world</p>
hiddenOculta el elemento del documento<div hidden>No visible</div>
tabindexOrden de tabulación con el teclado<button tabindex="1">
contenteditablePermite editar el contenido<div contenteditable="true">
draggablePermite 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">
Learn HTML – Full Tutorial for Beginners — freeCodeCamp.org

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>

AtributoValorEfecto
controlsbooleanoMuestra los controles nativos (play, volumen, progreso)
autoplaybooleanoReproduce automáticamente al cargar (requiere muted)
mutedbooleanoSilencia el vídeo (necesario para autoplay)
loopbooleanoRepite el vídeo indefinidamente
posterURLImagen de portada antes de reproducir
preloadnone / metadata / autoControla cuánto se descarga antes de reproducir
width / heightpíxelesDimensiones del reproductor
playsinlinebooleanoEn iOS: reproduce dentro de la página, no a pantalla completa
video.html
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).

audio.html
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.

iframe.html
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

Nunca incrustes iframes de fuentes desconocidas sin el atributo 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.

imagenes-responsivas.html
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?▾
HTML (HyperText Markup Language) es el lenguaje estándar para crear páginas web. Define la estructura y el contenido de los documentos mediante etiquetas.
¿Cuál es la diferencia entre HTML y CSS?▾
HTML define qué es cada parte del contenido (estructura), mientras que CSS define cómo se ve (presentación). Son complementarios y siempre trabajan juntos.
¿Qué aporta JavaScript? (abierto por defecto con open)▴
JavaScript añade interactividad, lógica y comportamiento dinámico a las páginas web. Es el único lenguaje de programación que los navegadores ejecutan de forma nativa.

Haz clic en los títulos — funciona sin JavaScript

details-summary.html
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.

dialog.html
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)

progress-meter.html
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

datalist.html
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>

output.html
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.

SVG inline
canvas-svg.html
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

head-completo.html
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?

Cuando alguien comparte tu URL en WhatsApp, Twitter o LinkedIn, estas plataformas leen los meta tags 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

<div onclick="comprar()">Comprar</div>

✗ Un usuario de teclado no puede activarlo

<img src="logo.png">

✗ Sin alt: un lector de pantalla dirá "imagen"

<a href="#">Haz clic aquí</a>

✗ "Haz clic aquí" no describe el destino

✓ Accesible

<button type="button" onclick="comprar()">Comprar</button>

✓ El botón es activable con Enter/Espacio

<img src="logo.png" alt="Logo de TechShop">

✓ El lector dice "Logo de TechShop"

<a href="/productos">Ver catálogo de productos</a>

✓ 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.

aria.html
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

accesibilidad-completa.html
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)

El estándar internacional WCAG 2.1 organiza la accesibilidad en 4 principios recordados como POUR: Perceptible (el contenido debe ser perceptible por todos los sentidos), Operable (navegable con teclado, sin límites de tiempo), Understandable/Comprensible (texto claro, errores descriptivos) y Robusto (compatible con tecnologías asistivas actuales y futuras). Los niveles son A (mínimo), AA (estándar para webs públicas) y AAA (óptimo).


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.

data-attributes.html
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>
HTML5: etiquetas de audio y video — iDESWEB UA


Versiones de HTML

VersiónAñoCaracterísticas claveEstado
HTML 4.011999Primera versión madura. Frames (obsoleto), tablas para layoutObsoleto
XHTML 1.02000HTML reescrito como XML. Sintaxis estricta: todo en minúsculas, cierre obligatorioLegacy
HTML52014Etiquetas semánticas, canvas, video, audio, APIs web, formularios enriquecidosEstándar actual
HTML Living Standard2019–hoyEvolución continua sin versiones fijas. Mantenido por WHATWGVigente

XHTML — Reglas estrictas

xhtml.html
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

html5.html
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?

Siempre <!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

Título de la página

Un párrafo con texto normal sin ningún estilo aplicado.

Un enlace azul subrayado

✓ Con CSS

Título de la página

Un párrafo con tipografía, color y espaciado cuidados.

Un enlace con estilo personalizado

Sintaxis 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

h1, h2 {
color: #1e40af;/* propiedad: valor */
font-size: 2rem;
margin-bottom: 1.5rem;
font-weight: bold;
}
h1, h2

Selector(es) — ¿A qué elementos se aplica?

color

Propiedad — ¿Qué se cambia?

#1e40af

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

nav a {
  color: white;
  text-decoration: none;
}

Selector descendiente — <a> dentro de <nav>, a cualquier nivel.

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)

SelectorEjemploSelecciona…Especificidad
Universal* { }Todos los elementos0,0,0
Tipop { }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
Atributoinput[type='text']Inputs de tipo texto0,1,0
Descendientenav aTodos los <a> dentro de <nav>0,0,2
Hijo directoul > liSolo <li> hijos directos de <ul>0,0,2
Hermano adyacenteh2 + p<p> inmediatamente después de <h2>0,0,2
Pseudoclasea:hoverEnlace al pasar el ratón0,1,1
Pseudoelementop::first-linePrimera línea de un párrafo0,0,2

La especificidad (specificity): quién gana cuando hay conflicto

Cuando dos reglas apuntan al mismo elemento, gana la de mayor especificidad. Se calcula con tres números (ID, Clases/Pseudoclases, Tipo): #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

color: red
color: #3b82f6
color: rgb(34,197,94)
color: hsl(271,81%,56%)
rgba(239,68,68,0.4)
oklch(0.7 0.2 120)

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.

✗ Evitar

CSS Inline — atributo style

inline.html
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. -->
Solo si necesario

CSS Interno — etiqueta <style>

interno.html
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. -->
✓ Siempre en producción

CSS Externo — archivo .css separado

externo.html
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

margin: 24px
border: 6px
padding: 20px
content
width × height
Content

El texto, imagen u otros elementos que contiene la caja

Padding

Espacio interno entre el contenido y el borde

Border

Línea visible o invisible que rodea el elemento

Margin

Espacio externo que separa la caja de otras cajas

box-model.css
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

Todos los frameworks modernos (Bootstrap, Tailwind, etc.) aplican 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.
Learn CSS in 20 Minutes — Web Dev Simplified

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 →

A
B
C
D
→

flex-direction: column — eje principal vertical ↓

A
B
C

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

A
B
C

center

A
B
C

flex-end

A
B
C

stretch (por defecto)

A
B
C

baseline

A
B
C

flex-wrap, flex-grow y flex-shrink

flex-wrap: nowrap (por defecto) — los ítems se comprimen

Item largo 1
Item 2
Item largo 3
Item 4
Item 5

flex-wrap: wrap — los ítems saltan a la siguiente línea

Item largo 1
Item 2
Item largo 3
Item 4
Item 5

flex-grow — reparto del espacio sobrante

grow: 1
grow: 2
grow: 1

El ítem con grow:2 recibe el doble del espacio libre

flexbox.css
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

celda 1
celda 2
celda 3
celda 4
celda 5
celda 6
celda 7
celda 8

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

sidebar fijo
contenido
col

grid-template-columns: 1fr 2fr 1fr — columna central doble de ancho

sidebar fijo
contenido
col

grid-template-columns: 200px 1fr — sidebar fijo + contenido flexible

sidebar fijo
contenido

grid-template-columns: repeat(4, 1fr) — 4 columnas iguales

sidebar fijo
contenido
col
col

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

header — ocupa toda la fila (col 1 + col 2)
sidebar
200px fijo
main
1fr — espacio restante
footer — ocupa toda la fila
grid-layout.css
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

CriterioFlexboxGrid
Dimensiones1D — fila o columna2D — filas Y columnas
Ideal para…Componentes: navbar, botones, chips, listasLayouts: páginas, secciones, dashboards
Control del espacioLos ítems deciden su tamañoEl contenedor decide el layout
Orden visualFácil con orderFácil con grid-area y posicionamiento
Soporte100% navegadores modernos100% navegadores modernos
Ejemplo típicodisplay:flex en nav, card, formdisplay:grid en body layout, galería

Regla práctica: no es un concurso

Flexbox y Grid no se excluyen. En un layout real se usan juntos: Grid para el layout general de la página y Flexbox para los componentes internos. Una navbar puede ser un ítem de Grid que internamente usa Flexbox para alinear su logo y menú.


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

0px
📱 Base (móvil)Mobile first: los estilos base se escriben sin media query
640px
📱 SM — Tablet pequeña@media (min-width: 640px)
768px
📓 MD — Tablet@media (min-width: 768px)
1024px
💻 LG — Escritorio@media (min-width: 1024px)
1280px
🖥️ XL — Escritorio grande@media (min-width: 1280px)
1536px
🖥️ 2XL — Pantalla UHD@media (min-width: 1536px)
responsive.css
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)

Tarjeta 1
Tarjeta 2
Tarjeta 3

Tablet (2 cols)

Tarjeta 1
Tarjeta 2
Tarjeta 3

Escritorio (3 cols)

Tarjeta 1
Tarjeta 2
Tarjeta 3

El viewport meta tag es indispensable para el responsive

Sin <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

variables-css.css
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

transitions.css
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

Hola!

skeleton

animaciones.css
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

Activo
Inactivo
Pendiente

Separador de texto (líneas con ::before y ::after)

o continúa con

Tooltip puro CSS con ::before y ::after

Este es el tooltip ← hecho con ::before y ::after en CSS puro

Comillas decorativas en blockquote

"La web es para todos. No para unos pocos."— Tim Berners-Lee
pseudoelementos.css
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

sidebar: 240px
contenido: calc(100% - 240px)

padding: calc(var(--espacio-4) * 2)

padding = calc(1rem × 2) = 2rem en cada lado

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

funciones-css.css
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

selectores-modernos.css
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.

container-queries.css
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

AspectoMedia QueryContainer Query
Se basa en…Tamaño del VIEWPORTTamaño del CONTENEDOR PADRE
Ideal para…Layout de página generalComponentes reutilizables
ContextoGlobal — siempre igualContextual — depende de dónde se usa
SoporteUniversalTodos los navegadores modernos (2023)
Ejemplo@media (min-width: 768px)@container (min-width: 400px)
¡Guía de CSS Flexbox para aprender de forma sencilla! — midulive


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

bem-ejemplo.html
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>
bem-estilos.css
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

Además de BEM, sigue estas prácticas: agrupa el CSS por componente (no por propiedades), evita selectores con más de 2 niveles de anidamiento, nunca uses !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

Los errores más frecuentes son: etiqueta </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

Ejemplo Guiado
Fácil

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

Ejercicio Práctico
Medio

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

Ejemplo Guiado
Medio

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

Ejercicio Práctico
Difícil

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.

Ejemplo Guiado
Medio

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

Ejercicio Práctico
Difícil

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.

Ejercicio Práctico
Difícil

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)

Ejemplo Guiado
Difícil

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

Ejercicio Práctico
Difícil

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

Ejemplo Guiado
Medio

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.

Ejercicio Práctico
Medio

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

¿Has encontrado un error, algo desactualizado o una explicación que no se entiende? Avísanos y lo corregimos.

¿Has terminado este tema?

Crea una cuenta gratis para guardar qué temas has terminado, subir de nivel y ganar medallas.

Guardar mi progreso