Banco de 20 Retos de Live Coding — Unidad 1
Los 20 retos parten de tu Práctica 2 (Unidad 1/DSM4-2da/index.html): tu header con nav, «Sobre mí», Skills, Proyectos, la tarjeta de perfil suelta, las Cajas y el formulario de contacto. Cada reto muestra el código ANTES y DESPUÉS y cómo se ve en el navegador, para que sepas qué cambio lograr y no solo qué escribir.
index.html tal como está hoy; si agregas o quitas líneas, se corren. Los ratios de contraste (≈) del reto 17 se calcularon con la fórmula de luminancia relativa de WCAG.
HTML5 semántico + Tailwind CSS + Git/Render (Semanas 1-2)
Agregar la sección semántica «Testimonios» y enlazarla desde el nav
Consigna del docente: “Tu nav tiene 4 enlaces. Agrega una sección Testimonios con un <article> adentro, enlázala desde el nav y hazla responsiva.”
<!-- último enlace del nav, el que ya existe -->
<li><a class="hover:text-emerald-400" href="#contacto">Contacto</a></li>
<!-- la sección de proyectos existente, sin Testimonios todavía -->
<section id="proyectos" class="max-w-5xl mx-auto px-6 py-14">...</section><!-- último enlace que ya existía, no se toca -->
<li><a class="hover:text-emerald-400" href="#contacto">Contacto</a></li>
<!-- li nuevo: el href debe ser IGUAL al id de la section de abajo -->
<li><a class="hover:text-emerald-400" href="#testimonios">Testimonios</a></li>
<!-- sección de proyectos existente, no se toca -->
<section id="proyectos" class="max-w-5xl mx-auto px-6 py-14">...</section>
<!-- section nueva: mismo contenedor centrado que las demás -->
<section id="testimonios" class="max-w-5xl mx-auto px-6 py-14">
<!-- h2 con el mismo estilo que Sobre mí, Skills y Proyectos -->
<h2 class="text-2xl font-bold mb-6">Testimonios</h2>
<!-- grid: 1 columna en celular, 2 desde 768px -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- article: el testimonio se entiende leído solo, por eso no es un div -->
<article class="bg-white rounded-2xl border border-slate-200 p-6 shadow-sm">
<!-- p: la cita del testimonio -->
<p class="text-slate-600 text-sm mb-3">"Excelente predisposición para aprender."</p>
<!-- p: autor de la cita, en negrita -->
<p class="font-semibold text-sm">— Docente, UTH</p>
<!-- cierra el article -->
</article>
<!-- cierra el div de la grilla -->
</div>
<!-- cierra la section -->
</section>Vista en el navegador — nav y nueva sección
No existe la sección Testimonios
— Docente
— Compañero
- El
idde la sección coincide letra por letra con elhref="#testimonios" - Usa
<article>(no<div>) y explica por qué: el testimonio se lee solo, fuera de contexto - Reutiliza las clases de contenedor de las otras secciones (
max-w-5xl mx-auto px-6 py-14)
📍 En tu P2: el nav está en las líneas 26-41; las secciones existentes van de la línea 69 a la 167.
Convertir las «Cajas» sueltas en una sección semántica
Consigna del docente: “Ese bloque de Caja 1 y Caja 2 está suelto en el body. Vuélvelo una sección con id propio, para que cuente como parte real de la estructura.”
<!-- div suelto: no pertenece a ninguna section y no tiene id -->
<div class="flex flex-col md:flex-row gap-4">
<!-- caja 1: solo un fondo celeste y relleno -->
<div class="bg-sky-500 p-4">Caja 1</div>
<!-- caja 2: solo un fondo verde y relleno -->
<div class="bg-emerald-300 p-4">Caja 2</div>
<!-- cierra el div suelto -->
</div><!-- section nueva: le da nombre (id) y significado al bloque -->
<section id="destacados" class="max-w-5xl mx-auto px-6 py-10">
<!-- el div de antes queda igual: columna en celular, fila desde md -->
<div class="flex flex-col md:flex-row gap-4">
<!-- caja 1: se le agregan esquinas redondeadas y texto blanco -->
<div class="bg-sky-500 p-4 rounded-lg text-white">Caja 1</div>
<!-- caja 2: esquinas redondeadas, mismo patrón -->
<div class="bg-emerald-300 p-4 rounded-lg">Caja 2</div>
<!-- cierra el div contenedor -->
</div>
<!-- cierra la section nueva -->
</section>Vista en el navegador — estructura del bloque
Sin id → no se puede enlazar (#) ni tiene región semántica
- Envuelve el bloque en
<section>con uniddescriptivo - Mantiene
flex flex-col md:flex-row: apila en celular, fila desde 768px - Explica que un
<div>no dice nada del contenido y una<section>sí
📍 En tu P2: líneas 55-63, entre el header y la sección «Sobre mí».
Completar el <p> vacío de «Sobre mí»
Consigna del docente: “En la segunda columna de Sobre mí quedó un <p> sin texto. Dale contenido real y un estilo acorde.”
<!-- p vacío: ocupa lugar en el DOM pero no dice nada -->
<p class="text-slate-500 text-sm"></p><!-- p con una cita personal real, en cursiva para que se lea destacada -->
<p class="text-slate-500 text-sm italic">"Aprender haciendo, un commit a la vez."</p>Vista en el navegador — tarjeta de la columna 2
Caja gris con un hueco vacío
"Aprender haciendo, un commit a la vez."
- Escribe texto real entre las etiquetas, no deja el elemento vacío
- Justifica
italic: es una cita, no texto de relleno - No toca las demás clases del párrafo
📍 En tu P2: línea 87, dentro de la segunda columna de #sobre-mi.
Corregir el alt de la foto de perfil
Consigna del docente: “Un lector de pantalla lee el alt de tu foto. Fíjate qué dice ahora y corrígelo.”
<!-- alt describe una clase CSS (rounded-lg), no la imagen -->
<img src="img/foto1.png" class="rounded-lg w-64 h-64 object-cover" alt="Rounded corners"><!-- alt describe QUÉ se ve en la foto, para quien no puede verla -->
<img src="img/foto1.png" class="rounded-lg w-64 h-64 object-cover" alt="Foto de perfil de Prado Diaz Bernardo">Vista — lo que anuncia un lector de pantalla
🔊 “Imagen: Rounded corners”
🔊 “Imagen: Foto de perfil de Prado Diaz Bernardo”
- Ubica el
alty explica que hoy describe estilo, no contenido - El texto nuevo es descriptivo (no “imagen” ni vacío)
- No cambia
srcniclass: el problema era solo elalt
📍 En tu P2: línea 88 (y también la línea 177, la foto del bloque de perfil suelto).
Agregar una quinta tarjeta a la grilla de Skills
Consigna del docente: “Tu grilla tiene HTML5, Tailwind CSS, Git & GitHub y MySQL. Agrega Django con el mismo aspecto.”
<!-- última tarjeta existente, la de MySQL -->
<article class="bg-white rounded-xl border border-slate-200 p-5 shadow-sm hover:shadow-md transition">
<!-- nombre de la skill -->
<h3 class="font-semibold mb-1">MySQL</h3>
<!-- descripción breve -->
<p class="text-sm text-slate-500">Manejador de Base de Datos</p>
<!-- cierra el article -->
</article><!-- tarjeta nueva: COPIA exacta de las clases de las otras cuatro -->
<article class="bg-white rounded-xl border border-slate-200 p-5 shadow-sm hover:shadow-md transition">
<!-- h3: solo cambia el nombre -->
<h3 class="font-semibold mb-1">Django</h3>
<!-- p: solo cambia la descripción -->
<p class="text-sm text-slate-500">Framework web en Python, patrón MTV.</p>
<!-- cierra el article; va dentro de la MISMA grilla, antes de cerrarla -->
</article>Vista en el navegador — grilla de Skills
- Copia las clases existentes en vez de inventar unas nuevas (consistencia)
- Pega la tarjeta dentro del
<div class="grid ...">correcto - Cambia solo
h3yp
📍 En tu P2: líneas 104-138. Ojo: hay una tarjeta (MySQL) con un comentario que dice “tercera” aunque es la cuarta.
Mover el breakpoint de 3 columnas de md: a lg:
Consigna del docente: “Tu grilla de Skills ya pasa a 3 columnas en tablet (768px). Haz que sean 3 columnas recién en escritorio (1024px) y explica qué se ve en una ventana de 800px.”
<!-- md: activa 3 columnas desde 768px (tablet) -->
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-5"><!-- lg: activa 3 columnas recién desde 1024px; sm:grid-cols-2 no se toca -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5">Vista — ventana de ~800px (tablet)
md: activo desde 768px → 3 columnas
lg: aún no activo a 800px → sigue en 2 columnas (por sm:)
- Solo cambia el prefijo (
md:→lg:), no la estructura - Explica que entre 768px y 1023px ahora se ven 2 columnas (por
sm:grid-cols-2), no 3 - Lo comprueba achicando y agrandando la ventana, sin recargar
📍 En tu P2: línea 104, el div de la grilla dentro de #skills.
Agregar una segunda tarjeta a «Proyectos»oficial · guion de defensa
Consigna del docente: “Solo tienes el Gestor de Tareas Web. Agrega un segundo proyecto reutilizando exactamente el mismo estilo.”
<!-- única tarjeta de proyecto actual -->
<article class="bg-white rounded-2xl border border-slate-200 p-6 shadow-sm hover:shadow-lg hover:-translate-y-1 transition">
<!-- nombre del proyecto -->
<h3 class="text-lg font-bold mb-2">Gestor de Tareas Web</h3>
<!-- enlace al repositorio -->
<a href="#" class="text-emerald-700 font-semibold text-sm hover:underline">Ver repositorio →</a>
<!-- cierra el article -->
</article><!-- tarjeta 2: mismas clases, incluido el hover que la levanta -->
<article class="bg-white rounded-2xl border border-slate-200 p-6 shadow-sm hover:shadow-lg hover:-translate-y-1 transition">
<!-- h3 con el nombre del proyecto nuevo -->
<h3 class="text-lg font-bold mb-2">Portafolio Personal</h3>
<!-- p con su descripción, mismo tamaño y color -->
<p class="text-slate-600 text-sm mb-4">Sitio propio con HTML5 semántico y Tailwind CSS.</p>
<!-- enlace: mismo estilo; cambia el href al repo real -->
<a href="#" class="text-emerald-700 font-semibold text-sm hover:underline">Ver repositorio →</a>
<!-- cierra el article; va dentro del mismo contenedor grid -->
</article>Vista en el navegador — sección Proyectos
Ver repositorio →
Ver repositorio →
Ver repositorio →
- Duplica el
<article>con todas las clases (incluidohover:-translate-y-1) - Cambia título, descripción y
href; no toca la estructura - Explica que reutilizar clases mantiene la consistencia visual
📍 En tu P2: líneas 146-167. Es el ejemplo oficial del guion de defensa de Unidad 1 (sumar además git push, ver reto 19).
Integrar la tarjeta de perfil «flotante» dentro de «Sobre mí»
Consigna del docente: “Al final de tu archivo hay una tarjeta de perfil suelta, en inglés, con un alt genérico. Muévela a #sobre-mi, tradúcela y ponele datos reales.”
<!-- bloque suelto: no está dentro de ninguna section -->
<div class="flex items-center space-x-4 p-4 bg-white rounded-xl shadow-md max-w-sm">
<!-- alt genérico y en inglés -->
<img class="w-16 h-16 rounded-full object-cover border-2 border-indigo-500" src="img/foto1.png" alt="Student profile photo" />
<!-- rol en inglés -->
<p class="text-sm font-medium text-indigo-600">Computer Science Engineer</p>
<!-- dato inventado -->
<p class="text-xs text-gray-500">ID: #84920</p>
<!-- cierra el div -->
</div><!-- ahora vive dentro de la columna 2 de #sobre-mi; mt-6 lo separa del texto de arriba -->
<div class="flex items-center space-x-4 p-4 bg-white rounded-xl shadow-md max-w-sm mt-6">
<!-- alt describe la foto real -->
<img class="w-16 h-16 rounded-full object-cover border-2 border-indigo-500" src="img/foto1.png" alt="Foto de perfil de Prado Diaz Bernardo" />
<!-- rol traducido y real -->
<p class="text-sm font-medium text-indigo-600">Estudiante de Desarrollo Web</p>
<!-- dato real en lugar del ID inventado -->
<p class="text-xs text-gray-500">UTH · APLICAWEB 4to</p>
<!-- cierra el div -->
</div>Vista en el navegador — posición y textos de la tarjeta
Computer Science Engineer
ID: #84920
Estudiante de Desarrollo Web
UTH · APLICAWEB 4to
- Explica que un bloque fuera de cualquier
<section>queda sin contexto - Traduce los textos y reemplaza el dato inventado por uno real
- Corrige el
altpara que describa la foto, no el rol
📍 En tu P2: líneas 172-185, justo después de la sección Proyectos.
Agregar un botón «Cancelar» con sus propios estados
Consigna del docente: “El formulario solo tiene Enviar. Agrega un botón Cancelar que no envíe el formulario y que tenga hover y focus propios.”
<!-- botón Enviar solo, sin nada al lado -->
<button type="submit" class="bg-emerald-600 hover:bg-emerald-700 transition text-white font-semibold px-6 py-2.5 rounded-lg">Enviar</button><!-- div: pone ambos botones en fila con separación -->
<div class="flex gap-3">
<!-- Enviar: sin cambios -->
<button type="submit" class="bg-emerald-600 hover:bg-emerald-700 transition text-white font-semibold px-6 py-2.5 rounded-lg">Enviar</button>
<!-- Cancelar: type="button" es lo crítico, evita que dispare el submit -->
<button type="button" class="border border-slate-600 hover:bg-slate-800 focus:outline-none focus:ring-2 focus:ring-slate-400 transition text-white font-semibold px-6 py-2.5 rounded-lg">Cancelar</button>
<!-- cierra el div -->
</div>Vista en el navegador — zona del botón
- Usa
type="button"y explica que un botón dentro de un<form>essubmitpor defecto - Agrega
hover:yfocus:propios - Comprueba en vivo que Cancelar no valida ni envía
📍 En tu P2: líneas 254-261, el botón del footer #contacto.
Dejar el header fijo al hacer scroll (sticky)
Consigna del docente: “Quiero que el menú quede visible arriba mientras se baja por la página.”
<!-- header normal: se va hacia arriba con el scroll -->
<header class="bg-slate-900 text-white flex flex-col gap-4 md:flex-row md:items-center md:justify-between px-6 py-6 md:px-10"><!-- sticky top-0 lo pega arriba al hacer scroll; z-10 lo deja por encima del contenido -->
<header class="bg-slate-900 text-white flex flex-col gap-4 md:flex-row md:items-center md:justify-between px-6 py-6 md:px-10 sticky top-0 z-10">Vista en el navegador — después de hacer scroll
…Sobre mí (texto)…
Skills (tarjetas)
El menú ya no se ve: se fue con el scroll
Skills (tarjetas)
El menú sigue visible ✓
- Agrega
sticky top-0y sabe qué hace cada una - Agrega
z-10y explica por qué (que el contenido no lo tape) - Lo prueba haciendo scroll en vivo
📍 En tu P2: líneas 18-20, la etiqueta <header>.
Hacer la foto de perfil responsiva (mobile-first)
Consigna del docente: “En un celular de 320px tu foto se sale de su tarjeta. Arréglalo pensando primero en celular.”
<!-- ancho y alto fijos de 256px en cualquier pantalla, incluso en celular chico -->
<img src="img/foto1.png" class="rounded-lg w-64 h-64 object-cover" alt="Foto de perfil"><!-- w-full: base para celular · max-w-xs: tope · sm:w-64: tamaño fijo recién desde 640px -->
<img src="img/foto1.png" class="rounded-lg w-full max-w-xs h-64 object-cover sm:w-64" alt="Foto de perfil">Vista — celular de 320px
La foto se sale de la tarjeta
La foto se adapta a la tarjeta ✓
- Explica mobile-first: la clase sin prefijo vale para todo, el prefijo (
sm:) se suma después - Reemplaza
w-64porw-fullcomo base - Reserva
sm:w-64para pantallas más grandes
📍 En tu P2: línea 88, el <img> de la columna 2 de #sobre-mi.
Suavizar el hover del nav con una transición
Consigna del docente: “Al pasar el mouse el color cambia de golpe. Hazlo suave.”
<!-- solo hover: el cambio de color es instantáneo -->
<li><a class="hover:text-emerald-400" href="#sobre-mi">Sobre mí</a></li><!-- transition-colors anima el cambio de color en lugar de aplicarlo de golpe -->
<li><a class="hover:text-emerald-400 transition-colors" href="#sobre-mi">Sobre mí</a></li>Vista — al pasar el mouse por «Sobre mí»
Cambia en 0 ms (salto seco)
Cambia gradualmente (≈150 ms) ✓
- Agrega
transition-colorsal mismo elemento que tiene elhover: - Lo aplica a los 4 enlaces del nav, no solo a uno
📍 En tu P2: líneas 35-41, los cuatro <a> del nav.
Cambiar el color de acento de emerald a indigo
Consigna del docente: “Cambia el color de acento del sitio a indigo en el nav y en el botón Enviar. Mantén los mismos números.”
<!-- nav: verde al pasar el mouse -->
<li><a class="hover:text-emerald-400" href="#skills">Skills</a></li>
<!-- botón: verde, hover más oscuro y aro de foco verde -->
<button type="submit" class="bg-emerald-600 hover:bg-emerald-700 focus:ring-emerald-400 ..."><!-- nav: mismo número (400), cambia solo el nombre del color -->
<li><a class="hover:text-indigo-400" href="#skills">Skills</a></li>
<!-- botón: 600, 700 y 400 se conservan; solo cambia emerald por indigo -->
<button type="submit" class="bg-indigo-600 hover:bg-indigo-700 focus:ring-indigo-400 ...">Vista en el navegador — nav y botón
- Encuentra todas las apariciones (nav, botón,
focus:ringde los inputs, enlace de Proyectos) - Conserva los números de intensidad (400/600/700)
- Puede explicar por qué
bg-emerald-300de las Cajas no forma parte del acento
📍 En tu P2: busca “emerald” con Ctrl+F: aparece en el nav, Proyectos, inputs y botón.
El campo «Nombre» acepta enviarse vacío: agregar required
Consigna del docente: “Deja el nombre vacío y envía el formulario. Se envía. ¿Por qué, si ya tiene minlength="2"?”
<!-- input de nombre solo con minlength; no tiene required -->
<input type="text" id="nombre" name="nombre" minlength="2" placeholder="Tu nombre"><!-- required obliga a que haya algo; minlength solo mide lo que ya se escribió -->
<input type="text" id="nombre" name="nombre" required minlength="2" placeholder="Tu nombre">Vista — al pulsar «Enviar» con el nombre vacío
El formulario se envía vacío, sin aviso
- Explica que
minlengthno se activa si el campo está vacío; para eso existerequired - Aclara que ambas validaciones las hace el navegador, sin JavaScript
- Prueba en vivo con el campo vacío y con 1 sola letra
📍 En tu P2: línea 203, el <input> de Nombre (Correo y Mensaje sí tienen required).
Poner un tope al nombre con maxlength
Consigna del docente: “El nombre tiene mínimo de 2 letras pero nadie te impide escribir 500. Ponele un máximo razonable.”
<!-- solo tiene el piso (mínimo) -->
<input type="text" id="nombre" name="nombre" required minlength="2" placeholder="Tu nombre"><!-- maxlength agrega el techo: el navegador no deja escribir más de 60 -->
<input type="text" id="nombre" name="nombre" required minlength="2" maxlength="60" placeholder="Tu nombre">Vista — al escribir un nombre muy largo
Sigue aceptando caracteres sin límite
Se detiene a los 60 caracteres ✓
- Distingue
minlength(piso) demaxlength(techo) - Elige un número razonable y lo justifica
- Deja
requiredyminlengthintactos
📍 En tu P2: línea 203-212, mismo input del reto 14. El textarea de Mensaje ya tiene minlength="10" maxlength="500".
Explicar (sin tocar código) por qué el label del Correo está bien vinculado
Consigna del docente: “No cambies nada. Solo explícame cómo se relacionan ese label y ese input y qué gana el usuario.”
<!-- el for del label es IGUAL al id del input: letra por letra -->
<label for="correo" class="block text-sm font-semibold mb-1">Correo</label>
<!-- el id coincide con el for de arriba; name es lo que viaja al enviar -->
<input type="email" id="correo" name="correo" required placeholder="tu@correo.com"><!-- si el for NO coincidiera (for="email"), el vínculo se rompería -->
<label for="email" class="block text-sm font-semibold mb-1">Correo</label>
<!-- el id sigue siendo correo: ya no hay pareja -->
<input type="email" id="correo" name="correo" required placeholder="tu@correo.com">Vista — hacer clic en el texto «Correo»
Clic en “Correo” → el input recibe el foco ✓
Clic en “Correo” → no pasa nada (vínculo roto)
- Compara
forconidy dice que deben ser idénticos - Beneficio 1: el clic en el texto enfoca el campo (zona de clic más grande)
- Beneficio 2: el lector de pantalla anuncia “Correo” al entrar al campo
📍 En tu P2: líneas 218-219. Aquí ANTES = lo que tienes (correcto) y DESPUÉS = el error hipotético a evitar.
Corregir el contraste del placeholder (Lighthouse)
Consigna del docente: “Corre Lighthouse en Accesibilidad. El texto de ejemplo dentro de los campos del formulario se lee mal sobre el fondo oscuro. Corrígelo.”
<!-- placeholder-slate-500 sobre bg-slate-800: contraste ≈ 3.1:1, menos del 4.5:1 exigido por WCAG AA -->
<input class="bg-slate-800 border border-slate-700 text-white placeholder-slate-500" placeholder="Tu nombre"><!-- placeholder-slate-400 sobre bg-slate-800: contraste ≈ 5.7:1, cumple AA -->
<input class="bg-slate-800 border border-slate-700 text-white placeholder-slate-400" placeholder="Tu nombre">Vista — reporte de Lighthouse y campo
Accessibility: Background and foreground colors do not have a sufficient contrast ratio (≈3.1:1)
✓ Contraste suficiente (≈5.7:1)
- Ubica el elemento señalado y explica qué es el contraste de color
- Cambia solo el tono (
500→400) y menciona el mínimo 4.5:1 de WCAG AA - Vuelve a correr Lighthouse para mostrar el cambio
📍 En tu P2: líneas 210-212, 227-229 y 247-249: los tres campos del formulario usan placeholder-slate-500. Los ratios (≈) se calcularon con la fórmula de luminancia de WCAG.
Agregar estado focus: a los enlaces del nav
Consigna del docente: “Navega tu menú solo con la tecla Tab. ¿Ves dónde estás parado? Arréglalo.”
<!-- solo hover: con el teclado no hay ninguna pista de dónde está el foco -->
<li><a class="hover:text-emerald-400" href="#skills">Skills</a></li><!-- focus:ring-2 dibuja un aro al llegar con Tab; rounded lo redondea -->
<li><a class="hover:text-emerald-400 focus:outline-none focus:ring-2 focus:ring-emerald-400 rounded" href="#skills">Skills</a></li>Vista — navegando con la tecla Tab
Con Tab: no se ve cuál está activo
Con Tab: aro visible ✓
- Agrega
focus:(no solohover:) y lo demuestra pulsando Tab, no con el mouse - Explica que quien navega con teclado depende del foco visible
- Menciona que
focus:outline-nonesolo es válido si se repone confocus:ring
📍 En tu P2: líneas 35-41. Los inputs y el botón del formulario sí tienen focus; el nav no.
Versionar el cambio con Git y comprobar el redeploy en Render
Consigna del docente: “Ya hiciste un cambio en tu index.html. Súbelo con Git y muéstrame que Render lo publicó solo.”
# muestra qué archivos cambiaron y siguen sin guardarse en un commit
$ git status# prepara TODOS los archivos modificados para el commit
$ git add .
# guarda la fotografía del proyecto con un mensaje descriptivo
$ git commit -m "Agregar segunda tarjeta en Proyectos"
# sube el commit a GitHub; eso dispara el redeploy automático en Render
$ git pushVista — página de Render tras el push
git status
modified: index.html
tu-portafolio.onrender.com
Proyectos: 1 tarjeta
✓ Deploy live (commit “Agregar segunda tarjeta…”)
tu-portafolio.onrender.com
Proyectos: 2 tarjetas
- Ejecuta
add→commit→pushen ese orden - El mensaje del commit describe el cambio, no dice “cambios” ni “asdf”
- Explica que el push a GitHub avisa a Render y este redespliega solo; refresca la URL para mostrarlo
📍 En tu P2: es el cierre de cualquier reto anterior. Se hace en la terminal de VS Code, dentro de la carpeta del proyecto.
Cambiar el layout de Skills de grid a flex-wrap
Consigna del docente: “Convierte la grilla de Skills a flexbox de forma que las tarjetas salten de línea solas cuando no entren.”
<!-- grid: columnas fijas por breakpoint (1, 2, 3) -->
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-5">
<!-- cada tarjeta ocupa lo que le da la columna -->
<article class="bg-white rounded-xl border border-slate-200 p-5"><!-- flex flex-wrap: las tarjetas fluyen en fila y saltan de línea si no entran -->
<div class="flex flex-wrap gap-5">
<!-- flex-1 reparte el espacio sobrante; min-w-[14rem] evita que se aplasten (repetir en las 5 tarjetas) -->
<article class="flex-1 min-w-[14rem] bg-white rounded-xl border border-slate-200 p-5">Vista — ventana de ~800px
Columnas decididas por breakpoint
Saltan de línea según el espacio ✓
- Quita
gridy todas lasgrid-cols-*y poneflex flex-wrap - Explica la diferencia: grid decide columnas por breakpoint; flex-wrap deja que el espacio decida
- Sabe por qué hace falta
min-wo un ancho en las tarjetas (sin él se encogen al contenido)
📍 En tu P2: línea 104, el div de la grilla de #skills.