APLICAWEB 4to/Propuesta Intensiva 8 Semanas/Unidad 1/Semana 2
4 hrs guiadas + 1 hr autónoma
semana
02/08

Tailwind CSS responsivo y portafolio terminado

La semana pasada construiste el esqueleto HTML5 de tu portafolio, sin ningún estilo. Esta semana le vas a dar diseño real con Tailwind CSS —un framework "utility-first" que se instala con una sola línea, sin configurar nada— y vas a dejarlo mobile-first: que se vea bien primero en un celular y después en pantallas más grandes. Al final lo vas a publicar en un repositorio y un sitio de Render nuevos (u1s2-…), ya con el diseño terminado; el de la Semana 1 se queda como evidencia.

Entregable: el repositorio u1s2-apellidopaterno-apellidomaterno-nombres y su sitio en https://u1s2-apellidopaterno-apellidomaterno-nombres.onrender.com, ahora con diseño responsivo probado en móvil y escritorio, y una auditoría de Lighthouse con accesibilidad ≥ 90.
01

Utility-first CSS: por qué Tailwind y cómo leer sus clases

⏱ 0:00–0:40práctica guiada

CSS "tradicional" te hace inventar un nombre de clase (.tarjeta-proyecto) y después escribir sus reglas en otro archivo. Tailwind invierte esa lógica: te da cientos de clases ya hechas —una por cada propiedad CSS— y las combinas directo en el HTML. No hay que inventar nombres ni saltar de archivo.

01

Comparación: el mismo botón, dos formas

Mismo resultado visual, dos formas de llegar. Con Tailwind, la clase es el estilo: no hace falta ir a otro archivo a buscar qué hace .btn-primario.

CSS tradicional
/* estilos.css: hay que inventar el nombre y escribir las reglas */
/* .btn-primario: selector que agrupa todas las reglas de este botón */
.btn-primario {
  /* background-color: color de fondo del botón */
  background-color: #059669;
  /* color: color del texto dentro del botón */
  color: white;
  /* padding: relleno interno vertical y horizontal */
  padding: 0.5rem 1.25rem;
  /* border-radius: redondea las esquinas del botón */
  border-radius: 0.5rem;
  /* font-weight: grosor de la letra (600 = semi-negrita) */
  font-weight: 600;
/* cierre de la regla .btn-primario */
}
/* :hover: reglas que solo se aplican al pasar el mouse por encima */
.btn-primario:hover {
  /* background-color: fondo más oscuro solo en hover */
  background-color: #047857;
/* cierre de la regla :hover */
}

<!-- index.html: solo el nombre de la clase, el estilo vive en el CSS de arriba -->
<button class="btn-primario">Enviar</button>
Tailwind (utility-first)
<!-- index.html: el estilo vive en las clases, no hay CSS aparte -->
<!-- bg-emerald-600: fondo verde · hover:bg-emerald-700: se oscurece al pasar el mouse · text-white: texto blanco -->
<!-- px-5 py-2: relleno horizontal/vertical · rounded-lg: esquinas redondeadas · font-semibold: negrita media -->
<button class="bg-emerald-600 hover:bg-emerald-700 text-white px-5 py-2 rounded-lg font-semibold">
  <!-- texto visible del botón -->
  Enviar
<!-- cierre de button -->
</button>
02

Cómo se lee cada clase

Cada clase de Tailwind hace una sola cosa. Uniendo varias armas el diseño completo:

·

bg-emerald-600 → color de fondo verde, tono 600 (escala de 50 a 950, más alto = más oscuro)

text-white → color de texto blanco

px-5 py-2 → relleno horizontal (px) de 1.25rem y vertical (py) de 0.5rem

rounded-lg → esquinas redondeadas, tamaño "large" de la escala predefinida

font-semibold → grosor de fuente 600

03

Prefijos: estado y tamaño de pantalla

hover:bg-emerald-700 → esa regla solo se aplica al pasar el mouse. md:flex-row → esa regla solo se aplica desde el breakpoint md (768px) hacia arriba. Vas a usar ambos patrones en los pasos siguientes.

TIP

No hace falta memorizar las clases. Mientras escribes, la documentación oficial tiene un buscador por propiedad CSS (ej. busca "padding" y te muestra p-*, px-*, py-*).

02

Configurar el CDN de Tailwind y probar el breakpoint md:

⏱ 0:40–1:10práctica guiada

Para esta unidad vas a usar el Play CDN de Tailwind: una sola etiqueta <script>, sin instalar Node.js ni configurar nada. Es la forma oficial de probar Tailwind rápido; más adelante en el curso, con Django, se instala distinto.

01

Agregar el script al <head>

Abre tu index.html de la semana pasada y agrega esta línea antes del cierre de </head>:

index.html — dentro de <head>
  <!-- Play CDN: descarga Tailwind y genera las clases que uses, sin build step -->
  <script src="https://cdn.tailwindcss.com"></script>
<!-- cierre de head (esta línea ya la tenías: el script va justo ARRIBA de ella) -->
</head>
02

Probar que está funcionando

Con Live Server corriendo, agrega temporalmente class="bg-emerald-300 p-6" a tu <body> y guarda. Si el fondo cambia de color, Tailwind está activo. Saca esa clase de prueba antes de seguir.

03

Mobile-first: cómo funcionan los breakpoints

Una clase sin prefijo (ej. flex-col) aplica siempre, empezando por pantallas chicas. Un prefijo como md: agrega o reemplaza esa regla solo desde 768px de ancho en adelante. Por eso se diseña primero pensando en celular, y se "corrige" hacia arriba con los prefijos.

