Diseño responsive con CSS moderno

viernes, 1 de julio de 2022

Por lo general, cuando queremos crear un diseño responsive, nos vienen a la mente las media queries. Estas nos permiten ajustar el diseño en función del tamaño de la pantalla del dispositivo. Aunque, depender únicamente de las media queries para hacer un diseño responsive puede resultar complicado. Imagina que tienes varios componentes que deben cambiar constantemente con el tamaño de la pantalla, tendrías que escribir múltiples media queries, esto generaría un código CSS complejo y difícil de mantener.

Afortunadamente, CSS ha evolucionado enormemente en los últimos años, incorporando nuevas características y mejoras que nos facilitan la implementación de diseños responsive. En este artículo, exploraremos algunas de estas características.

¿Qué es el diseño responsive?

El diseño responsive es una técnica de diseño web que se enfoca en crear sitios que se puedan adaptar de manera óptima a distintos tamaños de pantallas y resoluciones, sin sacrificar su funcionalidad o usabilidad. En otras palabras, el objetivo es asegurar que los usuarios tengan una experiencia óptima al visitar el sitio, independientemente del dispositivo que utilicen, ya sea una computadora de escritorio, una tableta, un reloj inteligente o un teléfono móvil.

Ahora vamos con los ejemplos.

Layout de 2 columnas

A starry night sky.

Este es un diseño bastante común en la que el contenido se divide en dos secciones, generalmente para mostrar diferentes tipos de información, como en una barra de navegación global o un área de contenido principal.

Empezaremos una estructura HTML básica, algunos elementos estarán ocultos para enfocarnos en la estructura general.

<div class="section">
  <div class="text"></div>
  <div class="img"></div>
</div>

En el padre usaremos display:flex y un pequeño gap entre los elementos.

.section {
  display: flex;
  gap: 1rem;
}

Se vería de esta manera.

A starry night sky.

Ahora con flex-wrap:wrap, los elementos flexibles (hijos del elemento con la propiedad flex) van a poder ajustarse a varias líneas.

.section {
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
}

A starry night sky.

Como has notado la imagen ha saltado a la siguiente línea ubicándose debajo del texto, ahora necesitamos controlar cuando ocurre ese salto. Para esto podemos usar la propiedad flex-basis.

.section > * {
  flex-basis: 500px;
  flex-grow: 1;
}

Estamos indicando al navegador que el ancho base para cada elemento flexible es de 500px, si no hay suficiente espacio en esa línea para mantener a los dos elementos con un ancho de 500px cada uno, ajústalos a la siguiente línea. Podemos pensar en el valor de flex-basis como un breakpoint.

Este sería el resultado.

A starry night sky.

Puedes ver que aunque hayamos establecido 500px como ancho base para los elementos, se estiran hasta llenar cualquier espacio vacío que tenga el contenedor, esto ocurre porque hemos establecido flex-grow: 1.

Lista de elementos

A starry night sky.

Este es un patrón muy popular cuando se quieren mostrar elementos de una manera ordenada y fácilmente escaneable. Podríamos intentar realizarlo con flexbox, pero opino que usar grid nos daría mucho más control para alinear y distribuir los elementos de la lista de una manera más precisa. Así que utilizaremos grid.

<ul class="container">
  <li class="card"></li>
  <li class="card"></li>
  <li class="card"></li>
  <li class="card"></li>
  <li class="card"></li>
  <li class="card"></li>
  <li class="card"></li>
</ul>
.container {
  --min-col-size: 378px;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--min-col-size), 1fr));
  gap: 2rem;
}

En el contenedor definimos la rejilla junto con la propiedad grid-template-columns, en esta es donde ocurre la magia, así que vamos por parte.

Con la función repeat() hacemos que el tamaño de columna definido se aplique al resto de columnas, como primer argumento le pasamos auto-fit para que repita el conjunto de columnas tantas veces como sea necesario para llenar el espacio disponible en el contenedor, el segundo argumento es minmax(), en esta definimos el valor mínimo y máximo que quiero que tengan las columnas.

A starry night sky.

Al utilizar 1fr como valor máximo garantizamos que el espacio se distribuya equitativamente.

Aparte de auto-fit también pudimos haber usado auto-fill, la diferencia entre ambos es con auto-fit las columnas se van a expandir tanto como se necesite para adaptarse al tamaño de la fila, mientras que con auto-fill si en la fila hay espacio suficiente para agregar una nueva columna se agregará sin importar que está se encuentre vacía. La elección de una u otra dependerá del resultado que quieras lograr.

Algo no esta bien

