Proyectos · HTML y CSS · Básico

Página de receta adaptable

Una receta que se lee bien en el móvil y en el ordenador: HTML semántico, una rejilla de dos columnas en pantallas anchas e ingredientes que se tachan al marcarlos.

  • 5 pasos
  • 50 minutos aproximadamente
  • 153 líneas de código
  • Editor en vivo

Lo que vas a construir

Una receta es un ejemplo perfecto de diseño adaptable: en el móvil se lee de arriba abajo y en una pantalla ancha conviene tener los ingredientes siempre a la vista. Vas a construirla con HTML semántico y CSS Grid, sin una sola línea de JavaScript.

Lo que vas a practicar

  • HTML semántico: article, header y section
  • Un ancho de lectura cómodo
  • Rejillas con grid-template-areas
  • Casillas que tachan el texto con :has()
  • Estilos para imprimir
Resultado final

Antes de empezar, conviene haber hecho estas lecciones

Paso 1 de 5

La receta en HTML semántico

Usa las etiquetas que describen cada parte: <article> para la receta entera, <header> para el título y los datos, y una <section> para los ingredientes y otra para la preparación. Los pasos van en una lista numerada (<ol>), porque el orden importa.

aria-labelledby asocia cada sección con su título, así los lectores de pantalla las anuncian por su nombre.

index.html
<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Tortilla de patatas</title>
  <link rel="stylesheet" href="estilos.css">
</head>
<body>
  <article class="receta">
    <header>
      <h1>Tortilla de patatas</h1>
      <p class="entradilla">Jugosa por dentro y dorada por fuera, con cebolla.</p>
      <ul class="datos">
        <li><strong>45 min</strong> en total</li>
        <li><strong>4</strong> raciones</li>
        <li><strong>Fácil</strong></li>
      </ul>
    </header>

    <section class="ingredientes" aria-labelledby="titulo-ingredientes">
      <h2 id="titulo-ingredientes">Ingredientes</h2>
      <ul>
        <li>6 huevos</li>
        <li>700 g de patatas</li>
        <li>1 cebolla</li>
        <li>200 ml de aceite de oliva</li>
        <li>Sal</li>
      </ul>
    </section>

    <section class="pasos" aria-labelledby="titulo-pasos">
      <h2 id="titulo-pasos">Preparación</h2>
      <ol>
        <li>Pela y corta las patatas en láminas finas y la cebolla en tiras.</li>
        <li>Fríelas a fuego medio en el aceite unos 20 minutos, hasta que estén tiernas.</li>
        <li>Escúrrelas y mézclalas con los huevos batidos y la sal. Deja reposar 5 minutos.</li>
        <li>Cuaja la mezcla en una sartén con un poco de aceite, 3 minutos por cada lado.</li>
      </ol>
    </section>
  </article>
</body>
</html>
estilos.css
/* Los estilos llegan en el paso 2 */

Paso 2 de 5

Un texto cómodo de leer

Las líneas demasiado largas cansan: max-width: 70ch limita el texto a unos 70 caracteres por línea. margin: 0 auto lo centra.

clamp(2rem, 6vw, 3rem) hace que el título crezca con el ancho de la pantalla, pero sin bajar de 2rem ni pasar de 3rem.

estilos.css
* { box-sizing: border-box; }

body {
  margin: 0;
  background: #fffbeb;
  color: #292524;
  font-family: Georgia, "Times New Roman", serif;
  font-size: 1.05rem;
  line-height: 1.7;
}

.receta {
  max-width: 70ch;
  margin: 0 auto;
  padding: 32px 20px;
}

h1 {
  margin: 0;
  font-size: clamp(2rem, 6vw, 3rem);
  line-height: 1.1;
}

h2 {
  margin-top: 0;
  color: #b45309;
  font-family: system-ui, sans-serif;
}

.entradilla {
  font-size: 1.2rem;
  color: #57534e;
}

Paso 3 de 5

Datos y tarjetas

Los datos (tiempo, raciones, dificultad) se ponen en fila con display: flex; con flex-wrap: wrap bajan a otra línea si no caben.

Los ingredientes y los pasos se convierten en tarjetas con fondo blanco, esquinas redondeadas y una sombra suave.