prueba de breakpoint (pégalo temporalmente dentro de <body>)
<!-- div contenedor: flex flex-col apila las cajas en columna en celular -->
<!-- md:flex-row: desde 768px cambia a fila · gap-4: separación entre cajas -->
<div class="flex flex-col md:flex-row gap-4">
  <!-- primera caja: fondo verde claro y relleno interno -->
  <div class="bg-emerald-200 p-4">Caja 1</div>
  <!-- segunda caja: mismo patrón, un tono de verde más oscuro -->
  <div class="bg-emerald-300 p-4">Caja 2</div>
<!-- cierre del div contenedor -->
</div>

Pega ese bloque de prueba en el <body>, abre las DevTools (F12), activa el ícono de celular/tablet (Ctrl+Shift+M) y cambia el ancho: vas a ver las cajas apilarse y luego ponerse en fila al cruzar 768px. Borra el bloque de prueba cuando lo entiendas.

OJO

El Play CDN muestra en la consola del navegador un aviso de que "no se recomienda para producción". Para esta unidad es la forma oficial y correcta de trabajar: no compila nada, así que Render lo sirve igual que HTML plano. Lo vas a reemplazar por una instalación real más adelante en el curso.

03

Rediseño guiado: header, secciones y tarjetas de proyectos

⏱ 1:10–3:00práctica guiada

Vas a reescribir el contenido de tu <body> agregando clases de Tailwind a la estructura semántica que ya tenías. La estructura (header, nav, main, section, article, footer) no cambia — solo le agregas class.

01

Header + nav: apilado en celular, en fila en escritorio

Fondo oscuro, texto claro, y la navegación que pasa de columna a fila en el breakpoint md.

En index.html selecciona todo tu <header> (desde su comentario de arriba hasta </header>), bórralo y pega:

index.html — REEMPLAZA tu <header>
  <!-- header: fondo oscuro, texto claro · flex-col apila en celular · md:flex-row lo pone en fila desde 768px · px/py: relleno -->
  <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">
    <!-- div: agrupa el nombre y el subtítulo para que viajen juntos -->
    <div>
      <!-- text-2xl: tamaño de letra en celular · md:text-3xl: más grande desde 768px · font-bold: negrita -->
      <h1 class="text-2xl md:text-3xl font-bold">[Tu Nombre]</h1>
      <!-- text-slate-300: gris claro para diferenciarlo del título · mt-1: margen chico arriba -->
      <p class="text-slate-300 mt-1">[Estudiante de Desarrollo Web · UTH]</p>
    <!-- cierre del div de nombre/subtítulo -->
    </div>
    <!-- nav: agrupa los enlaces de navegación interna -->
    <nav>
      <!-- flex: enlaces en fila · gap-5: separación · flex-wrap: bajan de línea si no caben · text-sm font-semibold: estilo de menú -->
      <ul class="flex gap-5 flex-wrap text-sm font-semibold">
        <!-- cada enlace cambia a verde (hover:text-emerald-400) al pasar el mouse; py-2 inline-block agranda el área para tocarlo -->
        <li><a class="inline-block py-2 hover:text-emerald-400" href="#sobre-mi">Sobre mí</a></li>
        <!-- mismo patrón de clase, salta a la sección "skills" -->
        <li><a class="inline-block py-2 hover:text-emerald-400" href="#skills">Skills</a></li>
        <!-- salta a la nueva sección "proyectos" que agregas en este paso -->
        <li><a class="inline-block py-2 hover:text-emerald-400" href="#proyectos">Proyectos</a></li>
        <!-- salta al footer con id="contacto" -->
        <li><a class="inline-block py-2 hover:text-emerald-400" href="#contacto">Contacto</a></li>
      <!-- cierre de la lista de enlaces -->
      </ul>
    <!-- cierre de nav -->
    </nav>
  <!-- cierre de header -->
  </header>
02

Sobre mí: una columna en celular, dos en escritorio

Usa grid en vez de flex: es más cómodo cuando las columnas deben ocupar un ancho parejo.

Dentro de <main>, reemplaza la <section id="sobre-mi"> completa (con su comentario) por:

index.html — REEMPLAZA tu <section id="sobre-mi">
    <!-- section "sobre-mi": max-w-5xl mx-auto centra y limita el ancho · grid de 1 columna en celular y 2 desde 768px -->
    <section id="sobre-mi" class="max-w-5xl mx-auto px-6 py-14 grid grid-cols-1 md:grid-cols-2 gap-8 items-center">
      <!-- primera columna: título y texto de presentación -->
      <div>
        <!-- text-2xl font-bold: título de sección · mb-3: margen abajo antes del texto -->
        <h2 class="text-2xl font-bold mb-3">Sobre mí</h2>
        <!-- text-slate-600: gris oscuro para el cuerpo de texto · leading-relaxed: más espacio entre líneas -->
        <p class="text-slate-600 leading-relaxed">[2-3 frases: quién eres, qué estás estudiando, qué te interesa del desarrollo web.]</p>
      <!-- cierre de la primera columna -->
      </div>
      <!-- segunda columna: fondo gris muy claro, esquinas redondeadas, relleno y borde sutil (aquí va una foto, ícono o dato) -->
      <div class="bg-slate-50 rounded-2xl p-6 border border-slate-200">
        <!-- text-slate-600 text-sm: texto gris y chico, de relleno hasta que agregues contenido real -->
        <p class="text-slate-600 text-sm">[Foto, ícono o dato destacado]</p>
      <!-- cierre de la segunda columna -->
      </div>
    <!-- cierre de la sección "sobre-mi" -->
    </section>
03

Skills: grilla que crece con la pantalla

