Proyecto: una landing page con CSS
En esta lección
Ha llegado el momento de juntar todo lo que has aprendido. En este proyecto vas a maquetar, desde cero y paso a paso, la página de presentación (landing page) de un gimnasio de barrio: cabecera con menú, sección principal, tarjetas, testimonios, formulario y pie. Usarás variables, Flexbox, Grid, media queries y el enfoque mobile first. Al final tendrás una página completa y una lista para revisar tus propios proyectos.
Lo que vas a construir
La página de FitBarrio tiene siete bloques, de arriba abajo:
- Cabecera con el logo y un menú de navegación.
- Héroe: el gran bloque de bienvenida con un título y un botón.
- Clases: tres tarjetas colocadas con Grid.
- Por qué nosotros: una imagen y un texto uno al lado del otro con Flexbox.
- Testimonios de socios.
- Formulario de contacto.
- Pie de página.
En el móvil todo irá en una columna. A partir de 700px de ancho, el menú se pone en fila, las tarjetas en tres columnas y la imagen al lado del texto.
Crea una carpeta con dos archivos: index.html y estilos.css. El ejemplo de esta lección es una versión recortada que puedes tocar en el editor.
Paso 1: el HTML completo
Antes de escribir una sola línea de CSS, escribe el HTML semántico y completo. Si el HTML está bien estructurado, el CSS es mucho más fácil. Fíjate en que las clases siguen la convención BEM que viste en Organizar el CSS.
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>FitBarrio | Tu gimnasio de barrio</title>
<link rel="stylesheet" href="estilos.css">
</head>
<body>
<header class="cabecera">
<div class="contenedor cabecera__interior">
<a class="logo" href="/">FitBarrio</a>
<nav>
<ul class="menu">
<li><a class="menu__enlace" href="#clases">Clases</a></li>
<li><a class="menu__enlace" href="#testimonios">Opiniones</a></li>
<li><a class="menu__enlace" href="#contacto">Contacto</a></li>
</ul>
</nav>
</div>
</header>
<main>
<section class="heroe">
<div class="contenedor">
<h1 class="heroe__titulo">Entrena cerca de casa</h1>
<p class="heroe__texto">Clases en grupo y sala abierta de 7:00 a 23:00.</p>
<a class="boton" href="#contacto">Primera clase gratis</a>
</div>
</section>
<section class="seccion" id="clases">
<div class="contenedor">
<h2 class="seccion__titulo">Nuestras clases</h2>
<div class="tarjetas">
<article class="tarjeta">
<h3 class="tarjeta__titulo">Spinning</h3>
<p>45 minutos de cardio al ritmo de la música.</p>
</article>
<article class="tarjeta">
<h3 class="tarjeta__titulo">Yoga</h3>
<p>Gana flexibilidad y aprende a respirar mejor.</p>
</article>
<article class="tarjeta">
<h3 class="tarjeta__titulo">Fuerza</h3>
<p>Técnica con monitor y rutinas a tu medida.</p>
</article>
</div>
</div>
</section>
<section class="seccion seccion--clara">
<div class="contenedor destacado">
<img class="destacado__imagen" src="sala.jpg" alt="Sala de musculación de FitBarrio"
width="600" height="400">
<div class="destacado__texto">
<h2 class="seccion__titulo">Por qué FitBarrio</h2>
<p>Sin permanencia, con monitores titulados y a cinco minutos andando.</p>
</div>
</div>
</section>
<section class="seccion" id="testimonios">
<div class="contenedor">
<h2 class="seccion__titulo">Lo que dicen nuestros socios</h2>
<div class="testimonios">
<blockquote class="testimonio">
<p>«Vengo antes de trabajar y el ambiente es genial.»</p>
<footer class="testimonio__autor">Lucía, socia desde 2024</footer>
</blockquote>
<blockquote class="testimonio">
<p>«Los monitores te corrigen la técnica sin agobiar.»</p>
<footer class="testimonio__autor">Andrés, socio desde 2025</footer>
</blockquote>
</div>
</div>
</section>
<section class="seccion seccion--clara" id="contacto">
<div class="contenedor">
<h2 class="seccion__titulo">Pide tu clase gratis</h2>
<form class="formulario">
<label class="formulario__etiqueta" for="nombre">Nombre</label>
<input class="formulario__campo" id="nombre" name="nombre" required>
<label class="formulario__etiqueta" for="correo">Correo</label>
<input class="formulario__campo" id="correo" name="correo" type="email" required>
<button class="boton" type="submit">Enviar</button>
</form>
</div>
</section>
</main>
<footer class="pie">
<div class="contenedor">
<p>© 2026 FitBarrio · Calle Mayor, 12</p>
</div>
</footer>
</body>
</html>
Un detalle: el .contenedor va dentro de cada sección. Así el fondo de la sección ocupa todo el ancho de la pantalla, pero el contenido queda centrado y con un ancho máximo.
Paso 2: variables, reset y base
Empieza el estilos.css por las variables y la base. Si mañana cambian los colores de la marca, solo tocas estas líneas.
/* ---------- Variables ---------- */
:root {
--primario: #e4572e;
--primario-oscuro: #b8401d;
--oscuro: #1d2433;
--claro: #f6f7fb;
--radio: 10px;
--espacio: 1rem;
}
/* ---------- Reset y base ---------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
body {
font-family: system-ui, sans-serif;
line-height: 1.5;
color: var(--oscuro);
}
img { display: block; max-width: 100%; height: auto; }
input, button { font: inherit; }
/* ---------- Layout ---------- */
.contenedor {
width: min(100% - 32px, 1100px);
margin-inline: auto;
}
.seccion { padding-block: clamp(2rem, 6vw, 4rem); }
.seccion--clara { background: var(--claro); }
.seccion__titulo { margin-bottom: calc(var(--espacio) * 1.5); }
width: min(100% - 32px, 1100px) hace que el contenido ocupe todo el ancho menos 16px por lado, pero nunca más de 1100px. El padding-block con clamp() da más aire vertical en pantallas grandes sin media queries.
Paso 3: la cabecera y el menú
En móvil, logo arriba y menú debajo. En pantalla ancha, el logo a la izquierda y el menú a la derecha, con Flexbox.
/* ---------- Cabecera ---------- */
.cabecera {
background: var(--oscuro);
padding-block: 12px;
}
.logo {
color: white;
font-size: 1.4rem;
font-weight: bold;
text-decoration: none;
}
.menu {
list-style: none;
padding: 0;
margin-top: 8px;
display: flex;
flex-wrap: wrap;
gap: 8px 20px;
}
.menu__enlace {
color: #ccd3e0;
text-decoration: none;
}
.menu__enlace:hover,
.menu__enlace:focus-visible {
color: white;
}
@media (width >= 700px) {
.cabecera__interior {
display: flex;
justify-content: space-between;
align-items: center;
}
.menu { margin-top: 0; }
}
Fíjate en :focus-visible: quien navega con el teclado también debe ver qué enlace está activo.
Paso 4: el héroe
Es lo primero que ve el visitante: un título grande, una frase y un botón llamativo.
/* ---------- Héroe ---------- */
.heroe {
background: linear-gradient(135deg, var(--claro), #ffe3d8);
padding-block: clamp(3rem, 10vw, 6rem);
text-align: center;
}
.heroe__titulo {
font-size: clamp(2rem, 1rem + 5vw, 3.5rem);
line-height: 1.1;
}
.heroe__texto {
margin-block: 12px 24px;
font-size: 1.1rem;
}
/* ---------- Botón (componente reutilizable) ---------- */
.boton {
display: inline-block;
background: var(--primario);
color: white;
padding: 12px 24px;
border: none;
border-radius: var(--radio);
font-weight: bold;
text-decoration: none;
cursor: pointer;
transition: background 0.2s;
}
.boton:hover { background: var(--primario-oscuro); }
El título usa tipografía fluida: crece con la pantalla entre 2rem y 3.5rem. Lo explicamos en Diseño responsive.
Paso 5: tres tarjetas con Grid
Mobile first: el grid empieza con una columna (lo que hace por defecto) y pasa a tres en pantallas anchas.
/* ---------- Tarjetas ---------- */
.tarjetas {
display: grid;
gap: 20px;
}
.tarjeta {
background: white;
border: 1px solid #e3e6ee;
border-top: 4px solid var(--primario);
border-radius: var(--radio);
padding: 20px;
}
.tarjeta__titulo { margin-bottom: 8px; }
@media (width >= 700px) {
.tarjetas { grid-template-columns: repeat(3, 1fr); }
}
Como alternativa sin media query, grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)) decidiría el número de columnas solo. Lo tienes en Grid. Aquí usamos la media query para que siempre haya exactamente una o tres, nunca dos con una huérfana.
Paso 6: imagen y texto con Flexbox
En móvil, la imagen encima del texto; en pantalla ancha, uno al lado del otro, repartiendo el espacio a partes iguales.
/* ---------- Destacado ---------- */
.destacado {
display: flex;
flex-direction: column;
gap: 24px;
}
.destacado__imagen {
border-radius: var(--radio);
aspect-ratio: 3 / 2;
object-fit: cover;
width: 100%;
}
@media (width >= 700px) {
.destacado {
flex-direction: row;
align-items: center;
}
.destacado__imagen,
.destacado__texto {
flex: 1;
}
}
aspect-ratio y object-fit evitan que la foto se deforme aunque tenga otras proporciones (más en Imágenes: object-fit, aspect-ratio y filtros).
Paso 7: testimonios
Cada testimonio es una cita con su autor. En pantallas anchas, dos columnas.
/* ---------- Testimonios ---------- */
.testimonios {
display: grid;
gap: 20px;
}
.testimonio {
margin: 0;
padding: 20px;
border-left: 4px solid var(--primario);
background: var(--claro);
border-radius: 0 var(--radio) var(--radio) 0;
font-style: italic;
}
.testimonio__autor {
margin-top: 8px;
font-style: normal;
font-weight: bold;
font-size: 0.9rem;
}
@media (width >= 700px) {
.testimonios { grid-template-columns: 1fr 1fr; }
}
Paso 8: el formulario
Etiquetas encima de cada campo, campos a todo el ancho y un ancho máximo para que no queden larguísimos en el ordenador.
/* ---------- Formulario ---------- */
.formulario {
display: grid;
gap: 8px;
max-width: 480px;
}
.formulario__etiqueta { font-weight: bold; }
.formulario__campo {
padding: 10px 12px;
border: 1px solid #b9c0cf;
border-radius: var(--radio);
margin-bottom: 8px;
}
.formulario__campo:focus {
outline: 3px solid #ffc7b3;
border-color: var(--primario);
}
.formulario .boton { justify-self: start; }
justify-self: start evita que el botón se estire a todo el ancho, que es lo que hacen los hijos de un grid por defecto. Tienes más ideas en Estilos para formularios.
Paso 9: el pie
/* ---------- Pie ---------- */
.pie {
background: var(--oscuro);
color: #ccd3e0;
text-align: center;
padding-block: 24px;
font-size: 0.9rem;
}
Pegando en orden los bloques de los pasos 2 a 9 tienes el estilos.css completo. Ábrelo en el navegador, pulsa F12, activa el modo dispositivo y estrecha y ensancha la ventana: verás cómo cada bloque cambia a partir de 700px.
Errores frecuentes
- Empezar por el CSS sin tener el HTML. Acabas añadiendo
<div>a trompicones y reescribiendo estilos. - Poner el ancho máximo en la sección en lugar de en un contenedor interior. El fondo de color deja de llegar a los bordes de la pantalla.
- Olvidar la etiqueta viewport. En el móvil real las media queries no se aplicarán.
- Repetir colores a mano en lugar de usar las variables. Al cambiar la marca tendrás que buscar cada
#e4572e. - Olvidar el
alt, elwidthy elheightde la imagen. Sin las dimensiones, la página “salta” mientras carga la foto. - Probar solo en el ancho de tu pantalla. Revisa también 320px, que es el ancho de los móviles más pequeños.
Checklist final
Antes de dar por terminada cualquier maquetación, repasa esta lista:
- El HTML es semántico (
header,nav,main,section,footer) y valida sin errores. - Tiene
<meta name="viewport">ylang="es". - Los colores, radios y espacios salen de variables en
:root. - El CSS está escrito mobile first: base para móvil y
min-width/width >=para crecer. - No hay scroll horizontal en ningún ancho, de 320px en adelante.
- Las imágenes tienen
alt, no se deforman y no se salen de su caja. - Los títulos usan tipografía fluida o se ven bien en todos los tamaños.
- Todos los enlaces y botones tienen estado
:hovery:focus-visiblevisibles. - Los campos de formulario tienen su
<label>asociado. - El contraste entre texto y fondo es suficiente.
- Las clases siguen un criterio (BEM) y no hay ids ni
!importantpara estilar. - Lo has probado en DevTools y en un móvil de verdad.
Resumen
| Parte | Técnica principal |
|---|---|
| Base | Variables, reset, .contenedor con min() |
| Cabecera | Flexbox con justify-content: space-between |
| Héroe | Degradado y tipografía fluida con clamp() |
| Tarjetas | Grid de una a tres columnas |
| Destacado | Flexbox de columna a fila, flex: 1 |
| Testimonios | Grid de dos columnas |
| Formulario | Grid con gap y justify-self |
| Todo | Mobile first y una media query a 700px |
Como siguiente paso, prueba a añadir una sección de tarifas por tu cuenta, o descubre cómo hacer lo mismo más rápido con Bootstrap.
Pruébalo tú
Cambia el código y pulsa Ejecutar (o Ctrl + Enter).
Pon a prueba lo que has aprendido
¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.