Diseño responsive con CSS grid y gatitos

Si escuchaste hablar de CSS grid y cómo puede ayudarte a hacer diseños responsive pero aún no te animaste a probarlo o no sabés cómo empezar, ¡acompañanos en la construcción de este diseño responsive con grillas y gatitos!

If want to see this post in English, click here.

Del diseño al código

Supongamos que queremos construir un página sobre gatitos replicando el siguiente diseño:

Bueno, ¿por dónde arrancamos? 🤔

Miremos un poco la estructura: podemos ver que el diseño tiene tres grandes secciones, siendo la primera un contenedor rosa con un título, un subtítulo y una imagen. Vayamos en orden y empecemos por modelar en HTML y CSS una estructura básica de esta sección, que por ahora va a contener solo una grilla de doce columnas.

<!-- grillas-y-gatitos.html -->

<section class="intro">
  <div class="grilla">
    <!-- Acá van a estar el título, el subtítulo y la imagen -->
  </div>
</section>
/* estilos.css */

.grilla {
  box-sizing: border-box;
  display: grid;
  grid-gap: 2rem;
  grid-template-columns: repeat(12, 1fr);
  margin: 0 auto;
  max-width: 70rem;
  width: 100%;
}

Como podés observar en el archivo CSS, vamos a crear nuestra grilla usando display: grid en su estilo y además agregando la propiedad grid-template-columns, que es la que define el tamaño de sus columnas. El resto de las propiedades simplemente le están dando un poco de estilo adicional (para que mida como máximo 70rem de ancho y además esté centrada respecto al navegador).

Detengámonos un momento en el valor repeat(12, 1fr). ¿Qué significa exactamente? La unidad fr hace referencia a una fracción del espacio disponible que hay dentro de la grilla y repeat es una función que nos permite indicar que una medida se repite una cantidad determinada de veces (por ejemplo, repeat(3, 1fr) es equivalente a escribir 1fr 1fr 1fr).

Más despacio cerebrito… ¿por qué necesitamos repetir este valor? Bueno, el uso más común de la propiedad grid-template-columns consiste en asignarle una serie de valores que van a definir de cuántas columnas va a constar mi grilla y qué ancho va a tener cada una. Entonces, si yo le asigno los valores 10rem 15rem 12rem voy a obtener como resultado una grilla con tres columnas (de ancho 10rem, 15rem y 12rem respectivamente). Al utilizar repeat con los valores 12 y fr, lo que estoy escribiendo es básicamente "dividí el ancho total de mi grilla en 12 columnas iguales".

¿Y por qué 12? ¿No podrían ser 2, 5 u 83? ¡Claro que sí! Pero en este ejemplo usamos el 12 por la flexibilidad que nos da, ya que al tener muchos divisores nos permite fraccionar fácilmente nuestro diseño en 1, 2, 3, 4, 6 o 12 columnas o incluso más combinaciones si no necesitamos que sean del mismo ancho 🤯

Si observamos nuevamente el diseño e intentamos dividirlo en 12 columnas podremos notar que (convenientemente para este artículo 😜) varios de los elementos tienen un tamaño relativo a una cantidad de columnas.

Por ejemplo, la imagen de la sección introductoria tiene un ancho de 6 columnas y cada una de las 3 tarjetas de la segunda sección tienen un ancho de 4 columnas. Esta heurística de medir los anchos de los elementos en columnas es la que vamos a usar para construir nuestra página.

Primera sección

Ahora que entendimos un poco mejor cómo está estructurada nuestra página, continuemos agregando los elementos internos de la primera sección. Podríamos decir que en ella van a existir dos contenedores: uno que va a contener el título y el subtítulo, y otro que va a tener la imagen del gatito.

Entonces, nuestra sección va a quedar de esta forma:

<!-- grillas-y-gatitos.html -->

<section class="intro">
  <div class="grilla">
    <div class="grilla">
      <div class="contenedor">
        <h1 class="titulo">Diseño Responsive</h1>
        <h2 class="subtitulo">¡con gatitos!</h2>
      </div>
      <div class="contenedor">
        <img class="imagen-gatito" src="gatito-de-intro.jpg" alt="Gatito bajo una frazada"/>
      </div>
    </div>
  </div>
</section>

Si observamos ahora cómo va quedando nuestro maquetado, veremos que: nuestros contenedores están ocupando una columna dentro de nuestra grilla (¡qué bien!) pero no ocupan la mitad de la sección de ancho como querríamos (¡qué mal!).

