Relaciones de Aspecto Perfectas en CSS

O, cómo aprendí a dejar de preocuparme y amar el padding

Ya sea que estemos desarrollando una web o la interfaz de una aplicación, las relaciones de aspecto son algo por lo que no tenemos que preocuparnos mucho. Hasta que el cliente nos pasa un video que quiere que sea vea sin bandas negras, o nos explican que la imagen tiene que estar sí o sí en una relación de 1:1.

¿Qué es la relación de aspecto?

La relación de aspecto es la relación que hay, proporcionalmente, entre el alto y el ancho de un elemento. Esta relación se suele expresar como la fracción de ancho sobre alto. Por ejemplo, para un elemento que tiene 1:1 su ancho es igual a su altura. Sin embargo un elemento que tiene 4:3 como relación de aspecto*, el ancho es 1.33 veces mas grande (porque 4 sobre 3, es 1.33).*

Algunas de las más conocidas y populares siendo:

  • 4:3 Televisores de tubo viejos 📺, y la relación nativa de los VHS 📼
  • 16:9 Monitores, celulares en horizontal, laptops, y televisores smart en promedio (cualquiera que no sea widescreen).
  • 9:16 Los celulares en vertical 📱 y con ellos, todas los videos filmados verticalmente como el contenido de TikTok
  • 1:1 Las imágenes del feed de Instagram y las fotos Polaroid.

🎨 Relaciones de Aspecto en CSS

Excelente, ahora que sabemos que es una relación de aspecto 1:1, podemos forzar un alto que corresponda al ancho y listo ¿no?
Si un elemento tiene 1100px de ancho (width: 1100px) le agregamos 1100px de alto (height: 1100px), tiene la relación de aspecto que queríamos, 1:1 pero si el tamaño de la pantalla cambia, también deberíamos cambiar el alto para preservar la relación de aspecto. Esta solución es válida pero **no es responsiva.

Necesitamos alguna regla de CSS que automáticamente mute en base al ancho, pero que tenga influencia en el alto.

🤯 El padding porcentual de un elemento es relativo al ancho de su padre

Una propiedad bastante interesante y poco intuitiva es que el tamaño del padding cuando las unidades son porcentuales corresponde al ancho del elemento 🤯. Esto que a simple lectura parece algo muy chico ¡nos permite calcular un alto, en base a un ancho 🤯!

Si pueden ver a donde vamos con esto, si a un elemento de 100px de ancho, le agregamos un hijo con alto automático (height: auto) y le damos 100% de padding-top, el alto de este elemento va a ser 100px (1:1). ¡Tadá!

👨‍💻 Relaciones de aspecto más complejas

En caso de tener por ejemplo el pantallazo de una película, filmada en 16:9, mi elemento tiene que tener la misma relación de aspecto si quiero que se vea sin bandas negras.

Para calcularlo dividimos el alto, por el ancho: 9/16 = 0.5625 (padding-top: 56.25%).

Tambien podemos usar la función calc() para automatizar el proceso de cálculo del padding.

.aspect-ratio-16-9 {
    height: auto;
    padding-top: 56.25%;
}

.aspect-ratio-1-1 {
    height: auto;
    padding-top: 100%;
}
/* O haciendo uso de calc() */
.aspect-ratio-4-3 {
    height: auto;
    padding-top: calc(100% * (3 / 4));
}

.aspect-ratio-9-16 {
    height: auto;
    padding-top: calc(100% * (16 / 9));
}

🤔 Pero el padding empujaría todo el contenido hacia abajo ¿no?

Exacto, vamos a tener que volver a acomodar a los hijos de este elemento usando la ayuda de 🌈 posicionamiento absoluto 🦄.

Supongamos que dentro de nuestro elemento con la relación de aspecto 16:9 tenemos que agregar una imagen, lo único que tenemos que hacer es darle un posicionamiento relativo al padre, y acomodar la imagen para que ocupe todo el tamaño provisto por él.

<div class="aspect-ratio-parent">
    <div class="aspect-ratio-16-9">
        <img src="http://placehold.it/1920x1080?text=16:9"/>    
    </div>
</div>
.aspect-ratio-16-9 {
    height: auto;
    padding-top: 56.25%;
    position: relative;
}

.aspect-ratio-16-9 img {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    object-fit: cover; 
}

👀 ¿Hay alguna manera de evitarme hacer yo el cálculo?

Sí, calc(), pero si necesitas un amigo que te ayude con los ratios, RatioBuddy recibe el tamaño del elemento del que quieras calcular su relación de aspecto, y realiza los cálculos por nosotros. Al final, nos deja el SCSS listo para copiar y pegar ✨.
🙏 En caso de que no sepas como usar SCSS (Sass), pega el resultado de RatioBuddy en Sassmeister.

🆙 Level Up!

Usando pseudo elementos y variables de CSS, podemos agregarle a nuestros elementos una relación de aspecto, usando solo una clase.

Explicación

  • Al crear un ::before dinámicamente, nos ahorramos el tener que crear otro el elemento desde el HTML.
  • En vez de crear una clase para cada uno de los casos de relación de aspecto que tengamos que cubrir, hacemos uso de las variables de CSS ponemos --aspect-ratio en el padding-top.
  • Calculamos el padding-top con calc() dividiendo el total, lo que venga en la variable --aspect-ratio.
  • Como estamos dentro de un calc(), CSS va a dividir al 100% calculando la relación de aspecto que esté en la variable.
  • Desde nuestro HTML, vamos a darle valor a esa variable para elemento usando style="--aspect-ratio: n/n".

💡 Pst, también podemos pasarle relación de aspecto, ya dividida, como por ejemplo 1.778 (16/9).

⚡ Usando relaciones de aspecto en Frameworks

Si estás usando un framework es probable que ya exista una clase o componente para aprovechar estas relaciones de aspecto responsivas:

👋 Conclusiones y el futuro

Un verdadero truco de CSS, que al aprenderlo vamos a ser los primeros en proponer una solución a problemas que parecen tener solución solo con Javascript.
Sin embargo, existe un futuro donde no tenemos que depender de trucos y engaños. La regla de CSS aspect-ratio ya tiene un sólido soporte en la mayoría de los exploradores conocidos y con el tiempo ese número solo va a crecer. Mientras tanto, el Truco del Padding ✨, es responsive y válido para todos los exploradores que existen.

31