Tres pasos de columnas: 1 en celular, 2 en tablet, 3 en escritorio.

También dentro de <main>, reemplaza la <section id="skills"> completa por:

index.html — REEMPLAZA tu <section id="skills">
    <!-- section "skills": bg-slate-50 la separa visualmente de "sobre-mi" · py-14: relleno vertical -->
    <section id="skills" class="bg-slate-50 py-14">
      <!-- div interno: max-w-5xl mx-auto centra y limita el ancho, igual que en "sobre-mi" -->
      <div class="max-w-5xl mx-auto px-6">
        <!-- título de la sección, mismo estilo que "Sobre mí" -->
        <h2 class="text-2xl font-bold mb-6">Skills</h2>
        <!-- grilla: 1 columna en celular, 2 en tablet (sm), 3 en escritorio (md) · gap-5: separación entre tarjetas -->
        <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-5">
          <!-- tarjeta: fondo blanco, borde, esquinas redondeadas, relleno y sombra que crece al pasar el mouse -->
          <article class="bg-white rounded-xl border border-slate-200 p-5 shadow-sm hover:shadow-md transition">
            <!-- font-semibold: nombre de la skill en semi-negrita · mb-1: margen chico abajo -->
            <h3 class="font-semibold mb-1">HTML5</h3>
            <!-- text-sm text-slate-600: descripción breve, más chica y gris que el título -->
            <p class="text-sm text-slate-600">Estructura semántica de páginas web.</p>
          <!-- cierre de la primera tarjeta -->
          </article>
          <!-- segunda tarjeta: mismas clases exactas, solo cambia el contenido -->
          <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">Tailwind CSS</h3>
            <!-- descripción breve -->
            <p class="text-sm text-slate-600">Estilos responsivos utility-first.</p>
          <!-- cierre de la segunda tarjeta -->
          </article>
          <!-- tercera tarjeta: mismo patrón otra vez -->
          <article class="bg-white rounded-xl border border-slate-200 p-5 shadow-sm hover:shadow-md transition">
            <!-- nombre de la skill; &amp; se ve como el símbolo & -->
            <h3 class="font-semibold mb-1">Git &amp; GitHub</h3>
            <!-- descripción breve -->
            <p class="text-sm text-slate-600">Control de versiones y despliegue.</p>
          <!-- cierre de la tercera tarjeta -->
          </article>
        <!-- cierre de la grilla de skills -->
        </div>
      <!-- cierre del div interno -->
      </div>
    <!-- cierre de la sección "skills" -->
    </section>
04

Proyectos: tarjetas con hover

Es una sección nueva (agrégala si todavía no la tenías). Cada proyecto es un <article> dentro de <section id="proyectos">, con una transición suave al pasar el mouse.

Pégala dentro de <main>, justo debajo de la sección skills y antes de </main>:

index.html — sección NUEVA dentro de <main>
    <!-- section "proyectos": mismo contenedor centrado que las secciones anteriores -->
    <section id="proyectos" class="max-w-5xl mx-auto px-6 py-14">
      <!-- título de sección, mismo estilo que "Sobre mí" y "Skills" -->
      <h2 class="text-2xl font-bold mb-6">Proyectos</h2>
      <!-- grilla: 1 columna en celular, 2 desde 768px · gap-6: separación entre tarjetas -->
      <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
        <!-- tarjeta de proyecto: sombra que crece y la tarjeta "flota" (hover:-translate-y-1) al pasar el mouse · transition: suaviza -->
        <article class="bg-white rounded-2xl border border-slate-200 p-6 shadow-sm hover:shadow-lg hover:-translate-y-1 transition">
          <!-- text-lg font-bold: nombre del proyecto · mb-2: margen abajo -->
          <h3 class="text-lg font-bold mb-2">[Nombre del proyecto]</h3>
          <!-- text-slate-600 text-sm: descripción breve · mb-4: margen antes del enlace -->
          <p class="text-slate-600 text-sm mb-4">[Qué resuelve, en una frase.]</p>
          <!-- enlace verde que se subraya al pasar el mouse; cambia "#" por la URL de tu repo · inline-block py-2: área cómoda para tocar -->
          <a href="#" class="inline-block py-2 text-emerald-700 font-semibold text-sm hover:underline">Ver repositorio →</a>
        <!-- cierre de la tarjeta de proyecto -->
        </article>
      <!-- cierre de la grilla de proyectos -->
      </div>
    <!-- cierre de la sección "proyectos" -->
    </section>
TIP

Guarda seguido y mira Live Server en una ventana angosta primero (simulando celular) y después maximizada. Si algo se ve mal en el medio, ese es el punto donde te falta un prefijo md: o sm:.

04

Formulario de contacto estilizado + estados focus: y hover:

⏱ 3:00–3:40práctica guiada

El formulario y su validación HTML5 (required, type="email") ya los hiciste la semana pasada — no se tocan. Ahora le agregas clases para que se vea prolijo y responda visualmente al usar el teclado o el mouse.

Selecciona tu <footer id="contacto"> de la Semana 1 (con su formulario), bórralo y pega este. Los atributos de validación son los mismos; cada etiqueta va en una sola línea para que ningún comentario quede dentro de ella.

