APLICAWEB 4to/Propuesta Intensiva 8 Semanas/Anexos/Banco de Live Coding
20 retos · Unidad 1 · Práctica 2
20

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.

Cómo se evalúa: el docente elige uno al azar en la defensa oral de Unidad 1. No busca sintaxis memorizada: busca que sepas qué cambiar, dónde y por qué, explicándolo mientras lo escribes. La sección «Se evalúa» de cada reto lista los puntos que se revisan y «En tu P2» te dice en qué parte de tu archivo está.
Tip: las referencias de línea corresponden a tu 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.
Unidad 1

HTML5 semántico + Tailwind CSS + Git/Render (Semanas 1-2)

01

Agregar la sección semántica «Testimonios» y enlazarla desde el nav

⏱ 6-8 minSemana 1-2

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

ANTES — index.html
<!-- ú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>
DESPUÉS — index.html
<!-- ú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

tu-portafolio.onrender.com
Sobre míSkillsProyectosContacto

No existe la sección Testimonios

tu-portafolio.onrender.com
Sobre míSkillsProyectosContactoTestimonios
"Excelente predisposición."
— Docente
"Muy responsable."
— Compañero
SE EVALÚA
  • El id de la sección coincide letra por letra con el href="#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.

02

Convertir las «Cajas» sueltas en una sección semántica

⏱ 4-5 minSemana 2

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

ANTES — index.html
<!-- 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>
DESPUÉS — index.html
<!-- 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

tu-portafolio.onrender.com
Caja 1
Caja 2

Sin id → no se puede enlazar (#) ni tiene región semántica

tu-portafolio.onrender.com
section#destacados
Caja 1
Caja 2
SE EVALÚA
  • Envuelve el bloque en <section> con un id descriptivo
  • 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í».

03

Completar el <p> vacío de «Sobre mí»

⏱ 2-3 minSemana 2

Consigna del docente: “En la segunda columna de Sobre mí quedó un <p> sin texto. Dale contenido real y un estilo acorde.”

ANTES — index.html
<!-- p vacío: ocupa lugar en el DOM pero no dice nada -->
<p class="text-slate-500 text-sm"></p>
DESPUÉS — index.html
<!-- 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

tu-portafolio.onrender.com

Caja gris con un hueco vacío

tu-portafolio.onrender.com

"Aprender haciendo, un commit a la vez."

SE EVALÚA
  • 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.

04

Corregir el alt de la foto de perfil

⏱ 3 minSemana 2

Consigna del docente: “Un lector de pantalla lee el alt de tu foto. Fíjate qué dice ahora y corrígelo.”

ANTES — index.html
<!-- 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">
DESPUÉS — index.html
<!-- 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

tu-portafolio.onrender.com

🔊 “Imagen: Rounded corners”

tu-portafolio.onrender.com

🔊 “Imagen: Foto de perfil de Prado Diaz Bernardo”

SE EVALÚA
  • Ubica el alt y explica que hoy describe estilo, no contenido
  • El texto nuevo es descriptivo (no “imagen” ni vacío)
  • No cambia src ni class: el problema era solo el alt

📍 En tu P2: línea 88 (y también la línea 177, la foto del bloque de perfil suelto).

05

Agregar una quinta tarjeta a la grilla de Skills

⏱ 3-4 minSemana 2

Consigna del docente: “Tu grilla tiene HTML5, Tailwind CSS, Git & GitHub y MySQL. Agrega Django con el mismo aspecto.”

ANTES — index.html
<!-- ú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>
DESPUÉS — index.html
<!-- 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

tu-portafolio.onrender.com
HTML5
Tailwind
Git
MySQL
tu-portafolio.onrender.com
HTML5
Tailwind
Git
MySQL
Django ✓
SE EVALÚA
  • Copia las clases existentes en vez de inventar unas nuevas (consistencia)
  • Pega la tarjeta dentro del <div class="grid ..."> correcto
  • Cambia solo h3 y p

📍 En tu P2: líneas 104-138. Ojo: hay una tarjeta (MySQL) con un comentario que dice “tercera” aunque es la cuarta.

06

Mover el breakpoint de 3 columnas de md: a lg:

⏱ 3-5 minSemana 2

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

ANTES — index.html
<!-- md: activa 3 columnas desde 768px (tablet) -->
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-5">
DESPUÉS — index.html
<!-- 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)

tu-portafolio.onrender.com
HTML5
Tailwind
Git

md: activo desde 768px → 3 columnas

tu-portafolio.onrender.com
HTML5
Tailwind
Git

lg: aún no activo a 800px → sigue en 2 columnas (por sm:)

SE EVALÚA
  • 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.

07

Agregar una segunda tarjeta a «Proyectos»oficial · guion de defensa

⏱ 4-5 minSemana 2

Consigna del docente: “Solo tienes el Gestor de Tareas Web. Agrega un segundo proyecto reutilizando exactamente el mismo estilo.”

ANTES — index.html
<!-- ú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>
DESPUÉS — index.html
<!-- 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

