Créez des effets d'image réactifs avec des dégradés CSS et un rapport d'aspect

Publié: 2022-03-10
Résumé rapide ↬ Un problème classique en CSS est de maintenir le rapport d'aspect des images entre les composants associés, tels que les cartes. La propriété de aspect-ratio nouvellement prise en charge en combinaison avec object-fit fournit un remède à ce casse-tête du passé ! Apprenons à utiliser ces propriétés, en plus de créer un effet d'image dégradé réactif pour plus de style.

Pour préparer nos futurs effets d'image, nous allons configurer un composant de carte comportant une grande image en haut, suivie d'un titre et d'une description. Le problème commun avec cette configuration est que nous n'avons pas toujours un contrôle parfait sur ce qu'est l'image, et plus important encore pour notre mise en page, quelles sont ses dimensions . Et bien que cela puisse être résolu en recadrant à l'avance, nous pouvons toujours rencontrer des problèmes dus à des conteneurs de taille adaptée. Une conséquence est des positions inégales du contenu de la carte qui se démarquent vraiment lorsque vous présentez une rangée de cartes.

Une autre solution précédente en plus du recadrage aurait pu être de passer d'un img en ligne à un div vide qui n'existait que pour présenter l'image via background-image . J'ai implémenté cette solution plusieurs fois moi-même dans le passé. L'un des avantages que cela présente est d'utiliser une astuce plus ancienne pour le rapport d'aspect qui utilise un élément de hauteur nulle et définit une valeur padding-bottom . La définition d'une valeur de remplissage en pourcentage donne une valeur calculée finale relative à la largeur de l'élément. Vous avez peut-être également utilisé cette idée pour conserver un ratio 16:9 pour les intégrations vidéo, auquel cas la valeur de rembourrage est trouvée avec la formule : 9/16 = 0.5625 * 100% = 56.26% . Mais nous allons explorer deux propriétés CSS modernes qui n'impliquent pas de mathématiques supplémentaires, nous donnent plus de flexibilité et permettent également de conserver la sémantique fournie en utilisant un vrai img au lieu d'un div vide.

Définissons d'abord la sémantique HTML, y compris l'utilisation d'une liste non ordonnée comme conteneur des cartes :

 <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>

Ensuite, nous allons créer un ensemble minimal de styles de base pour le composant .card . Nous définirons quelques styles visuels de base pour la carte elle-même, une mise à jour rapide du titre h3 attendu, puis des styles essentiels pour commencer à styliser l'image de la carte.

 .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 dernière règle utilise le combinateur général des frères et sœurs pour ajouter une marge horizontale à tout élément qui suit l' img puisque nous voulons que l'image elle-même soit alignée avec les côtés de la carte.

Et nos progrès jusqu'à présent nous amènent à l'apparence de carte suivante :

Une carte avec les styles de base décrits précédemment appliqués et comprenant une image d'Unsplash d'un dessert sur une petite assiette à côté d'une boisson chaude dans une tasse
Une carte avec les styles de base décrits précédemment appliqués et comprenant une image d'Unsplash d'un dessert sur une petite assiette à côté d'une boisson chaude dans une tasse. ( Grand aperçu )

Enfin, nous allons créer les styles .card-wrapper pour une mise en page réactive rapide à l'aide de la grille CSS. Cela supprimera également les styles de liste par défaut.

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

Remarque : Si cette technique de grille ne vous est pas familière, consultez l'explication de mon tutoriel sur les solutions modernes pour la grille à 12 colonnes.

Avec cela appliqué et avec toutes les cartes contenant une image avec un chemin source valide, nos styles .card-wrapper nous donnent la disposition suivante :

Trois cartes sont affichées à la suite en raison des styles de mise en page de l'enveloppe de carte appliqués. Chaque carte a une image unique qui a différents rapports d'aspect naturels, la dernière carte ayant une image orientée verticalement qui est plus de deux fois la hauteur des autres images de carte
Trois cartes sont affichées à la suite en raison des styles de mise en page de l'enveloppe de carte appliqués. Chaque carte a une image unique qui a différents rapports d'aspect naturels, la dernière carte ayant une image orientée verticalement qui est plus de deux fois la hauteur des autres images de carte. ( Grand aperçu )

Comme le montre l'image d'aperçu, ces styles de base ne suffisent pas pour contenir correctement les images compte tenu de leurs dimensions naturelles variables. Nous avons besoin d'une méthode pour contraindre ces images de manière uniforme et cohérente.

Plus après saut! Continuez à lire ci-dessous ↓