Para solucionarlo vamos a tener que explicitar en el código que cada uno de ellos debe expandirse a lo largo de 6 columnas de la grilla (por ser la mitad de las 12 totales), poniéndole a cada contenedor la propiedad grid-column-end: span 6.

/* estilos.css */

.contenedor {
  grid-column-end: span 6;
}

Si además agregamos un poco más de estilo a la sección, la grilla y a los elementos internos las nuevas reglas quedarían así:

/* estilos.css */

:root {
  /* Usamos variables de CSS para reutilizar fácilmente los colores */
  --color-principal: #FADFDB;
  --color-secundario: darkblue;
}

body {
  color: var(--color-secundario);
  /* Para usar esta fuente primero tenés que embeberla en tu ćodigo */
  /* Podés encontrarla en Google Fonts: https://fonts.google.com/ */
  font-family: 'Nunito', sans-serif;
  margin: 0;
  padding: 2rem;
}

.grilla {
  grid-gap: 2rem;
  padding: 2rem;
}

.intro .grilla {
  align-items: center;
  background-color: var(--color-principal);
  border-radius: 1rem;
  text-align: center;
}

.imagen-gatito {
  border-radius: 1rem;
  width: 100%;
}

.titulo {
  font-size: 4rem;
  font-weight: 500;
  margin-block-end: 1rem;
}

.subtitulo {
  font-size: 2rem;
  font-weight: 300;
}

Entre estas reglas (más precisamente dentro del estilo de la grilla) utilizamos otro concepto de CSS grid: grid-gap, el cual nos permite establecer el tamaño del hueco entre columnas y filas de la grilla. Con esta propiedad podemos asegurarnos de que haya algo de espacio entre los elementos internos de la misma. Si inspeccionás la sección introductoria con las herramientas de desarrollador vas a poder observar tanto las columnas como los huecos o gaps.

Segunda sección

¡Empecemos ahora a enumerar las razones por las que nos gustan los gatitos! Vamos a necesitar una nueva sección, con un título y una grilla, dentro de la cual crearemos las tres tarjetas con esos motivos.

<!-- grillas-y-gatitos.html -->

<section class="razones">
  <h3>¿Por qué me gustan?</h3>
  <div class="grilla">
    <div class="tarjeta">
      <!-- No olvides completar el atributo alt -->
      <!-- para mejorar la accesibilidad de la página -->
      <!-- https://webaim.org/techniques/alttext/ -->
      <img src="lindos.jpeg" alt="Gatito tierno en la cucha"/>
      <h4>Son lindos</h4>
      <p>¿Quién puede resistirse al encanto de esas suaves caritas?</p>
    </div>
    <div class="tarjeta">
      <img src="duermen.jpeg" alt="Gatito durmiendo"/>
      <h4>Duermen mucho</h4>
      <p>Ojalá yo también pudiese estar todo el día en la cama...</p>
    </div>
    <div class="tarjeta">
      <img src="graciosos.jpeg" alt="Gatito con cara graciosa"/>
      <h4>Son graciosos</h4>
      <p>¡Son los protagonistas perfectos para video memes!</p>
    </div>
  </div>
</section>

Lo más importante en esta parte del diseño es lograr que se puedan mostrar 3 tarjetas en una sola fila de nuestra grilla, por lo que vamos a hacer que cada una de ellas ocupe un tercio del total de columnas, es decir, 4 columnas cada una. ¿Cómo? ¡Con nuestra ya conocida amiga grid-column-end! Si además agregamos algunos estilos adicionales, el CSS nos quedará así:

/* estilos.css */

.razones {
  padding: 3rem 0;
  text-align: center;
}

.tarjeta {
  border-radius: 1rem;
  box-shadow: rgba(0, 0, 0, 0.08) 0 0 2rem;
  grid-column-end: span 4;
  padding: 2rem;
}

.tarjeta img {
  /* Para que la imagen de la tarjeta se vea redonda */
  border-radius: 50%;
  max-width: 10rem;
  width: 100%;
}

h3 {
  font-size: 2.5rem;
  font-weight: 500;
}

h4 {
  font-size: 1.5rem;
  font-weight: 700;
}

p {
  font-size: 1.25rem;
  line-height: 150%;
}

Nuevamente podés inspeccionar el HTML y observar que ahora las tarjetas ocupan cuatro columnas cada una 🕵️‍♀️

Tercera sección

¡Pasemos a la última parte de nuestro diseño! Como ya te imaginarás, vamos a necesitar una nueva sección junto con todos sus componentes.

<!-- grillas-y-gatitos.html -->