estilos.cssLo nuevo, resaltado
* { box-sizing: border-box; }

body {
  margin: 0;
  background: #fffbeb;
  color: #292524;
  font-family: Georgia, "Times New Roman", serif;
  font-size: 1.05rem;
  line-height: 1.7;
}

.receta {
  max-width: 70ch;
  margin: 0 auto;
  padding: 32px 20px;
}

h1 {
  margin: 0;
  font-size: clamp(2rem, 6vw, 3rem);
  line-height: 1.1;
}

h2 {
  margin-top: 0;
  color: #b45309;
  font-family: system-ui, sans-serif;
}

.entradilla {
  font-size: 1.2rem;
  color: #57534e;
}

.datos {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 16px 0 32px;
  padding: 0;
  list-style: none;
  font-family: system-ui, sans-serif;
  font-size: 0.95rem;
}

.datos li {
  padding: 6px 12px;
  border-radius: 999px;
  background: #fef3c7;
}

.ingredientes,
.pasos {
  padding: 20px;
  border-radius: 12px;
  background: #ffffff;
  box-shadow: 0 4px 16px rgb(120 53 15 / 0.08);
}

.pasos { margin-top: 20px; }
.pasos li + li { margin-top: 12px; }

Paso 4 de 5

Dos columnas en pantallas anchas

Solo a partir de 800 px de ancho, la receta pasa a ser una rejilla de dos columnas. Con grid-template-areas se «dibuja» el reparto: la cabecera arriba ocupando todo y, debajo, los ingredientes a la izquierda y los pasos a la derecha.

position: sticky deja los ingredientes fijos al hacer scroll por los pasos, para tenerlos siempre a la vista.

estilos.cssLo nuevo, resaltado
* { box-sizing: border-box; }

body {
  margin: 0;
  background: #fffbeb;
  color: #292524;
  font-family: Georgia, "Times New Roman", serif;
  font-size: 1.05rem;
  line-height: 1.7;
}

.receta {
  max-width: 70ch;
  margin: 0 auto;
  padding: 32px 20px;
}

@media (min-width: 800px) {
  .receta {
    max-width: 1000px;
    display: grid;
    grid-template-columns: 280px 1fr;
    grid-template-areas:
      "cabecera cabecera"
      "ingredientes pasos";
    gap: 24px;
    align-items: start;
  }
  .receta > header { grid-area: cabecera; }
  .ingredientes { grid-area: ingredientes; position: sticky; top: 16px; }
  .pasos { grid-area: pasos; margin-top: 0; }
}

h1 {
  margin: 0;
  font-size: clamp(2rem, 6vw, 3rem);
  line-height: 1.1;
}

h2 {
  margin-top: 0;
  color: #b45309;
  font-family: system-ui, sans-serif;
}

.entradilla {
  font-size: 1.2rem;
  color: #57534e;
}

.datos {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 16px 0 32px;
  padding: 0;
  list-style: none;
  font-family: system-ui, sans-serif;
  font-size: 0.95rem;
}

.datos li {
  padding: 6px 12px;
  border-radius: 999px;
  background: #fef3c7;
}

.ingredientes,
.pasos {
  padding: 20px;
  border-radius: 12px;
  background: #ffffff;
  box-shadow: 0 4px 16px rgb(120 53 15 / 0.08);
}

.pasos { margin-top: 20px; }
@media (min-width: 800px) { .pasos { margin-top: 0; } }
.pasos li + li { margin-top: 12px; }

Paso 5 de 5

Ingredientes marcables e impresión

Cada ingrediente pasa a ser una casilla con su <label>: al pulsar el texto también se marca. El selector label:has(input:checked) tacha el texto del ingrediente marcado, sin JavaScript.

@media print ajusta la página al imprimirla: fondo blanco, bordes en lugar de sombras y los ingredientes en su sitio normal.

index.htmlLo nuevo, resaltado
<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Tortilla de patatas</title>
  <link rel="stylesheet" href="estilos.css">
