Cree efectos de imagen receptivos con degradados CSS y relación de aspecto

Publicado: 2022-03-10
Resumen rápido ↬ Un problema clásico en CSS es mantener la relación de aspecto de las imágenes en los componentes relacionados, como las tarjetas. ¡La propiedad aspect-ratio recientemente admitida en combinación con object-fit proporciona un remedio a este dolor de cabeza del pasado! Aprendamos a usar estas propiedades, además de crear un efecto de imagen de degradado receptivo para un estilo adicional.

Para prepararnos para nuestros futuros efectos de imagen, configuraremos un componente de tarjeta que tiene una imagen grande en la parte superior seguida de un título y una descripción. El problema común con esta configuración es que es posible que no siempre tengamos un control perfecto sobre lo que es la imagen y, lo que es más importante, para nuestro diseño, cuáles son sus dimensiones . Y aunque esto se puede resolver recortando antes de tiempo, aún podemos encontrar problemas debido a los contenedores de tamaño receptivo. Una consecuencia son las posiciones desiguales del contenido de la tarjeta que realmente se destaca cuando presenta una fila de tarjetas.

Otra solución anterior además de recortar puede haber sido cambiar de un img en línea a un div en blanco que solo existía para presentar la imagen a través de una imagen background-image . Yo mismo he implementado esta solución muchas veces en el pasado. Una ventaja que esto tiene es usar un truco más antiguo para la relación de aspecto que usa un elemento de altura cero y establece un valor padding-bottom . Establecer un valor de relleno como un porcentaje da como resultado un valor calculado final que es relativo al ancho del elemento. Es posible que también haya utilizado esta idea para mantener una proporción de 16:9 para incrustaciones de video, en cuyo caso el valor de relleno se encuentra con la fórmula: 9/16 = 0.5625 * 100% = 56.26% . Pero vamos a explorar dos propiedades CSS modernas que no involucran matemáticas adicionales, nos brindan más flexibilidad y también permiten mantener la semántica proporcionada al usar un img real en lugar de un div vacío.

Primero, definamos la semántica HTML, incluido el uso de una lista desordenada como contenedor de tarjetas:

 <ul class="card-wrapper"> <li class="card"> <img src="" alt=""> <h3>A Super Wonderful Headline</h3> <p>Lorem ipsum sit dolor amit</p> </li> <!-- additional cards --> </ul>

A continuación, crearemos un conjunto mínimo de estilos de línea de base para el componente .card . Estableceremos algunos estilos visuales básicos para la tarjeta en sí, una actualización rápida del titular h3 esperado, luego estilos esenciales para comenzar a diseñar la imagen de la tarjeta.

 .card { background-color: #fff; border-radius: 0.5rem; box-shadow: 0.05rem 0.1rem 0.3rem -0.03rem rgba(0, 0, 0, 0.45); padding-bottom: 1rem; } .card > :last-child { margin-bottom: 0; } .card h3 { margin-top: 1rem; font-size: 1.25rem; } img { border-radius: 0.5rem 0.5rem 0 0; width: 100%; } img ~ * { margin-left: 1rem; margin-right: 1rem; }

La última regla usa el combinador general de hermanos para agregar un margen horizontal a cualquier elemento que sigue al img , ya que queremos que la imagen en sí esté al ras con los lados de la tarjeta.

Y nuestro progreso hasta ahora nos lleva a la siguiente aparición de cartas:

Una tarjeta con los estilos básicos descritos anteriormente aplicados y que incluye una imagen de Unsplash de un postre en un plato pequeño junto a una bebida caliente en una taza.
Una tarjeta con los estilos básicos descritos anteriormente aplicados y que incluye una imagen de Unsplash de un postre en un plato pequeño junto a una bebida caliente en una taza. (Vista previa grande)

Finalmente, crearemos los estilos .card-wrapper para un diseño de respuesta rápida usando la cuadrícula CSS. Esto también eliminará los estilos de lista predeterminados.

 .card-wrapper { list-style: none; padding: 0; margin: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(30ch, 1fr)); grid-gap: 1.5rem; }