tu-portafolio.onrender.com
Gestor de Tareas Web
Ver repositorio →
tu-portafolio.onrender.com
Gestor de Tareas Web
Ver repositorio →
Portafolio Personal
Ver repositorio →
SE EVALÚA
  • Duplica el <article> con todas las clases (incluido hover:-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).

08

Integrar la tarjeta de perfil «flotante» dentro de «Sobre mí»

⏱ 6-8 minSemana 2

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

ANTES — index.html
<!-- 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>
DESPUÉS — index.html
<!-- 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

tu-portafolio.onrender.com
…footer / fin de la página…
Prado Diaz Bernardo
Computer Science Engineer
ID: #84920
tu-portafolio.onrender.com
Sobre mí
Prado Diaz Bernardo
Estudiante de Desarrollo Web
UTH · APLICAWEB 4to
SE EVALÚA
  • Explica que un bloque fuera de cualquier <section> queda sin contexto
  • Traduce los textos y reemplaza el dato inventado por uno real
  • Corrige el alt para que describa la foto, no el rol

📍 En tu P2: líneas 172-185, justo después de la sección Proyectos.

09

Agregar un botón «Cancelar» con sus propios estados

⏱ 5 minSemana 2

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

ANTES — index.html
<!-- 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>
DESPUÉS — index.html
<!-- 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

tu-portafolio.onrender.com
Enviar
tu-portafolio.onrender.com
EnviarCancelar ✓
SE EVALÚA
  • Usa type="button" y explica que un botón dentro de un <form> es submit por defecto
  • Agrega hover: y focus: propios
  • Comprueba en vivo que Cancelar no valida ni envía

📍 En tu P2: líneas 254-261, el botón del footer #contacto.

10

Dejar el header fijo al hacer scroll (sticky)

⏱ 3 minSemana 2

Consigna del docente: “Quiero que el menú quede visible arriba mientras se baja por la página.”

ANTES — index.html
<!-- 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">
DESPUÉS — index.html
<!-- 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

tu-portafolio.onrender.com

…Sobre mí (texto)…

Skills (tarjetas)

El menú ya no se ve: se fue con el scroll

tu-portafolio.onrender.com
Sobre mí · Skills · Proyectos · Contacto

Skills (tarjetas)

El menú sigue visible ✓

SE EVALÚA
  • Agrega sticky top-0 y sabe qué hace cada una
  • Agrega z-10 y 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>.

11

Hacer la foto de perfil responsiva (mobile-first)

⏱ 5 minSemana 2

Consigna del docente: “En un celular de 320px tu foto se sale de su tarjeta. Arréglalo pensando primero en celular.”

ANTES — index.html
<!-- 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">
DESPUÉS — index.html
<!-- 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

tu-portafolio.onrender.com

La foto se sale de la tarjeta

tu-portafolio.onrender.com

La foto se adapta a la tarjeta ✓

SE EVALÚA
  • Explica mobile-first: la clase sin prefijo vale para todo, el prefijo (sm:) se suma después
  • Reemplaza w-64 por w-full como base
  • Reserva sm:w-64 para pantallas más grandes

📍 En tu P2: línea 88, el <img> de la columna 2 de #sobre-mi.

12

Suavizar el hover del nav con una transición

⏱ 2-3 minSemana 2

Consigna del docente: “Al pasar el mouse el color cambia de golpe. Hazlo suave.”

ANTES — index.html
<!-- solo hover: el cambio de color es instantáneo -->
<li><a class="hover:text-emerald-400" href="#sobre-mi">Sobre mí</a></li>
DESPUÉS — index.html
<!-- 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í»

tu-portafolio.onrender.com
Sobre míSkills

Cambia en 0 ms (salto seco)

tu-portafolio.onrender.com
Sobre míSkills

Cambia gradualmente (≈150 ms) ✓

SE EVALÚA
  • Agrega transition-colors al mismo elemento que tiene el hover:
  • Lo aplica a los 4 enlaces del nav, no solo a uno

📍 En tu P2: líneas 35-41, los cuatro <a> del nav.

13

Cambiar el color de acento de emerald a indigo

⏱ 5 minSemana 2

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

ANTES — index.html
<!-- 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 ...">
DESPUÉS — index.html
<!-- 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

tu-portafolio.onrender.com
SkillsEnviar
tu-portafolio.onrender.com
SkillsEnviar
SE EVALÚA
  • Encuentra todas las apariciones (nav, botón, focus:ring de los inputs, enlace de Proyectos)
  • Conserva los números de intensidad (400/600/700)
  • Puede explicar por qué bg-emerald-300 de las Cajas no forma parte del acento

📍 En tu P2: busca “emerald” con Ctrl+F: aparece en el nav, Proyectos, inputs y botón.

14

El campo «Nombre» acepta enviarse vacío: agregar required

⏱ 3-4 minSemana 1-2

Consigna del docente: “Deja el nombre vacío y envía el formulario. Se envía. ¿Por qué, si ya tiene minlength="2"?”

ANTES — index.html
<!-- input de nombre solo con minlength; no tiene required -->
<input type="text" id="nombre" name="nombre" minlength="2" placeholder="Tu nombre">
DESPUÉS — index.html
<!-- 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