Activer les tailles d'image uniformes avec object-fit

Comme indiqué précédemment, vous avez peut-être précédemment effectué une mise à jour dans ce scénario pour modifier les images à ajouter via background-image à la place et utilisé background-size: cover pour gérer correctement le redimensionnement de l'image. Ou vous avez peut-être essayé d'appliquer le recadrage à l'avance (toujours un objectif louable puisque toute réduction de la taille de l'image améliorera les performances !).

Maintenant, nous avons la propriété object-fit disponible qui permet à une balise img d'agir comme conteneur pour l'image. Et, il est également livré avec une valeur de cover qui donne un effet similaire à la solution d'image d'arrière-plan, mais avec l'avantage de conserver la sémantique d'une image en ligne. Appliquons-le et voyons comment cela fonctionne.

Nous devons l'associer à une dimension de height pour obtenir des indications supplémentaires sur la façon dont nous voulons que le conteneur d'image se comporte (rappelez-vous que nous avions déjà ajouté width: 100% ). Et nous allons utiliser la fonction max() pour sélectionner 10rem ou 30vh selon ce qui est le plus grand dans un contexte donné, ce qui empêche la hauteur de l'image de trop rétrécir sur des fenêtres plus petites ou lorsque l'utilisateur a défini un grand zoom.

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

Astuce Accessibilité Bonus : Vous devriez toujours tester vos mises en page avec un zoom de 200% et 400% sur le bureau. Bien qu'il n'y ait pas actuellement de requête de zoom multimédia, des fonctions telles que max() peuvent aider à résoudre les problèmes de mise en page. Un autre contexte dans lequel cette technique est utile est l'espacement entre les éléments.

Avec cette mise à jour, nous avons définitivement amélioré les choses, et le résultat visuel est comme si nous utilisions l'ancienne technique d'image d'arrière-plan :

Les images à trois cartes semblent maintenant avoir une hauteur uniforme et le contenu de l'image est centré dans l'image comme s'il s'agissait d'un conteneur
Les images à trois cartes semblent maintenant avoir une hauteur uniforme et le contenu de l'image est centré dans l'image comme s'il s'agissait d'un conteneur. ( Grand aperçu )

Dimensionnement d'image cohérent et réactif avec aspect-ratio

Lorsque vous utilisez object-fit seul, un inconvénient est que nous devons encore définir des indications de dimension.

Une propriété à venir (actuellement disponible dans les navigateurs Chromium) appelée aspect-ratio améliorera notre capacité à dimensionner les images de manière cohérente.

En utilisant cette propriété, nous pouvons définir un ratio pour redimensionner l'image au lieu de définir des dimensions explicites. Nous continuerons à l'utiliser en combinaison avec object-fit pour nous assurer que ces dimensions n'affectent que l'image en tant que conteneur, sinon l'image pourrait apparaître déformée.

Voici notre règle d'image entièrement mise à jour :

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

Nous allons commencer avec un rapport d'image de 43 pour notre contexte de carte, mais vous pouvez choisir n'importe quel rapport. Par exemple, 11 pour un carré ou 169 pour les intégrations vidéo standard.

Voici les cartes mises à jour, bien qu'il soit probablement difficile de remarquer la différence visuelle dans ce cas particulier, car le rapport d'aspect correspond étroitement à l'apparence que nous avons obtenue en définissant la height uniquement pour object-fit .

Les images à trois cartes ont des dimensions de largeur et de hauteur identiques, qui sont légèrement différentes de la solution d'ajustement d'objet précédente
Les images à trois cartes ont des dimensions de largeur et de hauteur identiques, qui sont légèrement différentes de la solution d'ajustement d'objet précédente. ( Grand aperçu )

La définition d'un "rapport d'aspect" permet de maintenir le rapport au fur et à mesure que les éléments grandissent ou rétrécissent, alors qu'en définissant uniquement "object-fit" et "height", le rapport d'image sera constamment en évolution à mesure que les dimensions du conteneur changent.

"

Ajout d'effets réactifs avec des dégradés et des fonctions CSS

OK, maintenant que nous savons comment configurer des images de taille cohérente, amusons-nous avec elles en ajoutant un effet de dégradé !

Notre objectif avec cet effet est de donner l'impression que l'image se fond dans le contenu de la carte. Vous pourriez être tenté d'envelopper l'image dans son propre conteneur pour ajouter le dégradé, mais grâce au travail que nous avons déjà fait sur le dimensionnement de l'image, nous pouvons déterminer comment le faire en toute sécurité sur le .card principal.