Nota : si esta técnica de cuadrícula no le resulta familiar, revise la explicación en mi tutorial sobre soluciones modernas para la cuadrícula de 12 columnas.

Con esto aplicado y con todas las tarjetas que contienen una imagen con una ruta de origen válida, nuestros estilos .card-wrapper nos dan el siguiente diseño:

Se muestran tres tarjetas en una fila debido a los estilos de diseño de envoltura de tarjeta aplicados. Cada tarjeta tiene una imagen única que tiene diferentes relaciones de aspecto naturales, y la última tarjeta tiene una imagen orientada verticalmente que es más del doble de la altura de las otras imágenes de tarjetas.
Se muestran tres tarjetas en una fila debido a los estilos de diseño de envoltura de tarjeta aplicados. Cada tarjeta tiene una imagen única que tiene diferentes relaciones de aspecto naturales, y la última tarjeta tiene una imagen orientada verticalmente que es más del doble de la altura de las otras imágenes de tarjetas. (Vista previa grande)

Como se demuestra en la imagen de vista previa, estos estilos de línea de base no son suficientes para contener correctamente las imágenes dadas sus dimensiones naturales variables. Necesitamos un método para restringir estas imágenes de manera uniforme y consistente.

¡Más después del salto! Continúe leyendo a continuación ↓

Habilitar tamaños de imagen uniformes con object-fit

Como se señaló anteriormente, es posible que haya realizado previamente una actualización en este escenario para cambiar las imágenes que se agregarán a través background-image y utilizó background-size: cover para manejar correctamente el cambio de tamaño de la imagen. O es posible que haya intentado forzar el recorte antes de tiempo (¡todavía es un objetivo digno ya que cualquier reducción del tamaño de la imagen mejorará el rendimiento!).

Ahora, tenemos disponible la propiedad object-fit que permite que una etiqueta img actúe como contenedor de la imagen. Y también viene con un valor de cover que da como resultado un efecto similar al de la solución de imagen de fondo, pero con la ventaja de conservar la semántica de una imagen en línea. Vamos a aplicarlo y ver cómo funciona.

Necesitamos emparejarlo con una dimensión de height para obtener orientación adicional sobre cómo queremos que se comporte el contenedor de la imagen (recuerde que ya habíamos agregado width: 100% ). Y vamos a usar la función max() para seleccionar 10rem o 30vh según cuál sea más grande en un contexto determinado, lo que evita que la altura de la imagen se reduzca demasiado en ventanas de visualización más pequeñas o cuando el usuario ha configurado un zoom grande.

 img { /* ...existing styles */ object-fit: cover; height: max(10rem, 30vh); }

Sugerencia adicional de accesibilidad : siempre debe probar sus diseños con un zoom del 200 % y del 400 % en el escritorio. Si bien actualmente no hay una consulta de medios de zoom , funciones como max() pueden ayudar a resolver problemas de diseño. Otro contexto en el que esta técnica es útil es el espaciado entre elementos.

Con esta actualización, definitivamente hemos mejorado las cosas, y el resultado visual es como si usáramos la técnica anterior de imagen de fondo:

Las imágenes de tres cartas ahora parecen tener una altura uniforme y el contenido de la imagen está centrado dentro de la imagen como si fuera un contenedor.
Las imágenes de tres cartas ahora parecen tener una altura uniforme y el contenido de la imagen está centrado dentro de la imagen como si fuera un contenedor. (Vista previa grande)

Dimensionamiento de imagen coherente con capacidad de respuesta con aspect-ratio

Cuando se usa object-fit por sí mismo, una desventaja es que todavía necesitamos establecer algunas sugerencias de dimensión.

Una próxima propiedad (actualmente disponible en los navegadores Chromium) llamada aspect-ratio mejorará nuestra capacidad para cambiar el tamaño de las imágenes de manera consistente.

