Curso de CSS / proyecto final: un sitio adaptable con HTML y CSS
Puedes combinar HTML semántico, variables CSS compartidas, navegación con Flexbox y un diseño de tarjetas con Grid para construir un pequeño sitio web responsivo. Comienza con contenido legible y un diseño usable para pantallas estrechas, luego agrega ajustes para pantallas más anchas y estados de interacción visibles.
Define la página y las verificaciones de aceptación
Decide qué contenido y comportamiento necesita la página antes de aplicar el estilo. En pantallas estrechas, la navegación se ajusta cuando es necesario y las tarjetas forman una columna. Más espacio disponible permite dos o tres columnas. Limita el ancho de lectura general para que el texto no se vuelva excesivamente largo en pantallas anchas.
| Requisitos | Criterios de finalización |
|---|---|
| estructura del contenido | Un título principal, una frase introductoria, tres tarjetas y un pie de página se leen en orden. |
| pantalla estrecha | Los menús y las tarjetas son legibles sin necesidad de desplazamiento horizontal completo |
| pantalla ancha | Las tarjetas se muestran en múltiples columnas mientras se mantiene el ancho máximo del cuerpo |
| navegación con teclado | Aparece una marca de enfoque en los accesos directos al texto y a los enlaces de menú/movimiento. |
| Cambios en el contenido | Incluso si ingresas descripciones largas y cadenas sin espacios, el contenido crece sin quedar recortado por una altura fija. |
| movimiento disminuido | Permite moverse según sea necesario sin desplazamiento suave y transiciones decorativas. |
Este ejemplo es una pantalla estática y no hay enlaces en la propia tarjeta. El menú de introducción/curso y los enlaces de navegación grandes se mueven a secciones del mismo documento. Para ampliar tu lista de cursos reales, coloca la URL verificada del post en el título de la tarjeta. No es necesario agregar de manera forzada formularios sin usar o animaciones infinitas para completar este ejercicio.
Prepara archivos y HTML semántico
Crea index.html y style.css en la misma ubicación dentro de una carpeta vacía. Al crear el archivo, asegúrate de que no sea index.html.txt debido a la extensión oculta y guárdalo como UTF-8. La etiqueta link en index.html apunta a style.css en la misma carpeta. No se requiere instalación ni compilación separada.
learning-site/
index.html
style.css
Primero, guarda todo el HTML a continuación en index.html. Incluso si el CSS está vacío, el título, el enlace y el contenido de la tarjeta deben leerse en orden. header es el área de encabezado del sitio, nav es la navegación, main es el contenido principal y footer es el pie de página. Coloca el tema de estudio h2 y la tarjeta h3 debajo de h1 en el documento. Este HTML es un ejemplo de un archivo independiente, por lo que está separado de la estructura de la página que se adjunta directamente al cuerpo de WordPress.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>A Small Learning Site</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<a class="skip-link" href="#main">Skip to main content</a>
<header class="site-header">
<div class="container header-inner">
<p class="site-name">Learning Notes</p>
<nav aria-label="Main navigation">
<a href="#intro">About</a><a href="#lessons">Lessons</a>
</nav>
</div>
</header>
<main id="main" tabindex="-1">
<section id="intro" class="hero" aria-labelledby="intro-title">
<div class="container">
<h1 id="intro-title">Learn to Build Webpages Step by Step</h1>
<p>Structure content with HTML and adapt its appearance with CSS.</p>
<a class="button" href="#lessons">Explore the lessons</a>
</div>
</section>
<section id="lessons" class="lessons container" aria-labelledby="lessons-title">
<h2 id="lessons-title">Learning topics</h2>
<ul class="card-grid" role="list">
<li><article class="card"><h3>HTML structure</h3><p>Organize content with headings and paragraphs.</p></article></li>
<li><article class="card"><h3>CSS styles</h3><p>Adjust colors, fonts, and spacing.</p></article></li>
<li><article class="card"><h3>Responsive layout</h3><p>Arrange cards to fit the available width.</p></article></li>
</ul>
</section>
</main>
<footer class="site-footer"><div class="container"><p>Small learning site exercise</p></div></footer>
</body>
</html>
El acceso directo al texto principal está conectado al ID de main. tabindex="-1" permite que main reciba el enfoque sin agregarlo al orden normal de tabulación. Toca un menú o acceso directo para navegar a esa sección y asegúrate de que la navegación con teclado continúe desde el contenido principal. Para el propósito y la composición del acceso directo, consulta la técnica de acceso directo de texto de W3C.
Establece valores compartidos y ancho de contenido
Inicia style.css con la siguiente sección. Nombrar las variables de color según sus roles facilita cambiar el color de acento compartido. border-box incluye el padding y los bordes en el ancho declarado, lo que simplifica las dimensiones. Establece un diseño base que pueda acomodar contenido creciente en lugar de asignar dimensiones fijas en todas partes. Antes de cambiar los valores compartidos, distingue variables CSS: var(), alcance, herencia y valores de respaldo.
/* Shared values and base styles */
:root {
--ink: #183044;
--accent: #166b92;
--surface: #eef6fa;
--line: #c9dbe4;
--focus: #164f73;
}
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body { margin: 0; color: var(--ink); font-family: sans-serif; line-height: 1.6; }
a { color: var(--accent); text-underline-offset: .15em; }
:focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; }
.container { width: min(100% - 2rem, 68rem); margin-inline: auto; }
.container, .card { overflow-wrap: anywhere; }
El ancho del contenedor es el menor entre “el ancho disponible menos la suma de 2rem en ambos lados” y 68rem. Proporciona márgenes izquierdo y derecho para pantallas estrechas y ancho máximo para pantallas amplias. auto para margin-inline centra el área. overflow-wrap: anywhere permite que las cadenas largas sin espacios, como direcciones o identificadores, se dividan entre líneas si es necesario. Se debe hacer una distinción entre la práctica que solo aumenta la longitud de una oración general y la práctica que añade una palabra larga.
Si actualizas en esta etapa, se aplicarán los colores, la fuente predeterminada y el ancho del contenido, pero los encabezados y las tarjetas aún no se dispondrán en detalle. Si no hay ningún cambio, verifica la ruta del archivo antes de agregar la sección posterior. Los conceptos de tamaño fluido y ancho máximo están vinculados a los diseño fluido con anchuras relativas, min() y clamp().
Construye la navegación Flexbox y los caminos de enfoque
Agrega la siguiente sección bajo Reglas Comunes en style.css: La colocación entre el nombre del sitio y el menú se gestiona mediante el header-inner, y el espaciado dentro del menú se gestiona mediante el nav. Al separar la distancia entre las dos posiciones, puedes reducir la posibilidad de cambios no intencionados en el espaciado entre los enlaces del menú al cambiar el espaciado de todo el encabezado. Los principios básicos de la dirección del eje y la colocación de los elementos se pueden encontrar en fundamentos de Flexbox: contenedores, elementos y ejes.
/* Navigation and header */
.skip-link { position: absolute; left: 1rem; top: -6rem; padding: .5rem; background: white; }
.skip-link:focus { top: 1rem; z-index: 10; }
.site-header { border-bottom: 1px solid var(--line); }
.header-inner { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
gap: .5rem 1.5rem; padding-block: 1rem; }
.site-name { margin: 0; font-weight: 700; }
nav { display: flex; flex-wrap: wrap; gap: 1rem; }
flex-wrap coloca el elemento en la siguiente línea si no hay suficiente espacio. Tener un menú de dos líneas en una pantalla estrecha no es un fallo. Los criterios para juzgar son si hay enlaces superpuestos, truncados o no clicables. Conserva un orden de navegación coherente y evita usar order para invertir solo la presentación visual.
El atajo de texto normalmente se coloca fuera de la pantalla superior y se hace visible cuando recibe el enfoque. Ocultarlo con display:none no logra el mismo propósito porque ni siquiera recibe el enfoque del teclado. Al pulsar Tab por primera vez aparece el enlace de salto. Pulsa Enter y comprueba que la siguiente pulsación de Tab lleve al enlace grande en la introducción. El contorno de enfoque oscuro fue elegido para destacarse tanto del fondo blanco como del fondo de introducción.
Da estilo a la introducción fluida y los estados de los enlaces
Agregar las siguientes secciones completa el fondo de la introducción, el título y los enlaces de navegación grandes. La limitación del tamaño del título establece los valores mínimo, preferido y máximo. La fuente no se hace infinitamente más grande a medida que aumenta el ancho, y también se toma en cuenta la configuración del tamaño de la fuente, incluyendo el término rem. Se requiere una comprobación en pantalla separada para ver si realmente es fácil de leer cuando el usuario hace zoom.
/* Introduction and navigation link */
.hero { padding-block: clamp(3rem, 8vw, 6rem); background: var(--surface); }
.hero h1 { max-width: 18ch; margin-block: 0 1rem;
font-size: clamp(1.8rem, 1.2rem + 2vw, 3rem); line-height: 1.25; }
.hero p { max-width: 50ch; }
.button { display: inline-block; margin-top: .5rem; padding: .7rem 1rem;
border-radius: .4rem; background: var(--accent); color: white; text-decoration: none;
transition: background-color 180ms ease; }
.button:hover { background: #104d6a; }
El max-width: 18ch del encabezado utiliza el ancho del glifo “0” de la fuente; no garantiza 18 letras por línea. Su line-height controla las líneas de encabezado que se envuelven, mientras que el párrafo introductorio se limita a 50ch. Este ejemplo utiliza fuentes sans-serif del sistema y no descarga ninguna fuente externa.
Para enlaces más grandes, usa el elemento a porque te llevan a la sección de lecciones del mismo documento. Si la acción cambiara un estado sin navegar, sería adecuado un elemento button. Solo se usan transiciones de color de fondo al pasar el cursor, y no se requiere movimiento decorativo ni aumento de tamaño. El estado de enfoque puede confirmarse con un contorno común.
Construye tarjetas de cuadrícula que acomoden contenido
Agrega las siguientes secciones para completar tu tarjeta de curso y el pie de página. Elimina los márgenes y viñetas predeterminados de la lista, pero deja las estructuras ul y li en el HTML. Si una lista con list-style: none está fuera de nav, puede que no se reconozca como una lista en el árbol de accesibilidad de Safari. En el ejemplo, ul especifica role="list" para indicar el rol de lista. Esta condición también se puede encontrar en la descripción de accesibilidad de MDN . El orden de lectura del HTML no cambia para la disposición de filas y columnas en la cuadrícula.
/* Cards and footer */
.lessons { padding-block: 3rem; }
.lessons h2 { margin-block: 0 1.5rem; }
.card-grid { display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
gap: 1rem; margin: 0; padding: 0; list-style: none; }
.card-grid > li { min-width: 0; }
.card { height: 100%; padding: 1.5rem; border: 1px solid var(--line); border-radius: .5rem; }
.card h3 { margin-block: 0 .75rem; }
.card p { margin-block: 0; }
.site-footer { padding-block: 2rem; border-top: 1px solid var(--line); }
.site-footer p { margin: 0; }
La repetición de columnas con auto-fit en CSS Grid permite crear tantas columnas como caben, y minmax() determina el tamaño mínimo y el tamaño expandible de cada columna. El valor mínimo de min(100%, 16rem) provoca que la tarjeta se encoja a ese ancho cuando su contenedor padre es más estrecho que 16rem. El espacio libre se divide por 1fr, y dado que hay tres tarjetas reales, no aparece una cuarta tarjeta: auto-fit colapsa las columnas repetidas que quedan vacías. Ver MDN función repeat y función minmax.
Bajo la condición de que el tamaño del texto de nivel superior sea de 16px y el ancho disponible de la página sea el mismo que el del viewport, el ancho mínimo de la tarjeta es de 256px, la separación es de 16px y los márgenes totales izquierdo y derecho son de 32px. Necesitas al menos 560px de espacio para dos columnas y 832px para tres columnas. A medida que cambian la barra de desplazamiento, el ancho del elemento padre y el tamaño de la fuente, el ancho de transición real también cambia, por lo que no lo memorices basándote en el nombre del dispositivo.
La altura de una tarjeta del 100% es el valor que llena la altura de los elementos de la lista en la misma fila. No es una altura fija en píxeles, por lo que las filas pueden crecer a medida que crece la descripción. Para evitar que palabras largas ocupen demasiado el tamaño del elemento Grid, establece el min-width de li en 0 y utiliza las reglas de ajuste de línea de la tarjeta.
Añade condiciones de pantalla ancha y de movimiento reducido
Añade esta sección al final de style.css. A partir de 64rem de anchura de ventana, el fondo de la introducción cambia a un degradado. La cuadrícula continúa determinando el número de columnas de tarjetas de manera independiente; esta consulta de medios no establece ese número.
/* Conditional adjustments */
@media (min-width: 64rem) {
.hero { background: linear-gradient(100deg, var(--surface), #fff); }
}
@media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; }
.button { transition: none; }
}
En la condición de movimiento reducido, cambiamos el desplazamiento a movimiento instantáneo y también eliminamos las transiciones de color de fondo. Se mantiene el propósito y enfoque del enlace y su contenido. La aplicación de reglas condicionales está conectada a consultas de medios CSS y puntos de cambio según el contenido, y la verificación de movimiento y accesibilidad está conectada a comprobaciones del diseño adaptable con anchura, zoom y teclado.
Ejecuta la hoja de estilo completa
Este es un archivo que combina todas las reglas añadidas paso a paso. Usa este archivo completo para comprobar el orden de tu código o para empezar de nuevo. Después de guardarlo en la misma carpeta que index.html, abre index.html con un navegador y actualiza.
/* Shared values and base styles */
:root {
--ink: #183044;
--accent: #166b92;
--surface: #eef6fa;
--line: #c9dbe4;
--focus: #164f73;
}
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body { margin: 0; color: var(--ink); font-family: sans-serif; line-height: 1.6; }
a { color: var(--accent); text-underline-offset: .15em; }
:focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; }
.container { width: min(100% - 2rem, 68rem); margin-inline: auto; }
.container, .card { overflow-wrap: anywhere; }
/* Navigation and header */
.skip-link { position: absolute; left: 1rem; top: -6rem; padding: .5rem; background: white; }
.skip-link:focus { top: 1rem; z-index: 10; }
.site-header { border-bottom: 1px solid var(--line); }
.header-inner { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
gap: .5rem 1.5rem; padding-block: 1rem; }
.site-name { margin: 0; font-weight: 700; }
nav { display: flex; flex-wrap: wrap; gap: 1rem; }
/* Introduction and navigation link */
.hero { padding-block: clamp(3rem, 8vw, 6rem); background: var(--surface); }
.hero h1 { max-width: 18ch; margin-block: 0 1rem;
font-size: clamp(1.8rem, 1.2rem + 2vw, 3rem); line-height: 1.25; }
.hero p { max-width: 50ch; }
.button { display: inline-block; margin-top: .5rem; padding: .7rem 1rem;
border-radius: .4rem; background: var(--accent); color: white; text-decoration: none;
transition: background-color 180ms ease; }
.button:hover { background: #104d6a; }
/* Cards and footer */
.lessons { padding-block: 3rem; }
.lessons h2 { margin-block: 0 1.5rem; }
.card-grid { display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
gap: 1rem; margin: 0; padding: 0; list-style: none; }
.card-grid > li { min-width: 0; }
.card { height: 100%; padding: 1.5rem; border: 1px solid var(--line); border-radius: .5rem; }
.card h3 { margin-block: 0 .75rem; }
.card p { margin-block: 0; }
.site-footer { padding-block: 2rem; border-top: 1px solid var(--line); }
.site-footer p { margin: 0; }
/* Conditional adjustments */
@media (min-width: 64rem) {
.hero { background: linear-gradient(100deg, var(--surface), #fff); }
}
@media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; }
.button { transition: none; }
}
Cambia valores y diagnostica problemas
- Reduce el ancho de la pantalla a aproximadamente 390px y luego ensánchalo, manteniendo el valor original. Para anchos pequeños debería haber una columna, para anchos grandes debería haber dos o tres columnas. Si las condiciones descritas anteriormente son las mismas, también se comparan las diferencias entre 559px y 560px y entre 831px y 832px.
- Cambia solo 16rem de .card-grid a 20rem. Agregar más columnas en la misma pantalla requiere más espacio. Después de la confirmación, restaura 16rem.
- Cambia solo 68rem de .container a 50rem. Incluso en pantallas más grandes, el ancho general del contenido se vuelve más estrecho. Después de la confirmación, restaura 68rem.
- Cambia solo --accent por otro color suficientemente oscuro. Comprueba el color de los enlaces y el fondo del enlace con clase button y restaura el valor original. El texto blanco del botón y el indicador de enfoque también deben ser legibles en el nuevo fondo.
- Estira la descripción de una tarjeta en varias oraciones. Asegúrate de que el contenido no se trunque a medida que la altura aumenta y que la altura de las tarjetas en la misma fila esté organizada. Después de practicar, restaura la oración original.
- Inserta una cadena en inglés sin espacios en la tarjeta. El valor anywhere permite saltos dentro de esa cadena. Solo eliminar temporalmente esa regla de salto de línea puede causar desbordamiento. Después de la comparación, tanto las reglas como las oraciones se restauran.
- Muévete con Tab y Shift+Tab y presiona Enter para cada enlace. Deberías poder navegar hasta la introducción y los temas de estudio sin necesidad de un ratón.
- Reproduce las condiciones de reducción de movimiento. Los enlaces deben seguir navegando a su destino, mientras desaparecen el desplazamiento suave y las transiciones. Después de la confirmación, restaura la configuración de reproducción a los valores predeterminados.
Verifica que la navegación, las tarjetas, los indicadores de enfoque y los párrafos sigan siendo legibles cuando se cambia el zoom del navegador o la configuración de la fuente. Agrandar temporalmente el tamaño de fuente de html en DevTools prueba la dimensión basada en rem, pero es diferente del zoom del navegador. Una configuración exitosa no establece usabilidad bajo todas las condiciones de zoom y tamaño de texto.
| problema | Verificar primero |
|---|---|
| Sin estilo alguno | Extensión de archivo, estado de guardado, ruta relativa del enlace |
| Ocurre desplazamiento horizontal | Palabras largas, tamaño mínimo de columna, ancho real del contenedor, falta de border-box |
| La altura de la tarjeta es irregular | Establecer la altura de la misma fila y tarjeta; Las filas diferentes no tienen que ser iguales |
| No hay indicador de enfoque visible | Reglas que sobrescriben el contorno, ya sea que el contorno se corte debido al desbordamiento |
| efecto de movimiento no deseado | Condiciones reales de medios y valores aplicados de scroll-behavior·transition |
Selecciona el elemento problemático en las Herramientas de Desarrollador para ver el ancho calculado real, el selector y las columnas de la cuadrícula. En lugar de enmascarar el problema forzando todos los saltos de línea o agregando overflow:hidden al cuerpo, primero corrige el tamaño y el contenido del elemento desbordado. Al revisar el estado base, el estado modificado y el estado de restauración para cada requisito, puedes ir un paso más allá de copiar el código completado y aplicarlo también a otros contenidos.