En el caso anterior pudimos crear una grilla totalmente responsiva, pero ocurre algo interesante. Trata de adivinar que es…

A starry night sky.

Cuando el viewport es menor a 378px, ocurre un overflow, ya que hemos definido que el tamaño mínimo para la columna es 378px, aquí es donde entra la función min() al rescate, esta recibe una lista de valores y establece el menor, como su valor. Si la agregamos queda de esta manera.

.container {
  --min-col-size: 378px;
  display: grid;
  grid-template-columns: repeat(
    auto-fit,
    minmax(min(var(--min-col-size), 100%), 1fr)
  );
  gap: 2rem;
}

¿Qué ocurre? Cuando 100% es menor a 378px, este será el valor que tome min(), evitando así el overflow en viewport pequeños.

min() también la he encontrado muy útil para cuando queremos establecer el ancho de un elemento que tenga un tamaño máximo, pero que en viewports menores al tamaño máximo ocupe todo el espacio disponible.

.container {
  width: min(100%, 600px);
}

Con esto el ancho del contenedor no será mayor a 600px, y en viewport menores a 600px su ancho será del 100%.

Dentro de min() también se pueden realizar operaciones matemáticas.

.container {
  width: min(1200px, 100% - 2rem);
  margin-inline: auto;
}

Estamos indicando que se resten 2rem al valor del ancho cuando se establece en 100%, y con margin-inline:auto centramos el elemento horizontalmente. Esto da un efecto de padding de 1rem a ambos lados del elemento.

Fluidez con límites

A menudo, al trabajar con tamaños de fuente en CSS, es cuando más recurrimos al uso de media queries. Sin embargo, este enfoque puede resultar tedioso y poco natural, con cambios bruscos entre los diferentes breakpoints que establecemos. ¿No sería mejor crear un tamaño de fuente que sea fluido, que cambie su tamaño de acuerdo al viewport pero sin salirse de unos límites predefinidos? Te lo tengo!, para ello, vamos a usar clamp().

h1 {
  --font-large: clamp(2.8rem, 2.18rem + 3.09vw, 4.58rem);
  font-size: var(--font-large);
}
h2 {
  --font-medium: clamp(1.94rem, 1.6rem + 1.72vw, 2.93rem);
  font-size: var(--font-medium);
}
h3 {
  --font-small: clamp(1.35rem, 1.17rem + 0.91vw, 1.88rem);
  font-size: var(--font-small);
}

Definimos varias custom properties y a clamp le pasamos un valor mínimo, uno preferencial y un máximo.

A starry night sky.

Ahora el tamaño de las letras se ajusta automáticamente sin salirse de los límites que hemos definido como valor mínimo y máximo.

Es importante destacar que al establecer el valor preferido con unidades de viewport, debemos combinarlo con una unidad relativa como rem. De lo contrario, el usuario no podrá realizar zum para aumentar el tamaño de la fuente. Por ello, recomiendo siempre hacer pruebas para identificar posibles problemas de accesibilidad.

Aunque aquí hayamos utilizado clamp() para establecer el tamaño de fuente, también se puede emplear con otras propiedades, como: gap, padding, margin, etc. En definitiva, se trata de una herramienta muy versátil que nos permite crear diseños fluidos y naturales.

Conclusión

Hemos explorado diversas técnicas como el uso de flexbox, grid, min(), y clamp las cuales nos permiten crear diseños fluidos y adaptables a diferentes tamaños de pantalla sin tener que escribir varias media queries. Con esto no quiero decir que usar media queries esté mal, no, estas siguen siendo bastante útiles, y al combinarlas con las técnicas que hemos discutido nos facilitan bastante la implementación de diseños responsive, logrando un código CSS más eficiente y escalable.

Espero que este artículo te haya sido de utilidad, y ahora tengas nuevas herramientas en tu arsenal para ayudarte en tu día a día

Un hombre del pueblo de Negua, en la costa de Colombia, pudo subir al alto cielo.

A la vuelta, contó. Dijo que había contemplado, desde allá arriba, la vida humana. Y dijo que somos un mar de fueguitos.

-El mundo es eso – reveló-. Un montón de gente, un mar de fueguitos.

Cada persona brilla con luz propia entre todas las demás. No hay dos fuegos iguales. Hay fuegos grandes y fuegos chicos y fuegos de todos los colores. Hay gente de fuego sereno, que ni se entera del viento, y gente de fuego loco, que llena el aire de chispas. Algunos fuegos, fuegos bobos, no alumbran ni queman; pero otros arden la vida con tantas ganas que no se puede mirarlos sin parpadear, y quien se acerca, se enciende.

Un Mar de Fueguitos de Eduardo Galeano.