index.html — REEMPLAZA tu <footer id="contacto">
  <!-- footer "contacto": mismo tono oscuro que el header, para cerrar la página con el mismo color -->
  <footer id="contacto" class="bg-slate-900 text-white py-14">
    <!-- max-w-xl mx-auto: contenedor más angosto que las otras secciones, cómodo para un formulario -->
    <div class="max-w-xl mx-auto px-6">
      <!-- título de sección, mismo estilo que las anteriores -->
      <h2 class="text-2xl font-bold mb-6">Contacto</h2>
      <!-- form: space-y-4 agrega separación vertical automática entre cada div hijo -->
      <form class="space-y-4">
        <!-- div: agrupa la etiqueta y el campo de "Nombre" -->
        <div>
          <!-- block: el label ocupa su propia línea · text-sm font-semibold mb-1: letra chica, semi-negrita, margen abajo -->
          <label for="nombre" class="block text-sm font-semibold mb-1">Nombre</label>
          <!-- mismos atributos y validación de la Semana 1 · clases: campo oscuro de ancho completo con aro verde (focus:ring-2) al enfocarlo -->
          <input type="text" id="nombre" name="nombre" required minlength="2" placeholder="Tu nombre" class="w-full rounded-lg bg-slate-800 border border-slate-600 px-4 py-2 text-white placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-emerald-400">
        <!-- cierre del div de "Nombre" -->
        </div>
        <!-- div: agrupa la etiqueta y el campo de "Correo" -->
        <div>
          <!-- etiqueta del correo, mismo estilo que la de "Nombre" -->
          <label for="correo" class="block text-sm font-semibold mb-1">Correo</label>
          <!-- type="email": exige formato usuario@dominio · mismas clases visuales y de foco que "Nombre" -->
          <input type="email" id="correo" name="correo" required placeholder="tu@correo.com" class="w-full rounded-lg bg-slate-800 border border-slate-600 px-4 py-2 text-white placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-emerald-400">
        <!-- cierre del div de "Correo" -->
        </div>
        <!-- div: agrupa la etiqueta y el campo de "Mensaje" -->
        <div>
          <!-- etiqueta del mensaje, mismo estilo -->
          <label for="mensaje" class="block text-sm font-semibold mb-1">Mensaje</label>
          <!-- textarea: entre 10 y 500 caracteres, 4 líneas visibles · resize-none: el usuario no puede estirarlo · mismas clases de foco -->
          <textarea id="mensaje" name="mensaje" required minlength="10" maxlength="500" rows="4" class="w-full rounded-lg bg-slate-800 border border-slate-600 px-4 py-2 text-white placeholder-slate-400 resize-none focus:outline-none focus:ring-2 focus:ring-emerald-400"></textarea>
        <!-- cierre del div de "Mensaje" -->
        </div>
        <!-- botón verde (tono 700, el 600 no alcanza el contraste mínimo con texto blanco) que se oscurece al pasar el mouse · focus:ring-offset-2 separa el aro de foco del fondo oscuro -->
        <button type="submit" class="bg-emerald-700 hover:bg-emerald-800 transition text-white font-semibold px-6 py-2.5 rounded-lg focus:outline-none focus:ring-2 focus:ring-emerald-400 focus:ring-offset-2 focus:ring-offset-slate-900">Enviar</button>
      <!-- cierre del form -->
      </form>
      <!-- &copy; genera el símbolo © · text-slate-400 text-sm: texto chico y gris claro sobre el fondo oscuro · mt-10: separación arriba -->
      <p class="text-slate-400 text-sm mt-10">&copy; 2026 [Tu Nombre]</p>
    <!-- cierre del div contenedor -->
    </div>
  <!-- cierre de footer -->
  </footer>

Así debe quedar tu index.html completo (incluye class="text-slate-900" en el <body>). Si algo no se ve igual, compáralo o reemplaza todo tu archivo por este:

index.html — archivo COMPLETO de la Semana 2 (para comparar)
<!-- DOCTYPE: le dice al navegador que interprete esto como HTML5 -->
<!DOCTYPE html>
<!-- html: la raíz del documento; lang="es" ayuda a lectores de pantalla y buscadores -->
<html lang="es">
<!-- head: metadatos que el navegador necesita pero que NO se ven en la página -->
<head>
  <!-- UTF-8: permite escribir tildes y ñ sin que se rompan -->
  <meta charset="UTF-8">
  <!-- viewport: hace que la página se adapte al ancho de un celular (indispensable para mobile-first) -->
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <!-- title: el texto que aparece en la pestaña del navegador -->
  <title>[Tu Nombre] · Portafolio</title>
  <!-- Play CDN: descarga Tailwind y genera las clases que uses, sin build step -->
  <script src="https://cdn.tailwindcss.com"></script>