<section class="ahora-que">
  <h3>¿Y ahora qué?</h3>
  <div class="grilla">
    <img class="ver-mas" src="convenci.jpeg" alt="Dos gatitos tirados uno encima del otro"/>
  </div>
  <!-- Unsplash es un banco de imágenes de distribución libre-->
  <!-- ¡Podés usar sus fotos sin preocuparte por el copyright!  -->
  <a href="https://unsplash.com/s/photos/cat" target="_blank">¡Ver más fotos!</a>
</section>

Algo clave a tener en cuenta para el estilo de esta sección es que la foto ocupa una proporción de dos tercios del total del ancho de la grilla, es decir 8 columnas. ¿Qué vamos a usar para darle ese ancho? ¡Adivinaste! La vieja amiga grid-column-end. Sin embargo, esta vez hay un detalle adicional, y es que la imagen está centrada respecto de la grilla. Por eso, no solo le indicaremos cuántas columnas debe ocupar sino también desde dónde debe empezar a dibujarse, para lo cual usaremos grid-column-start. Es importante tener en cuenta que la propiedad espera recibir en qué línea de la grilla comenzará a dibujarse el elemento y que empieza a contar desde la línea 1.

Ahora, ¿cómo sabemos exactamente en cuál línea tenemos que elegir? 😱 Bueno, si consideramos que la imagen va a ocupar 8 columnas y que el total es de 12, nos restan 4 columnas libres. Entonces, si queremos que la imagen esté centrada, lo que nos gustaría lograr es que nos queden 2 columnas vacías a cada lado de la imagen. Así podemos deducir que queremos que empiece junto con la tercera columna (es decir, en la línea tres), tal como vemos en esta imagen:

Si traducimos esto a CSS (sumándole algunos retoques de estilo), nos va a quedar el siguiente código:

/* estilos.css */

.ver-mas {
  grid-column-end: span 8;
  grid-column-start: 3;
  width: 100%;
}

.ahora-que {
  text-align: center;
}

a {
  background-color: var(--color-principal);
  border: 4px solid var(--color-principal);
  border-radius: 2rem;
  display: block;
  font-size: 1.25rem;
  font-weight: 500;
  padding: 1rem 2rem;
  margin: 3rem auto;
  text-decoration: none;
  width: 12rem;
}

a:hover {
  background-color: white;
}

¡Ahora sí! Nuestro maquetado quedó (casi) terminado 🎉

¿Y lo responsive?

¿Dónde está lo responsive?

Para que también se vea bien en dispositivos móviles solo tenemos que hacer unos pequeños ajustes utilizando media queries. Una solución sencilla es hacer que cada uno de los componentes hijos de una grilla ocupe todo el ancho disponible en lugar de una porción de la misma (porque tenemos menos espacio que en una resolución de escritorio). Además vamos a eliminar el hueco entre columnas (ya que sabemos que no tendremos elementos dentro que queramos distanciar) y mantenerlo solo entre las filas.

/* estilos.css */

/* Las reglas que estén dentro de la media query se van a aplicar */
/* solo cuando la ventana del navegador sea menor o igual a 48rem */
@media (max-width: 48rem) {

  /* Con este selector indicamos que ese estilo debe aplicarse a */
  /* TODOS los elementos que sean hijos directos de la grilla */
  .grilla > * {
    grid-column-end: span 12;
  }

  /* El primer valor corresponde al hueco entre filas */
  /* El segundo valor corresponde al hueco entre columnas */
  .grilla {
    grid-gap: 2rem 0;
  }
}

No hay que olvidarnos de que en la última sección le dijimos a la imagen de los dos gatitos que debería empezar a dibujarse desde la línea 3, por lo que dentro de la media query deberíamos sobrescribir esa propiedad para que lo haga desde la primera línea:

/* estilos.css */

.ver-mas {
  grid-column-start: 1;
}

Agregamos unos últimos ajustes…

/* estilos.css */

body {
  padding: 1rem;
}

.titulo {
  font-size: 3rem;
}

Y... ¡voilà! Nuestra página está lista para ser vista en celulares también 🙌

¡Ahora te toca a vos!

Esta es solo una forma de un montón que existen para maquetar esta página, ¿te animás a buscar e implementar otras estrategias? Te dejamos a mano el código completo de este post para que puedas usarlo como disparador.

Existen muchos recursos que podés usar para conocer y practicar un poco más sobre CSS grid, algunos de mis favoritos son la guía completa de grid (de CSS tricks) y el juego grid garden.
Además, ¡podés combinarlo con CSS Flexbox para crear diseños más interesantes! (Y si no sabés de lo que estoy hablando, podés mirar este post 😉).