Usando esta propiedad, podemos definir una proporción para cambiar el tamaño de la imagen en lugar de establecer dimensiones explícitas. Continuaremos usándolo en combinación con object-fit para garantizar que estas dimensiones solo afecten a la imagen como contenedor; de lo contrario, la imagen podría aparecer distorsionada.

Aquí está nuestra regla de imagen actualizada completa:

 img { border-radius: 0.5rem 0.5rem 0 0; width: 100%; object-fit: cover; aspect-ratio: 4/3; }

Vamos a comenzar con una proporción de imagen de 43 para el contexto de nuestra tarjeta, pero puede elegir cualquier proporción. Por ejemplo, 11 para un cuadrado o 169 para incrustaciones de video estándar.

Aquí están las tarjetas actualizadas, aunque probablemente será difícil notar la diferencia visual en este caso en particular, ya que la relación de aspecto coincide con la apariencia que logramos al configurar la height solo para el ajuste del object-fit .

Las imágenes de tres tarjetas tienen dimensiones idénticas de ancho y alto, que son ligeramente diferentes a la solución anterior de ajuste de objetos.
Las imágenes de tres tarjetas tienen dimensiones idénticas de ancho y alto, que son ligeramente diferentes a la solución anterior de ajuste de objetos. (Vista previa grande)

Establecer una "relación de aspecto" da como resultado que la proporción se mantenga a medida que los elementos crecen o se reducen, mientras que cuando solo se establece "ajuste del objeto" y "altura", la proporción de la imagen cambiará constantemente a medida que cambien las dimensiones del contenedor.

Adición de efectos receptivos con degradados y funciones CSS

Bien, ahora que sabemos cómo configurar imágenes de tamaño constante, ¡divirtámonos con ellas agregando un efecto de degradado!

Nuestro objetivo con este efecto es hacer que parezca que la imagen se desvanece en el contenido de la tarjeta. Es posible que tenga la tentación de envolver la imagen en su propio contenedor para agregar el degradado, pero gracias al trabajo que ya hemos hecho en el tamaño de la imagen, podemos averiguar cómo hacerlo de manera segura en la .card principal.