<!-- cierre de head -->
</head>
<!-- body: text-slate-900 pone el texto general casi negro sobre el fondo blanco (buen contraste) -->
<body class="text-slate-900">

  <!-- header: fondo oscuro, texto claro · flex-col apila en celular · md:flex-row lo pone en fila desde 768px · px/py: relleno -->
  <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">
    <!-- div: agrupa el nombre y el subtítulo para que viajen juntos -->
    <div>
      <!-- text-2xl: tamaño de letra en celular · md:text-3xl: más grande desde 768px · font-bold: negrita -->
      <h1 class="text-2xl md:text-3xl font-bold">[Tu Nombre]</h1>
      <!-- text-slate-300: gris claro para diferenciarlo del título · mt-1: margen chico arriba -->
      <p class="text-slate-300 mt-1">[Estudiante de Desarrollo Web · UTH]</p>
    <!-- cierre del div de nombre/subtítulo -->
    </div>
    <!-- nav: agrupa los enlaces de navegación interna -->
    <nav>
      <!-- flex: enlaces en fila · gap-5: separación · flex-wrap: bajan de línea si no caben · text-sm font-semibold: estilo de menú -->
      <ul class="flex gap-5 flex-wrap text-sm font-semibold">
        <!-- cada enlace cambia a verde (hover:text-emerald-400) al pasar el mouse; py-2 inline-block agranda el área para tocarlo -->
        <li><a class="inline-block py-2 hover:text-emerald-400" href="#sobre-mi">Sobre mí</a></li>
        <!-- mismo patrón de clase, salta a la sección "skills" -->
        <li><a class="inline-block py-2 hover:text-emerald-400" href="#skills">Skills</a></li>
        <!-- salta a la nueva sección "proyectos" que agregas en este paso -->
        <li><a class="inline-block py-2 hover:text-emerald-400" href="#proyectos">Proyectos</a></li>
        <!-- salta al footer con id="contacto" -->
        <li><a class="inline-block py-2 hover:text-emerald-400" href="#contacto">Contacto</a></li>
      <!-- cierre de la lista de enlaces -->
      </ul>
    <!-- cierre de nav -->
    </nav>
  <!-- cierre de header -->
  </header>

  <!-- main: el contenido único de la página (las 3 secciones de abajo van DENTRO de main) -->
  <main>
    <!-- section "sobre-mi": max-w-5xl mx-auto centra y limita el ancho · grid de 1 columna en celular y 2 desde 768px -->
    <section id="sobre-mi" class="max-w-5xl mx-auto px-6 py-14 grid grid-cols-1 md:grid-cols-2 gap-8 items-center">
      <!-- primera columna: título y texto de presentación -->
      <div>
        <!-- text-2xl font-bold: título de sección · mb-3: margen abajo antes del texto -->
        <h2 class="text-2xl font-bold mb-3">Sobre mí</h2>
        <!-- text-slate-600: gris oscuro para el cuerpo de texto · leading-relaxed: más espacio entre líneas -->
        <p class="text-slate-600 leading-relaxed">[2-3 frases: quién eres, qué estás estudiando, qué te interesa del desarrollo web.]</p>
      <!-- cierre de la primera columna -->
      </div>
      <!-- segunda columna: fondo gris muy claro, esquinas redondeadas, relleno y borde sutil (aquí va una foto, ícono o dato) -->
      <div class="bg-slate-50 rounded-2xl p-6 border border-slate-200">
        <!-- text-slate-600 text-sm: texto gris y chico, de relleno hasta que agregues contenido real -->
        <p class="text-slate-600 text-sm">[Foto, ícono o dato destacado]</p>
      <!-- cierre de la segunda columna -->
      </div>
    <!-- cierre de la sección "sobre-mi" -->
    </section>

    <!-- section "skills": bg-slate-50 la separa visualmente de "sobre-mi" · py-14: relleno vertical -->
    <section id="skills" class="bg-slate-50 py-14">
      <!-- div interno: max-w-5xl mx-auto centra y limita el ancho, igual que en "sobre-mi" -->
      <div class="max-w-5xl mx-auto px-6">
        <!-- título de la sección, mismo estilo que "Sobre mí" -->
        <h2 class="text-2xl font-bold mb-6">Skills</h2>
        <!-- grilla: 1 columna en celular, 2 en tablet (sm), 3 en escritorio (md) · gap-5: separación entre tarjetas -->
        <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-5">
          <!-- tarjeta: fondo blanco, borde, esquinas redondeadas, relleno y sombra que crece al pasar el mouse -->
          <article class="bg-white rounded-xl border border-slate-200 p-5 shadow-sm hover:shadow-md transition">
            <!-- font-semibold: nombre de la skill en semi-negrita · mb-1: margen chico abajo -->
            <h3 class="font-semibold mb-1">HTML5</h3>
            <!-- text-sm text-slate-600: descripción breve, más chica y gris que el título -->
            <p class="text-sm text-slate-600">Estructura semántica de páginas web.</p>
          <!-- cierre de la primera tarjeta -->
          </article>
          <!-- segunda tarjeta: mismas clases exactas, solo cambia el contenido -->
          <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">Tailwind CSS</h3>
            <!-- descripción breve -->
            <p class="text-sm text-slate-600">Estilos responsivos utility-first.</p>
          <!-- cierre de la segunda tarjeta -->
          </article>
          <!-- tercera tarjeta: mismo patrón otra vez -->
          <article class="bg-white rounded-xl border border-slate-200 p-5 shadow-sm hover:shadow-md transition">
            <!-- nombre de la skill; &amp; se ve como el símbolo & -->
            <h3 class="font-semibold mb-1">Git &amp; GitHub</h3>
            <!-- descripción breve -->
            <p class="text-sm text-slate-600">Control de versiones y despliegue.</p>
          <!-- cierre de la tercera tarjeta -->
          </article>
        <!-- cierre de la grilla de skills -->
        </div>
      <!-- cierre del div interno -->
      </div>
    <!-- cierre de la sección "skills" -->
    </section>

    <!-- section "proyectos": mismo contenedor centrado que las secciones anteriores -->
    <section id="proyectos" class="max-w-5xl mx-auto px-6 py-14">
      <!-- título de sección, mismo estilo que "Sobre mí" y "Skills" -->
      <h2 class="text-2xl font-bold mb-6">Proyectos</h2>
      <!-- grilla: 1 columna en celular, 2 desde 768px · gap-6: separación entre tarjetas -->
      <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
        <!-- tarjeta de proyecto: sombra que crece y la tarjeta "flota" (hover:-translate-y-1) al pasar el mouse · transition: suaviza -->
        <article class="bg-white rounded-2xl border border-slate-200 p-6 shadow-sm hover:shadow-lg hover:-translate-y-1 transition">
          <!-- text-lg font-bold: nombre del proyecto · mb-2: margen abajo -->
          <h3 class="text-lg font-bold mb-2">[Nombre del proyecto]</h3>
          <!-- text-slate-600 text-sm: descripción breve · mb-4: margen antes del enlace -->
          <p class="text-slate-600 text-sm mb-4">[Qué resuelve, en una frase.]</p>
          <!-- enlace verde que se subraya al pasar el mouse; cambia "#" por la URL de tu repo · inline-block py-2: área cómoda para tocar -->
          <a href="#" class="inline-block py-2 text-emerald-700 font-semibold text-sm hover:underline">Ver repositorio →</a>
        <!-- cierre de la tarjeta de proyecto -->
        </article>
      <!-- cierre de la grilla de proyectos -->
      </div>
    <!-- cierre de la sección "proyectos" -->
    </section>
  <!-- cierre de main -->
  </main>

  <!-- footer "contacto": mismo tono oscuro que el header, para cerrar la página con el mismo color -->
  <footer id="contacto" class="bg-slate-900 text-white py-14">
    <!-- max-w-xl mx-auto: contenedor más angosto que las otras secciones, cómodo para un formulario -->
    <div class="max-w-xl mx-auto px-6">
      <!-- título de sección, mismo estilo que las anteriores -->
      <h2 class="text-2xl font-bold mb-6">Contacto</h2>
      <!-- form: space-y-4 agrega separación vertical automática entre cada div hijo -->
      <form class="space-y-4">
        <!-- div: agrupa la etiqueta y el campo de "Nombre" -->
        <div>
          <!-- block: el label ocupa su propia línea · text-sm font-semibold mb-1: letra chica, semi-negrita, margen abajo -->
          <label for="nombre" class="block text-sm font-semibold mb-1">Nombre</label>
          <!-- mismos atributos y validación de la Semana 1 · clases: campo oscuro de ancho completo con aro verde (focus:ring-2) al enfocarlo -->
          <input type="text" id="nombre" name="nombre" required minlength="2" placeholder="Tu nombre" class="w-full rounded-lg bg-slate-800 border border-slate-600 px-4 py-2 text-white placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-emerald-400">
        <!-- cierre del div de "Nombre" -->
        </div>
        <!-- div: agrupa la etiqueta y el campo de "Correo" -->
        <div>
          <!-- etiqueta del correo, mismo estilo que la de "Nombre" -->
          <label for="correo" class="block text-sm font-semibold mb-1">Correo</label>
          <!-- type="email": exige formato usuario@dominio · mismas clases visuales y de foco que "Nombre" -->
          <input type="email" id="correo" name="correo" required placeholder="tu@correo.com" class="w-full rounded-lg bg-slate-800 border border-slate-600 px-4 py-2 text-white placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-emerald-400">
        <!-- cierre del div de "Correo" -->
        </div>
        <!-- div: agrupa la etiqueta y el campo de "Mensaje" -->
        <div>
          <!-- etiqueta del mensaje, mismo estilo -->
          <label for="mensaje" class="block text-sm font-semibold mb-1">Mensaje</label>
          <!-- textarea: entre 10 y 500 caracteres, 4 líneas visibles · resize-none: el usuario no puede estirarlo · mismas clases de foco -->
          <textarea id="mensaje" name="mensaje" required minlength="10" maxlength="500" rows="4" class="w-full rounded-lg bg-slate-800 border border-slate-600 px-4 py-2 text-white placeholder-slate-400 resize-none focus:outline-none focus:ring-2 focus:ring-emerald-400"></textarea>
        <!-- cierre del div de "Mensaje" -->
        </div>
        <!-- botón verde (tono 700, el 600 no alcanza el contraste mínimo con texto blanco) que se oscurece al pasar el mouse · focus:ring-offset-2 separa el aro de foco del fondo oscuro -->
        <button type="submit" class="bg-emerald-700 hover:bg-emerald-800 transition text-white font-semibold px-6 py-2.5 rounded-lg focus:outline-none focus:ring-2 focus:ring-emerald-400 focus:ring-offset-2 focus:ring-offset-slate-900">Enviar</button>
      <!-- cierre del form -->
      </form>
      <!-- &copy; genera el símbolo © · text-slate-400 text-sm: texto chico y gris claro sobre el fondo oscuro · mt-10: separación arriba -->
      <p class="text-slate-400 text-sm mt-10">&copy; 2026 [Tu Nombre]</p>
    <!-- cierre del div contenedor -->
    </div>
  <!-- cierre de footer -->
  </footer>