tu-portafolio.onrender.com
Tu nombre

El formulario se envía vacío, sin aviso

tu-portafolio.onrender.com
Tu nombre
⚠ Completa este campo
SE EVALÚA
  • Explica que minlength no se activa si el campo está vacío; para eso existe required
  • 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).

15

Poner un tope al nombre con maxlength

⏱ 2-3 minSemana 1-2

Consigna del docente: “El nombre tiene mínimo de 2 letras pero nadie te impide escribir 500. Ponele un máximo razonable.”

ANTES — index.html
<!-- solo tiene el piso (mínimo) -->
<input type="text" id="nombre" name="nombre" required minlength="2" placeholder="Tu nombre">
DESPUÉS — index.html
<!-- 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

tu-portafolio.onrender.com
Bernardo Prado Diaz Bernardo Prado Diaz Bernardo Prado Diaz Bernardo Prado Diaz…

Sigue aceptando caracteres sin límite

tu-portafolio.onrender.com
Bernardo Prado Diaz Bernardo Prado Diaz Bernardo Prado

Se detiene a los 60 caracteres ✓

SE EVALÚA
  • Distingue minlength (piso) de maxlength (techo)
  • Elige un número razonable y lo justifica
  • Deja required y minlength intactos

📍 En tu P2: línea 203-212, mismo input del reto 14. El textarea de Mensaje ya tiene minlength="10" maxlength="500".

16

Explicar (sin tocar código) por qué el label del Correo está bien vinculado

⏱ 2 minSemana 1

Consigna del docente: “No cambies nada. Solo explícame cómo se relacionan ese label y ese input y qué gana el usuario.”

LO QUE TIENES (correcto)
<!-- 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 SE ROMPIERA (hipotético)
<!-- 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»

Correo vinculado
Correo
tu@correo.com

Clic en “Correo” → el input recibe el foco ✓

Correo con for≠id
Correo
tu@correo.com

Clic en “Correo” → no pasa nada (vínculo roto)

SE EVALÚA
  • Compara for con id y 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.

17

Corregir el contraste del placeholder (Lighthouse)

⏱ 4-5 minSemana 2

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

ANTES — index.html
<!-- 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">
DESPUÉS — index.html
<!-- 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

tu-portafolio.onrender.com
Tu nombre

Accessibility: Background and foreground colors do not have a sufficient contrast ratio (≈3.1:1)

tu-portafolio.onrender.com
Tu nombre

✓ Contraste suficiente (≈5.7:1)

SE EVALÚA
  • 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.

18

Agregar estado focus: a los enlaces del nav

⏱ 3-4 minSemana 2

Consigna del docente: “Navega tu menú solo con la tecla Tab. ¿Ves dónde estás parado? Arréglalo.”

ANTES — index.html
<!-- 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>
DESPUÉS — index.html
<!-- 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

tu-portafolio.onrender.com
Sobre míSkillsProyectos

Con Tab: no se ve cuál está activo

tu-portafolio.onrender.com
Sobre míSkillsProyectos

Con Tab: aro visible ✓

SE EVALÚA
  • Agrega focus: (no solo hover:) y lo demuestra pulsando Tab, no con el mouse
  • Explica que quien navega con teclado depende del foco visible
  • Menciona que focus:outline-none solo es válido si se repone con focus:ring

📍 En tu P2: líneas 35-41. Los inputs y el botón del formulario sí tienen focus; el nav no.

19

Versionar el cambio con Git y comprobar el redeploy en Render

⏱ 4-5 minSemana 1

Consigna del docente: “Ya hiciste un cambio en tu index.html. Súbelo con Git y muéstrame que Render lo publicó solo.”

ANTES — terminal
# muestra qué archivos cambiaron y siguen sin guardarse en un commit
$ git status
DESPUÉS — terminal
# 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 push

Vista — página de Render tras el push

Terminal + Render (antes)

git status

modified: index.html

tu-portafolio.onrender.com
Proyectos: 1 tarjeta

Terminal + Render (después)

✓ Deploy live (commit “Agregar segunda tarjeta…”)

tu-portafolio.onrender.com
Proyectos: 2 tarjetas

SE EVALÚA
  • Ejecuta add → commit → push en 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.

20

Cambiar el layout de Skills de grid a flex-wrap

⏱ 5-6 minSemana 2

Consigna del docente: “Convierte la grilla de Skills a flexbox de forma que las tarjetas salten de línea solas cuando no entren.”

ANTES — index.html
<!-- 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">
DESPUÉS — index.html
<!-- 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

tu-portafolio.onrender.com
HTML5
Tailwind
Git
MySQL

Columnas decididas por breakpoint

tu-portafolio.onrender.com
HTML5
Tailwind
Git
MySQL

Saltan de línea según el espacio ✓

SE EVALÚA
  • Quita grid y todas las grid-cols-* y pone flex flex-wrap
  • Explica la diferencia: grid decide columnas por breakpoint; flex-wrap deja que el espacio decida
  • Sabe por qué hace falta min-w o un ancho en las tarjetas (sin él se encogen al contenido)

📍 En tu P2: línea 104, el div de la grilla de #skills.