El primer paso es definir un degradado . Vamos a usar una propiedad personalizada de CSS para agregar los colores degradados para permitir cambiar fácilmente el efecto degradado, comenzando con un azul a rosa. El último color del degradado siempre será blanco para mantener la transición al fondo del contenido de la tarjeta y crear el borde "desvanecido".

 .card { --card-gradient: #5E9AD9, #E271AD; background-image: linear-gradient( var(--card-gradient), white max(9.5rem, 27vh) ); /* ...existing styles */ }

Pero espera, ¿es una función CSS max() ? ¿En un gradiente? ¡Sí, es posible, y es la magia lo que hace que este gradiente sea efectivo de manera receptiva!

Sin embargo, si tuviera que agregar una captura de pantalla, todavía no veríamos que el degradado tiene ningún efecto en la imagen. Para eso, necesitamos incorporar la propiedad mix-blend-mode , y en este escenario usaremos el valor de overlay :

 img { /* ...existing styles */ mix-blend-mode: overlay; }

La propiedad mix-blend-mode es similar a aplicar los estilos de fusión de capas disponibles en software de manipulación de fotografías como Photoshop. Y el valor de overlay tendrá el efecto de permitir que los tonos medios de la imagen se mezclen con el degradado que hay detrás, lo que dará como resultado el siguiente resultado:

Cada imagen de la tarjeta tiene un efecto de combinación de degradado que comienza con un azul claro en la parte superior, que se mezcla con un rosa rojizo y luego termina difuminándose en un blanco antes del resto del contenido del texto de la tarjeta.
Cada imagen de la tarjeta tiene un efecto de combinación de degradado que comienza con un azul claro en la parte superior, que se mezcla con un rosa rojizo y luego termina difuminándose en un blanco antes del resto del contenido del texto de la tarjeta. (Vista previa grande)

Ahora, en este punto, confiamos únicamente en el valor aspect-ratio para cambiar el tamaño de la imagen. Y si cambiamos el tamaño del contenedor y hacemos que el diseño de la tarjeta se redistribuya, el cambio de altura de la imagen provoca inconsistencias en el lugar donde el degradado se desvanece a blanco.

Así que también agregaremos una propiedad max-height que también usa la función max() y contiene valores ligeramente mayores que los del degradado. El comportamiento resultante es que el degradado (casi siempre) se alineará correctamente con la parte inferior de la imagen.

 img { /* ...existing styles */ max-height: max(10rem, 30vh); }

Es importante tener en cuenta que agregar una `altura máxima` altera el comportamiento de la `relación de aspecto`. En lugar de usar siempre la proporción exacta, se usará solo cuando haya suficiente espacio asignado dada la nueva restricción adicional de `max-height`.

Sin embargo, aspect-ratio continuará asegurando que las imágenes cambien de tamaño de manera consistente, como lo fue el beneficio sobre solo object-fit . Intente comentar la aspect-ratio en la demostración final de CodePen para ver la diferencia que está haciendo en los tamaños de los contenedores.

Dado que nuestro objetivo original era habilitar dimensiones de imagen receptivas consistentes, aun así hemos dado en el blanco. Para su propio caso de uso, es posible que deba jugar con los valores de proporción y altura para lograr el efecto deseado.

Alternativo: mix-blend-mode y agregar un filtro

El uso de la overlay como el valor mix-blend-mode fue la mejor opción para el efecto de fundido a blanco que buscábamos, pero probemos una opción alternativa para un efecto más dramático.

Vamos a actualizar nuestra solución para agregar una propiedad personalizada de CSS para el valor mix-blend-mode y también actualizar los valores de color para el degradado:

 .card { --card-gradient: tomato, orange; --card-blend-mode: multiply; } img { /* ...existing styles */ mix-blend-mode: var(--card-blend-mode); }

El valor de multiply tiene un efecto de oscurecimiento en los tonos medios, pero mantiene el blanco y el negro tal cual, lo que da como resultado la siguiente apariencia:

Cada imagen de la tarjeta tiene un fuerte tinte naranja del nuevo degradado que comienza y va de un rojo anaranjado a un naranja puro. Las áreas blancas siguen siendo blancas y las áreas negras siguen siendo negras
Cada imagen de la tarjeta tiene un fuerte tinte naranja del nuevo degradado que comienza y va de un rojo anaranjado a un naranja puro. Las áreas blancas siguen siendo blancas y las áreas negras siguen siendo negras. (Vista previa grande)

Si bien hemos perdido el desvanecimiento y ahora tenemos un borde duro en la parte inferior de la imagen, la parte blanca de nuestro degradado sigue siendo importante para garantizar que el degradado finalice antes que el contenido de la tarjeta.

Una modificación adicional que podemos agregar es el uso del filter y, en particular, usar la función grayscale() para eliminar los colores de la imagen y, por lo tanto, hacer que el degradado sea la única fuente de coloración de la imagen.

 img { /* ...existing styles */ filter: grayscale(100); }

El uso del valor de escala de grayscale(100) da como resultado la eliminación completa de los colores naturales de la imagen y la transforma en blanco y negro. Aquí está la actualización para compararla con la captura de pantalla anterior de su efecto al usar nuestro degradado naranja con multiply :

Ahora, cada imagen de la tarjeta aún tiene el degradado naranja, pero todos los demás colores se eliminan y se reemplazan por tonos de gris.
Ahora, cada imagen de la tarjeta aún tiene el degradado naranja, pero todos los demás colores se eliminan y se reemplazan por tonos de gris. (Vista previa grande)

Use aspect-ratio como una mejora progresiva

Como se mencionó anteriormente, actualmente aspect-ratio solo es compatible con la última versión de los navegadores Chromium (Chrome y Edge). Sin embargo, todos los navegadores admiten object-fit y eso, junto con nuestras restricciones de height , da como resultado un resultado menos ideal pero aún aceptable, que se ve aquí para Safari:

La altura de la imagen de la tarjeta está limitada, pero cada tarjeta tiene una altura realizada ligeramente diferente
La altura de la imagen de la tarjeta está limitada, pero cada tarjeta tiene una altura realizada ligeramente diferente. (Vista previa grande)

Sin el funcionamiento de la aspect-ratio , el resultado aquí es que, en última instancia, la altura de la imagen está limitada, pero las dimensiones naturales de cada imagen aún conducen a alguna variación entre las alturas de la imagen de la tarjeta. Es posible que desee cambiar para agregar una max-height o utilizar la función max() nuevamente para ayudar a que max-height responda mejor a los diferentes tamaños de tarjeta.

Extendiendo los efectos de degradado

Dado que definimos las paradas de color del degradado como una propiedad personalizada de CSS, tenemos acceso inmediato para cambiarlas en diferentes contextos. Por ejemplo, podríamos cambiar el degradado para que presente uno de los colores con más fuerza si la tarjeta está sobrevolada o tiene uno de sus elementos secundarios enfocado.

Primero, actualizaremos cada tarjeta h3 para que contenga un enlace, como:

 <h3><a href="">A Super Wonderful Headline</a></h3>

Luego, podemos usar uno de nuestros selectores disponibles más nuevos, :focus-within , para modificar el degradado de la tarjeta cuando el enlace está enfocado. Para una cobertura adicional de posibles interacciones, combinaremos esto con :hover . Y reutilizaremos nuestra idea de max() para asignar un solo color para cubrir la parte de la imagen de la tarjeta. La desventaja de este efecto en particular es que las paradas de degradado y los cambios de color no se pueden animar de manera confiable, pero pronto lo serán gracias a CSS Houdini.

Para actualizar el color y agregar la nueva parada de color, solo necesitamos reasignar el valor de --card-gradient dentro de esta nueva regla:

 .card:focus-within, .card:hover { --card-gradient: #24a9d5 max(8.5rem, 20vh); }

Nuestros valores max() son menores que los originales en uso para white para mantener el borde difuminado. Si usáramos los mismos valores, se encontraría con el white y crearía una separación claramente en regla.

Al crear esta demostración, originalmente probé un efecto que usaba transform con scale para un efecto de acercamiento. Pero descubrí que debido a que se aplicó el mix-blend-mode , el navegador no volvía a pintar la imagen de manera consistente, lo que causaba un parpadeo desagradable. Siempre habrá compensaciones al solicitar que el navegador realice efectos y animaciones solo de CSS, y si bien es genial lo que podemos hacer, siempre es mejor verificar el impacto en el rendimiento de sus efectos.

¡Diviértete experimentando!

El CSS moderno nos ha brindado algunas herramientas increíbles para actualizar nuestros kits de herramientas de diseño web, siendo la aspect-ratio la última incorporación. ¡Así que continúe y experimente con object-fit , aspect-ratio y agregue funciones como max() en sus gradientes para obtener algunos efectos de respuesta divertidos! Solo asegúrese de verificar las cosas entre navegadores (¡por ahora!) Y en diferentes ventanas gráficas y tamaños de contenedores.

Aquí está CodePen, incluidas las características y los efectos que revisamos hoy:

Consulte el lápiz [Efectos de imagen receptivos con degradados CSS y relación de aspecto] (https://codepen.io/smashingmag/pen/WNoERXo) de Stephanie Eckles.

Vea Efectos de imagen sensibles al lápiz con degradados CSS y relación de aspecto de Stephanie Eckles.

¿Buscando por mas? Asegúrate de consultar nuestra Guía CSS aquí en Smashing →