<!-- cierre de body -->
</body>
<!-- cierre de html: fin del documento -->
</html>
01

Por qué focus: no es cosmético

Quien navega solo con teclado (Tab) necesita ver claramente qué campo está activo. focus:ring-2 dibuja un aro de color al enfocar cualquier campo — es accesibilidad, no decoración, y Lighthouse lo revisa en el paso 06.

02

Pruébalo

Con Live Server abierto, haz Tab hasta llegar al formulario: cada campo debe mostrar el aro verde al enfocarse. Pasa el mouse sobre "Enviar": el fondo debe oscurecerse suavemente.

05

Publicar en un repo y un Static Site nuevos (u1s2-…)

⏱ 3:40–4:00práctica guiada

Como en cada práctica, esta versión va en su propio repositorio y en su propio sitio de Render. Tu repo y tu sitio u1s1-… de la Semana 1 se quedan intactos como evidencia.

01

Crear el repositorio u1s2-… vacío en GitHub

Mismo procedimiento que la Semana 1, con el prefijo de esta práctica: u1s2-apellidopaterno-apellidomaterno-nombres (ej. u1s2-hernandez-nunez-maria-jose). En github.com → + → New repository → pega el nombre del generador de abajo → Public → sin README, .gitignore ni license → Create repository. Copia la URL que termina en .git.