La première étape consiste à définir un dégradé . Nous allons utiliser une propriété personnalisée CSS pour ajouter les couleurs du dégradé afin de permettre de permuter facilement l'effet de dégradé, en commençant par un bleu à rose. La dernière couleur du dégradé sera toujours blanche pour maintenir la transition vers l'arrière-plan du contenu de la carte et créer le bord « en plumes ».

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

Mais attendez - est-ce une fonction CSS max() ? En dégradé ? Oui, c'est possible, et c'est la magie qui rend ce dégradé efficace en responsive !

Cependant, si je devais ajouter une capture d'écran, nous ne verrions pas encore le dégradé avoir d'effet sur l'image. Pour cela, nous devons intégrer la propriété mix-blend-mode et, dans ce scénario, nous utiliserons la valeur overlay :

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

La propriété mix-blend-mode est similaire à l'application des styles de fusion de calques disponibles dans les logiciels de manipulation de photos tels que Photoshop. Et la valeur de overlay aura pour effet de permettre aux tons moyens de l'image de se fondre avec le dégradé derrière, conduisant au résultat suivant :

Chaque image de carte a un effet de dégradé qui commence par un bleu clair en haut, qui se fond dans un rose rougeâtre, puis se termine par un dégradé de blanc avant le reste du contenu du texte de la carte.
Chaque image de carte a un effet de mélange de dégradé qui commence par un bleu clair en haut, qui se fond en rose rougeâtre, puis se termine par un dégradé de blanc avant le reste du contenu du texte de la carte. ( Grand aperçu )

Maintenant, à ce stade, nous nous appuyons uniquement sur la valeur aspect-ratio pour redimensionner l'image. Et si nous redimensionnons le conteneur et que la mise en page de la carte se redistribue, la modification de la hauteur de l'image provoque des incohérences dans l'endroit où le dégradé devient blanc.

Nous ajouterons donc également une propriété max-height qui utilise également la fonction max() et contient des valeurs légèrement supérieures à celles du dégradé. Le comportement résultant est que le dégradé s'alignera (presque toujours) correctement avec le bas de l'image.

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

Il est important de noter que l'ajout d'une `max-height` modifie le comportement `aspect-ratio`. Au lieu de toujours utiliser le rapport exact, il ne sera utilisé que lorsqu'il y aura suffisamment d'espace alloué compte tenu de la nouvelle contrainte supplémentaire de `max-height`.

"

Cependant, aspect-ratio continuera à garantir que les images sont redimensionnées de manière cohérente, comme c'était le cas par rapport object-fit uniquement. Essayez de commenter le aspect-ratio dans la démo finale de CodePen pour voir la différence qu'il fait entre les tailles de conteneur.

Étant donné que notre objectif initial était d'activer des dimensions d'image toujours réactives , nous avons tout de même atteint le but. Pour votre propre cas d'utilisation, vous devrez peut-être jouer avec les valeurs de rapport et de hauteur pour obtenir l'effet souhaité.

Alternative : mix-blend-mode et ajout d'un filtre

L'utilisation de overlay comme valeur mix-blend-mode était le meilleur choix pour l'effet de fondu au blanc que nous recherchions, mais essayons une autre option pour un effet plus dramatique.

Nous allons mettre à jour notre solution pour ajouter une propriété personnalisée CSS pour la valeur mix-blend-mode et également mettre à jour les valeurs de couleur pour le dégradé :

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

La valeur de multiply a un effet d'assombrissement sur les tons moyens, mais conserve le blanc et le noir tels quels, ce qui donne l'apparence suivante :

Chaque image de carte a une forte teinte orange à partir du nouveau dégradé qui va du rouge-orange à l'orange pur. Les zones blanches sont toujours blanches et les zones noires sont toujours noires
Chaque image de carte a une forte teinte orange à partir du nouveau dégradé qui va du rouge-orange à l'orange pur. Les zones blanches sont toujours blanches et les zones noires sont toujours noires. ( Grand aperçu )

Bien que nous ayons perdu le fondu et que nous ayons maintenant un bord dur au bas de l'image, la partie blanche de notre dégradé est toujours importante pour garantir que le dégradé se termine avant le contenu de la carte.

Une modification supplémentaire que nous pouvons ajouter est l'utilisation de filter et, en particulier, utiliser la fonction grayscale() pour supprimer les couleurs de l'image et donc faire du dégradé la seule source de coloration de l'image.

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