</head>
<body>
  <article class="receta">
    <header>
      <h1>Tortilla de patatas</h1>
      <p class="entradilla">Jugosa por dentro y dorada por fuera, con cebolla.</p>
      <ul class="datos">
        <li><strong>45 min</strong> en total</li>
        <li><strong>4</strong> raciones</li>
        <li><strong>Fácil</strong></li>
      </ul>
    </header>

    <section class="ingredientes" aria-labelledby="titulo-ingredientes">
      <h2 id="titulo-ingredientes">Ingredientes</h2>
      <ul class="lista-compra">
        <li><label><input type="checkbox"> 6 huevos</label></li>
        <li><label><input type="checkbox"> 700 g de patatas</label></li>
        <li><label><input type="checkbox"> 1 cebolla</label></li>
        <li><label><input type="checkbox"> 200 ml de aceite de oliva</label></li>
        <li><label><input type="checkbox"> Sal</label></li>
      </ul>
    </section>

    <section class="pasos" aria-labelledby="titulo-pasos">
      <h2 id="titulo-pasos">Preparación</h2>
      <ol>
        <li>Pela y corta las patatas en láminas finas y la cebolla en tiras.</li>
        <li>Fríelas a fuego medio en el aceite unos 20 minutos, hasta que estén tiernas.</li>
        <li>Escúrrelas y mézclalas con los huevos batidos y la sal. Deja reposar 5 minutos.</li>
        <li>Cuaja la mezcla en una sartén con un poco de aceite, 3 minutos por cada lado.</li>
      </ol>
    </section>
  </article>
</body>
</html>
estilos.cssLo nuevo, resaltado
* { box-sizing: border-box; }

body {
  margin: 0;
  background: #fffbeb;
  color: #292524;
  font-family: Georgia, "Times New Roman", serif;
  font-size: 1.05rem;
  line-height: 1.7;
}

.receta {
  max-width: 70ch;
  margin: 0 auto;
  padding: 32px 20px;
}

@media (min-width: 800px) {
  .receta {
    max-width: 1000px;
    display: grid;
    grid-template-columns: 280px 1fr;
    grid-template-areas:
      "cabecera cabecera"
      "ingredientes pasos";
    gap: 24px;
    align-items: start;
  }
  .receta > header { grid-area: cabecera; }
  .ingredientes { grid-area: ingredientes; position: sticky; top: 16px; }
  .pasos { grid-area: pasos; margin-top: 0; }
}

h1 {
  margin: 0;
  font-size: clamp(2rem, 6vw, 3rem);
  line-height: 1.1;
}

h2 {
  margin-top: 0;
  color: #b45309;
  font-family: system-ui, sans-serif;
}

.entradilla {
  font-size: 1.2rem;
  color: #57534e;
}

.datos {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 16px 0 32px;
  padding: 0;
  list-style: none;
  font-family: system-ui, sans-serif;
  font-size: 0.95rem;
}

.datos li {
  padding: 6px 12px;
  border-radius: 999px;
  background: #fef3c7;
}

.ingredientes,
.pasos {
  padding: 20px;
  border-radius: 12px;
  background: #ffffff;
  box-shadow: 0 4px 16px rgb(120 53 15 / 0.08);
}

.pasos { margin-top: 20px; }
@media (min-width: 800px) { .pasos { margin-top: 0; } }
.pasos li + li { margin-top: 12px; }

.lista-compra {
  margin: 0;
  padding: 0;
  list-style: none;
}

.lista-compra label {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 0;
  cursor: pointer;
}

.lista-compra input {
  width: 1.2rem;
  height: 1.2rem;
  accent-color: #b45309;
}

/* Al marcar un ingrediente, su texto se tacha */
.lista-compra label:has(input:checked) {
  color: #a8a29e;
  text-decoration: line-through;
}

@media print {
  body { background: #ffffff; }
  .ingredientes, .pasos { box-shadow: none; border: 1px solid #d6d3d1; }
  .ingredientes { position: static; }
}

Retos para seguir

Has terminado el proyecto. Si quieres ir más allá, prueba a añadirle esto por tu cuenta:

  1. Añade una foto del plato con su texto alternativo, que ocupe todo el ancho en el móvil.
  2. Pon un selector de raciones y, con JavaScript, recalcula las cantidades de los ingredientes.
  3. Crea una página índice con varias recetas en tarjetas usando CSS Grid.

Conceptos clave

Repasa la ruta de HTML y CSS o elige otro proyecto.