La regla del nombre · siempre igual (repo de GitHub y servicio de Render)

prefijou1s2- apellido paternohernandez- apellido maternonunez- nombre(s)maria-jose
  • Prefijo de la práctica: u + número de unidad + s + número de semana. Esta práctica es u1s2.
  • Todo en minúsculas, sin acentos (á → a), la ñ se escribe n y los espacios se cambian por guion -.
  • Orden fijo: prefijo → apellido paterno → apellido materno → nombre(s). Si tienes dos nombres, van los dos.
  • El mismo nombre se usa en dos lugares: como Repository name en GitHub y como Name del servicio en Render (paso 05.3). Así tu URL queda https://u1s2-….onrender.com.
  • GitHub solo acepta letras sin acento, números, -, _ y . (máximo 100 caracteres). Por eso la regla quita acentos y espacios.
AlumnoNombre del repositorio y del servicio de Render
María José Hernández Núñezu1s2-hernandez-nunez-maria-jose
José Ángel De la Cruz Peñau1s2-de-la-cruz-pena-jose-angel
Ana López (un solo apellido)u1s2-lopez-ana
PrefijoPráctica del curso
u1s1Unidad 1 · Semana 1 — Sitio estático en Render
u1s2Unidad 1 · Semana 2 — Tailwind CSS (esta guía)
u2s3Unidad 2 · Semana 3 — Django + MySQL en Aiven
u2s4Unidad 2 · Semana 4 — Modelos y deploy en Render
u3s5Unidad 3 · Semana 5 — CRUD con ModelForms
u3s6Unidad 3 · Semana 6 — Autenticación y sesiones
u4s7Unidad 4 · Semana 7 — Pulido y defensa
u4s8Unidad 4 · Semana 8 — Deploy final

Genera tu nombre sin errores

Repositorio y servicio de Renderu1s2-hernandez-nunez-maria-jose

Tu repo quedará en https://github.com/TU_USUARIO/u1s2-hernandez-nunez-maria-jose y, con el mismo nombre en Render (paso 05.3), tu sitio en https://u1s2-hernandez-nunez-maria-jose.onrender.com. Los datos de ejemplo están precargados: escribe los tuyos y usa «Copiar nombre».

02

Guardar el trabajo y subirlo al repo nuevo

TERMINAL DE VS CODE — PowerShell · en tu carpeta portafolio
# Agrega todos los archivos modificados al stage
git add .
# Guarda la fotografía en el historial con un mensaje descriptivo
git commit -m "Rediseño responsivo con Tailwind CSS"
# Cambia "origin" al repo nuevo de esta práctica (set-url porque origin ya existe desde la Semana 1); pega TU URL
git remote set-url origin https://github.com/TU_USUARIO/u1s2-hernandez-nunez-maria-jose.git
# Confirma que ahora apunta al u1s2-… (sale dos veces: fetch y push)
git remote -v
# Sube la rama main al repo nuevo; -u la deja vinculada para que después baste con "git push"
git push -u origin main

Recarga la página del repo en GitHub: debe verse tu index.html. Todo tu historial de la Semana 1 viaja también al repo nuevo; el repo u1s1-… no cambia.

03

Crear el Static Site u1s2-… en Render

En dashboard.render.com → "New +" → "Static Site" → elige tu repo u1s2-… (si no aparece, "Configure account" y dale permiso a ese repo). En Name pega el mismo nombre del generador. Llena los mismos campos de la Semana 1:

RENDER — campos del formulario «New Static Site»
# Name: el mismo nombre de tu repo; con él se arma tu URL (https://u1s2-….onrender.com)
Name:              u1s2-hernandez-nunez-maria-jose
# Branch: la rama de tu repo que Render clona y despliega en cada push
Branch:            main
# Root Directory: vacío, porque index.html está en la raíz del repo
Root Directory:    (dejar vacío)
# Build Command: vacío; el Play CDN de Tailwind funciona en el navegador, no hay nada que compilar
Build Command:     (dejar vacío)
# Publish Directory: la carpeta que Render sirve tal cual; "." significa "la raíz del repo"
Publish Directory: .

Clic en "Create Static Site". En la pestaña "Events" o "Deploys" verás el deploy en Building y después en Live (normalmente menos de un minuto). Desde ahora, cada git push a main vuelve a publicar este sitio solo.

04

Verificar en la URL pública

Abre https://u1s2-hernandez-nunez-maria-jose.onrender.com (con tu nombre) y presiona Ctrl+F5 (recarga forzada, ignora caché). Confirma que el diseño nuevo aparece ahí, no solo en tu Live Server local. Si el nombre ya estaba ocupado, Render le agrega letras al final: usa la dirección exacta que aparece debajo del nombre del sitio.

OJO

Si la URL pública no muestra los cambios después de un minuto, revisa el log del deploy en Render: la causa más común es un error de tipeo en el <script src> del CDN de Tailwind que rompe la carga de la página.

06

Práctica autónoma — auditoría con Lighthouse

⏱ 1 hrpráctica autónoma

Lighthouse es la herramienta de auditoría automática integrada en Chrome DevTools. Revisa tu página real (la de Render, no la local) y te da un puntaje de 0 a 100 en accesibilidad, rendimiento y buenas prácticas, con una lista concreta de qué corregir.

01

Abrir Lighthouse

En Chrome, abre tu URL de Render. Presiona F12 para abrir DevTools, ve a la pestaña "Lighthouse" (si no la ves, búscala con las flechas » del panel). Marca al menos las categorías Accessibility y Performance, deja "Mode: Navigation" y "Device: Mobile", y presiona "Analyze page load".