L'utilisation de la valeur de niveaux de grayscale(100) entraîne la suppression complète des couleurs naturelles de l'image et sa transformation en noir et blanc. Voici la mise à jour pour comparaison avec la capture d'écran précédente de son effet lors de l'utilisation de notre dégradé orange avec multiply :

Maintenant, chaque image de carte a toujours le dégradé orange mais toutes les autres couleurs sont supprimées et remplacées par des nuances de gris
Désormais, chaque image de carte a toujours le dégradé orange, mais toutes les autres couleurs sont supprimées et remplacées par des nuances de gris. ( Grand aperçu )

Utiliser aspect-ratio comme une amélioration progressive

Comme mentionné précédemment, aspect-ratio n'est actuellement pris en charge que dans la dernière version des navigateurs Chromium (Chrome et Edge). Cependant, tous les navigateurs prennent en charge object-fit et cela, avec nos contraintes de height , donne un résultat moins idéal mais toujours acceptable, vu ici pour Safari :

La hauteur de l'image de la carte est plafonnée, mais chaque carte a une hauteur réalisée légèrement différente
La hauteur de l'image de la carte est plafonnée, mais chaque carte a une hauteur réalisée légèrement différente. ( Grand aperçu )

Sans le fonctionnement du aspect-ratio , le résultat ici est qu'en fin de compte, la hauteur de l'image est plafonnée, mais les dimensions naturelles de chaque image entraînent toujours une certaine variance entre les hauteurs d'image de la carte. Vous voudrez peut-être plutôt ajouter une max-height ou utiliser à nouveau la fonction max() pour aider à rendre une max-height plus réactive sur différentes tailles de carte.

Extension des effets de dégradé

Puisque nous avons défini les arrêts de couleur de dégradé comme une propriété personnalisée CSS, nous avons un accès facile pour les modifier dans différents contextes. Par exemple, nous pouvons modifier le dégradé pour mettre davantage en valeur l'une des couleurs si la carte est survolée ou si l'un de ses enfants est mis au point.

Tout d'abord, nous allons mettre à jour chaque carte h3 pour qu'elle contienne un lien, tel que :

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

Ensuite, nous pouvons utiliser l'un de nos nouveaux sélecteurs disponibles - :focus-within - pour modifier le dégradé de la carte lorsque le lien est mis au point. Pour une couverture supplémentaire des interactions possibles, nous couplerons ceci avec :hover . Et, nous réutiliserons notre idée max() pour attribuer une seule couleur pour prendre en charge la couverture de la partie image de la carte. L'inconvénient de cet effet particulier est que les arrêts de dégradé et les changements de couleur ne sont pas animables de manière fiable - mais ils le seront bientôt grâce à CSS Houdini.

Pour mettre à jour la couleur et ajouter le nouvel arrêt de couleur, il nous suffit de réaffecter la valeur de --card-gradient dans cette nouvelle règle :

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

Nos valeurs max() sont inférieures à l'original utilisé pour white afin de maintenir le bord biseauté. Si nous utilisions les mêmes valeurs, cela rencontrerait le white et créerait une séparation clairement rectiligne.

Lors de la création de cette démo, j'ai initialement essayé un effet qui utilisait la transform avec scale pour un effet de zoom avant. Mais j'ai découvert qu'en raison de l'application mix-blend-mode , le navigateur ne repeignait pas systématiquement l'image, ce qui provoquait un scintillement désagréable. Il y aura toujours des compromis à demander au navigateur d'effectuer des effets et des animations CSS uniquement, et même si c'est très cool ce que nous pouvons faire, il est toujours préférable de vérifier l'impact de vos effets sur les performances .

Amusez-vous à expérimenter !

Le CSS moderne nous a donné des outils impressionnants pour mettre à jour nos boîtes à outils de conception Web, aspect-ratio étant le dernier ajout. Alors allez-y et expérimentez avec object-fit , aspect-ratio et ajoutez des fonctions comme max() dans vos dégradés pour des effets réactifs amusants ! Assurez-vous simplement de vérifier les choses entre les navigateurs (pour l'instant !) Et dans différentes fenêtres d'affichage et tailles de conteneurs.

Voici le CodePen, y compris les fonctionnalités et les effets que nous avons examinés aujourd'hui :

Voir le stylet [Responsive Image Effects with CSS Gradients and aspect-ratio](https://codepen.io/smashingmag/pen/WNoERXo) de Stephanie Eckles.

Voir les effets d'image réactifs au stylet avec dégradés CSS et rapport d'aspect par Stephanie Eckles.

Vous en voulez plus ? Assurez-vous de consulter notre guide CSS ici sur Smashing →