02

Leer el reporte

Cada categoría muestra un círculo de color: verde (90-100), naranja (50-89), rojo (0-49). Debajo aparece la lista de "Opportunities" y "Diagnostics" — cada ítem señala exactamente qué elemento del HTML falla y por qué.

03

Corregir al menos 2 hallazgos

Los más comunes en un portafolio nuevo, y cómo se arreglan:

Hallazgo típicoCorrección
Contraste de color insuficiente Texto gris claro sobre fondo claro. Cambia a un tono más oscuro de la misma paleta (ej. text-slate-600 en vez de text-slate-300 sobre fondo blanco).
Imágenes sin atributo alt Si agregaste una <img>, todo <img> necesita alt="descripción breve" (o alt="" si es puramente decorativa).
Orden de encabezados salteado No puedes pasar de <h2> a <h4> sin un <h3> en el medio. Revisa que cada sección respete la jerarquía h1 → h2 → h3.
Área táctil de botones muy chica Enlaces o botones con poco padding en celular. Agrega py-2.5/px-5 como mínimo para que sean fáciles de tocar.
04

Corregir, hacer push y volver a auditar

Después de corregir, repite git add . && git commit -m "..." && git push, espera el redeploy y corre Lighthouse de nuevo sobre la URL de Render para confirmar que el puntaje de accesibilidad subió.

TIP

Guarda una captura de pantalla del reporte final de Lighthouse (con los puntajes visibles) — la vas a necesitar para la entrega y la presentación.

07

Entregar y presentar

Marca cada punto a medida que lo completas — queda guardado en este navegador.

0/6

Cómo subir la práctica

01

Actualizar el README

Edita el README.md que creaste la semana 1 para reflejar el avance:

README.md — archivo COMPLETO (reemplaza el de la Semana 1)
<!-- título del repo: cambia por tus nombres reales -->
# U1S2 — [Apellido Paterno] [Apellido Materno] [Nombre(s)]

<!-- sección con la URL pública de Render de esta práctica -->
## Demo en vivo
<!-- tu dirección real de Render -->
https://u1s2-hernandez-nunez-maria-jose.onrender.com

<!-- sección con el enlace al código -->
## Repositorio
<!-- tu dirección real de GitHub -->
https://github.com/tu-usuario/u1s2-hernandez-nunez-maria-jose

<!-- resumen de lo que hiciste esta semana -->
## Semana 2 — Tailwind CSS responsivo
<!-- primera línea del resumen -->
Rediseño completo con Tailwind CSS (Play CDN), mobile-first.
<!-- segunda línea: el puntaje que obtuviste en Lighthouse -->
Lighthouse Accessibility: [tu puntaje]/100.
TERMINAL DE VS CODE — PowerShell
# agrega solo el README modificado al área de preparación de Git
git add README.md
# crea el commit con un mensaje que explica el cambio
git commit -m "Actualizar README con avance de semana 2"
# sube el commit al repo u1s2-…; Render detecta el push y vuelve a publicar solo
git push
02

Entregar los mismos dos enlaces

La URL de Render (ya actualizada) y la de GitHub (con los nuevos commits). Pégalos como texto en la tarea de la Semana 2 del aula virtual, junto con la captura de pantalla del reporte de Lighthouse.

Cómo presentar en vivo — guion paso a paso

Un recorrido de ~5 minutos frente al profesor. Practícalo una vez antes de tu turno.

0:00

Abrir la URL de Render maximizada

Muestra el diseño terminado: header, sobre mí, skills, proyectos y contacto con estilos aplicados.

1:00

Achicar la ventana en vivo

Arrastra el borde de la ventana del navegador hasta un ancho de celular sin recargar la página: el layout debe reacomodarse solo (columnas apiladas, nav más angosto) — es la prueba de que es responsivo de verdad, no una imagen fija.

2:00

Mostrar los estados del formulario

Tab hasta un campo (aro de foco visible) y pasar el mouse sobre "Enviar" (cambio de color). Explica que esto es accesibilidad, no solo estética.

3:00

Mostrar el reporte de Lighthouse

Corre la auditoría en vivo (o muestra la captura guardada) y señala el puntaje de Accessibility ≥ 90.

4:00

Cerrar con el flujo completo

"Agrego clases de Tailwind en VS Code → git push → Render redespliega solo → audito con Lighthouse y corrijo lo que falta." Esa frase resume el ciclo de trabajo de esta semana.

08

Tabla de referencias — de dónde sale toda la información

Todo lo de esta guía viene de documentación oficial de cada herramienta, no de blogs de terceros.

HerramientaPara quéDónde
Tailwind CSS Documentación oficial de clases utility-first tailwindcss.com/docs
Tailwind Play CDN Cómo cargar Tailwind con una sola etiqueta <script>, sin build tailwindcss.com/docs/installation/play-cdn
Tailwind Responsive Design Cómo funcionan los breakpoints (sm, md, lg) y el enfoque mobile-first tailwindcss.com/docs/responsive-design
Tailwind Hover/Focus States Prefijos de estado como hover: y focus: tailwindcss.com/docs/hover-focus-and-other-states
MDN: Media Queries Fundamento CSS nativo detrás de los breakpoints responsivos developer.mozilla.org — Using media queries
Lighthouse (Chrome DevTools) Auditoría de accesibilidad y rendimiento integrada en el navegador developer.chrome.com/docs/lighthouse
Render Documentación de despliegues automáticos (redeploy en cada push) render.com/docs/deploys