Habillage du contenu autour de tracés personnalisés
Pendant longtemps, les concepteurs Web ont été contraints de créer dans les limites du rectangle. La plupart des contenus sur le Web sont encore piégés dans des cadres simples, car la plupart des tentatives de création de mises en page non rectangulaires se soldent par un échec. Cela va changer avec l'introduction des formes CSS, disponibles à partir de Chrome 37. Les formes CSS permettent aux concepteurs Web d'habiller du contenu autour de tracés personnalisés, tels que des cercles, des ellipses et des polygones, s'affranchissant ainsi des contraintes du rectangle.
Les formes peuvent être définies manuellement ou déduites à partir d'images.
Prenons un exemple très simple.
Peut-être avez-vous été aussi naïf que moi lorsque vous avez fait flotter une image avec des parties transparentes pour la première fois, en vous attendant à ce que le contenu s'ajuste et comble les espaces, pour finalement être déçu par la forme rectangulaire de l'habillage qui persiste autour de l'élément. Les formes CSS peuvent être utilisées pour résoudre ce problème.
<img class="element" src="image.png" />
<p>Lorem ipsum…</p>
<style>
.element{
shape-outside: url(image.png);
shape-image-threshold: 0.5;
float: left;
}
</style>
La déclaration CSS shape-outside: url(image.png) indique au navigateur d'extraire une forme de l'image.
La propriété shape-image-threshold définit le niveau d'opacité minimal des pixels qui seront utilisés pour créer la forme. Sa valeur doit être comprise entre 0.0 (totalement transparent) et 1.0 (totalement opaque). Ainsi, shape-image-threshold: 0.5 signifie que seuls les pixels dont l'opacité est supérieure ou égale à 50 % seront utilisés pour créer la forme.
La propriété float est essentielle ici. La propriété shape-outside définit la forme de la zone autour de laquelle le contenu s'enroule, mais sans la propriété float, vous ne verrez pas les effets de la forme.
Les éléments ont une zone flottante du côté opposé à leur valeur float. Par exemple, si un élément avec une image de tasse de café est flottant à gauche, la zone flottante sera créée à droite de la tasse. Même si vous pouvez concevoir une image avec des espaces des deux côtés, le contenu ne s'enroulera autour de la forme que du côté opposé désigné par la propriété float, à gauche ou à droite, jamais les deux.
À l'avenir, il sera possible d'utiliser shape-outside sur des éléments qui ne sont pas flottants grâce à l'introduction des exclusions CSS.
Créer des formes manuellement
En plus d'extraire des formes à partir d'images, vous pouvez également les coder manuellement. Vous pouvez choisir parmi quelques valeurs fonctionnelles pour créer des formes : circle(), ellipse(), inset() et polygon(). Chaque fonction de forme accepte un ensemble de coordonnées et est associée à une boîte de référence qui établit le système de coordonnées. Nous reviendrons sur les zones de référence dans un instant.
La fonction circle()
La notation complète d'une valeur de forme circulaire est circle(r at cx cy), où r est le rayon du cercle, tandis que cx et cy sont les coordonnées du centre du cercle sur l'axe X et l'axe Y. Les coordonnées du centre du cercle sont facultatives. Si vous les omettez, le centre de l'élément (l'intersection de ses diagonales) sera utilisé par défaut.
.element{
shape-outside: circle(50%);
width: 300px;
height: 300px;
float: left;
}
Dans l'exemple ci-dessus, le contenu s'enroule autour d'un tracé circulaire. L'argument unique 50% spécifie le rayon du cercle, qui, dans ce cas précis, correspond à la moitié de la largeur ou de la hauteur de l'élément. La modification des dimensions de l'élément aura une incidence sur le rayon de la forme circulaire. Voici un exemple simple de la façon dont les formes CSS peuvent être responsives.
Avant d'aller plus loin, une petite précision : il est important de se rappeler que les formes CSS n'influencent que la forme de la zone flottante autour d'un élément. Si l'élément a un arrière-plan, celui-ci ne sera pas découpé par la forme. Pour obtenir cet effet, vous devez utiliser les propriétés de CSS Masking, soit clip-path, soit mask-image. La propriété clip-path est très pratique, car elle suit la même notation que les formes CSS, ce qui vous permet de réutiliser des valeurs.
Les illustrations de ce document utilisent le détourage pour mettre en évidence la forme et vous aider à comprendre les effets.
Revenons à la forme circulaire.
Lorsque vous utilisez des pourcentages pour le rayon du cercle, la valeur est en fait calculée à l'aide d'une formule légèrement plus complexe : sqrt(width^2 + height^2) / sqrt(2). Il est utile de comprendre cela, car cela vous aidera à imaginer la forme du cercle obtenu si les dimensions de l'élément ne sont pas égales.
Tous les types d'unités CSS peuvent être utilisés dans les coordonnées de la fonction de forme : px, em, rem, vw, vh, etc. Vous pouvez choisir celui qui est suffisamment flexible ou rigide pour vos besoins.
Vous pouvez ajuster la position du cercle en définissant des valeurs explicites pour les coordonnées de son centre.
.element{
shape-outside: circle(50% at 0 0);
}
Le centre du cercle est ainsi positionné à l'origine du système de coordonnées. Qu'est-ce que le système de coordonnées ? C'est là que nous introduisons les cadres de référence.
Boîtes de référence pour les formes CSS
La boîte de référence est une boîte virtuelle autour de l'élément, qui établit le système de coordonnées utilisé pour dessiner et positionner la forme. L'origine du système de coordonnées se trouve dans l'angle supérieur gauche, l'axe X pointant vers la droite et l'axe Y vers le bas.
N'oubliez pas que shape-outside modifie la forme de la zone flottante autour de laquelle le contenu s'enroule. La zone flottante s'étend jusqu'aux bords extérieurs de la zone définie par la propriété margin. Il s'agit de la margin-box, qui est la boîte de référence par défaut d'une forme si aucune n'est explicitement mentionnée.
Les deux déclarations CSS suivantes donnent des résultats identiques :
.element{
shape-outside: circle(50% at 0 0);
/* identical to: */
shape-outside: circle(50% at 0 0) margin-box;
}
Nous n'avons pas encore défini de marge sur l'élément. À ce stade, on peut supposer que l'origine du système de coordonnées et le centre du cercle se trouvent dans l'angle supérieur gauche de la zone de contenu de l'élément. Cela change lorsque vous définissez une marge :
.element{
shape-outside: circle(50% at 0 0) margin-box;
margin: 100px;
}
L'origine du système de coordonnées se trouve désormais en dehors de la zone de contenu de l'élément (100 px vers le haut et 100 px vers la gauche), tout comme le centre du cercle. La valeur calculée du rayon du cercle augmente également pour tenir compte de la surface accrue du système de coordonnées établi par la zone de référence margin-box.
Une seule boîte de référence peut être utilisée à la fois avec une déclaration shape-outside. Chaque boîte de référence influencera la forme d'une manière différente et parfois subtile. Un autre article approfondit le sujet et vous aide à comprendre les boîtes de référence pour les formes CSS.
La fonction ellipse()
Les ellipses ressemblent à des cercles écrasés. Elles sont définies comme ellipse(rx ry at cx cy), où rx et ry sont les rayons de l'ellipse sur l'axe X et l'axe Y, tandis que cx et cy sont les coordonnées du centre de l'ellipse.
.element{
shape-outside: ellipse(150px 300px at 50% 50%);
width: 300px;
height: 600px;
}
Les valeurs de pourcentage seront calculées à partir des dimensions du système de coordonnées. Pas besoin de faire des calculs compliqués. Vous pouvez omettre les coordonnées du centre de l'ellipse. Elles seront déduites du centre du système de coordonnées.
Les rayons sur les axes X et Y peuvent également être définis avec des mots clés : farthest-side donne un rayon égal à la distance entre le centre de l'ellipse et le côté de la zone de référence le plus éloigné, tandis que closest-side signifie l'exact opposé (utiliser la distance la plus courte entre le centre et un côté).
.element{
shape-outside: ellipse(closest-side farthest-side at 50% 50%);
/* identical to: */
shape-outside: ellipse(150px 300px at 50% 50%);
width: 300px;
height: 600px;
}
Cela peut s'avérer utile lorsque les dimensions de l'élément (ou de la zone de référence) peuvent changer de manière imprévisible, mais que vous souhaitez que la forme de l'ellipse s'adapte.
Les mêmes mots clés farthest-side et closest-side peuvent également être utilisés pour le rayon dans la fonction de forme circle().
La fonction polygon()
Si les cercles et les ellipses sont trop limités, la fonction de forme de polygone ouvre un monde d'options. Le format est polygon(x1 y1, x2 y2, ...), où vous spécifiez des paires de coordonnées x et y pour chaque sommet (point) d'un polygone. Le nombre minimal de paires pour spécifier un polygone est de trois (un triangle).
.element{
shape-outside: polygon(0 0, 0 300px, 300px 600px);
width: 300px;
height: 600px;
}
Les sommets sont placés sur le système de coordonnées. Pour les polygones responsifs, vous pouvez utiliser des valeurs de pourcentage pour certaines ou toutes les coordonnées.
.element{
/* polygon responsive to font-size*/
shape-outside: polygon(0 0, 0 100%, 100% 100%);
width: 20em;
height: 40em;
}
Il existe un paramètre fill-rule facultatif, importé depuis SVG, qui indique au navigateur comment considérer l'intérieur d'un polygone en cas de tracés qui se croisent ou de formes fermées. Joni Trythall explique très bien comment fonctionne la propriété fill-rule dans SVG. Si aucune valeur n'est définie, la valeur par défaut de fill-rule est nonzero.
.element{
shape-outside: polygon(0 0, 0 100%, 100% 100%);
/* identical to: */
shape-outside: polygon(nonzero, 0 0, 0 100%, 100% 100%);
}
La fonction inset()
La fonction de forme inset() vous permet de créer des formes rectangulaires autour desquelles encapsuler du contenu. Cela peut sembler contre-intuitif, étant donné que les formes CSS sont censées libérer le contenu Web des simples boîtes. C'est tout à fait possible. Je n'ai pas encore trouvé de cas d'utilisation pour inset() qui ne soit pas déjà réalisable avec des éléments flottants et des marges, ou avec un polygon(). Toutefois, inset() fournit une expression plus lisible pour les formes rectangulaires que polygon().
La notation complète d'une fonction de forme d'encart est inset(top right bottom left border-radius). Les quatre premiers arguments de position sont des décalages vers l'intérieur à partir des bords de l'élément. Le dernier argument est le rayon de la bordure de la forme rectangulaire. Il est facultatif, vous pouvez donc l'omettre. Il suit la notation abrégée border-radius que vous utilisez déjà en CSS.
.element{
shape-outside: inset(100px 100px 100px 100px);
/* yields a rectangular shape which is 100px inset on all sides */
float: left;
}
Créer des formes à partir de boîtes de référence
Si vous ne spécifiez pas de fonction de forme pour la propriété shape-outside, vous pouvez autoriser le navigateur à déduire une forme à partir de la zone de référence de l'élément. La zone de référence par défaut est margin-box. Rien d'exotique jusqu'à présent, c'est ainsi que fonctionnent déjà les floats. En appliquant cette technique, vous pouvez réutiliser la géométrie d'un élément. Examinons la propriété border-radius.
Si vous l'utilisez pour arrondir les angles d'un élément flottant, vous obtenez l'effet de rognage, mais la zone flottante reste rectangulaire. Ajoutez shape-outside: border-box pour envelopper le contour créé par border-radius.
.element{
border-radius: 50%;
shape-outside: border-box;
float: left;
}
Bien entendu, vous pouvez utiliser toutes les zones de référence de cette manière. Voici une autre utilisation des formes dérivées : les citations en exergue décalées.
Il est possible d'obtenir l'effet de citation décalée en utilisant uniquement les propriétés float et margin. Mais cela nécessite de positionner l'élément de citation dans l'arborescence HTML à l'endroit où vous souhaitez qu'il s'affiche.
Voici comment obtenir le même effet de citation décalée, avec plus de flexibilité :
.pull-quote{
shape-outside: content-box;
margin-top: 200px;
float: left;
}
Nous définissons explicitement la zone de référence content-box pour le système de coordonnées de la forme. Dans ce cas, la quantité de contenu dans la citation en exergue définit la forme autour de laquelle le contenu externe s'enroulera. La propriété margin-top est utilisée ici pour positionner (décaler) la citation, quelle que soit sa position dans l'arborescence HTML.
Marge de la forme
Vous remarquerez que l'habillage du contenu autour d'une forme peut le faire frotter de trop près contre l'élément. Vous pouvez ajouter de l'espacement autour de la forme avec la propriété shape-margin.
.element{
shape-outside: circle(40%);
shape-margin: 1em;
float: left;
}
L'effet est semblable à celui de la propriété margin standard, mais shape-margin n'affecte que l'espace autour de la valeur shape-outside. Il n'ajoute de l'espace autour de la forme que s'il y a de la place dans le système de coordonnées. C'est pourquoi, dans l'exemple ci-dessus, le rayon du cercle est défini sur 40 % et non sur 50 %. Si le rayon était défini sur 50 %, le cercle occuperait tout l'espace du système de coordonnées, ne laissant aucune place pour l'effet de shape-margin. N'oubliez pas que la forme est finalement limitée à la margin-box de l'élément (l'élément et sa margin environnante). Si la forme est plus grande et déborde, elle sera coupée à la margin-box et vous obtiendrez une forme rectangulaire.
Il est important de comprendre que shape-margin n'accepte qu'une seule valeur positive. Il n'existe pas de notation longue. Qu'est-ce qu'une marge supérieure de forme pour un cercle ?
Animer des formes
Vous pouvez combiner les formes CSS avec de nombreuses autres fonctionnalités CSS, telles que les transitions et les animations. Toutefois, je tiens à souligner que les utilisateurs trouvent très agaçant que la mise en page du texte change pendant qu'ils lisent. Si vous décidez d'animer des formes, faites très attention à l'expérience utilisateur.
Vous pouvez animer les rayons et les centres des formes circle() et ellipse() tant qu'ils sont définis dans des valeurs que le navigateur peut interpoler. Il est possible de passer de circle(30%) à circle(50%). Cependant, l'animation entre circle(closest-side) et circle(farthest-side) va saturer le navigateur.
.element{
shape-outside: circle(30%);
transition: shape-outside 1s;
float: left;
}
.element:hover{
shape-outside: circle(50%);
}
Vous pouvez obtenir des effets plus intéressants en animant des formes polygon(), mais notez que le polygone doit avoir le même nombre de sommets entre les deux états d'animation. Le navigateur ne peut pas interpoler si vous ajoutez ou supprimez des sommets.
Une astuce consiste à ajouter le nombre maximal de sommets dont vous avez besoin et à les regrouper dans l'état d'animation où vous souhaitez que la forme comporte moins d'arêtes.
.element{
/* four vertices (looks like rectangle) */
shape-outside: polygon(0 0, 100% 0, 100% 100%, 0 100%);
transition: shape-outside 1s;
}
.element:hover{
/* four vertices, but second and third overlap (looks like triangle) */
shape-outside: polygon(0 0, 100% 50%, 100% 50%, 0 100%);
}
Habiller du contenu dans une forme
La version initiale de la spécification CSS Shapes incluait une propriété shape-inside qui permettait d'habiller le contenu à l'intérieur d'une forme. Il y a même eu des implémentations dans Chrome et Webkit pendant un certain temps. Cependant, encapsuler du contenu positionné de manière arbitraire dans un tracé personnalisé nécessite beaucoup plus d'efforts et de recherches pour couvrir tous les scénarios possibles et éviter les bugs. C'est pourquoi la propriété shape-inside a été reportée à CSS Shapes Level 2 et ses implémentations ont été supprimées.
Toutefois, avec un peu d'effort et de compromis, vous pouvez toujours obtenir l'effet d'encapsuler du contenu dans une forme personnalisée. L'astuce consiste à utiliser deux éléments flottants avec shape-outside, positionnés de part et d'autre d'un conteneur. Le compromis est que vous devez utiliser un ou deux éléments vides qui n'ont pas de signification sémantique, mais qui servent de supports pour créer l'illusion d'une forme à l'intérieur.
<div>
<div class='left-shape'></div>
<div class='right-shape'></div>
Lorem ipsum...
</div>
La position des éléments d'espacement .left-shape et .right-shape en haut du conteneur est importante, car ils seront flottants à gauche et à droite pour encadrer le contenu.
.left-shape{
shape-outside: polygon(0 0, ...);
float: left;
width: 50%;
height: 100%;
}
.right-shape{
shape-outside: polygon(50% 0, ...);
float: right;
width: 50%;
height: 100%;
}
Ce style permet aux deux éléments flottants d'occuper tout l'espace de l'élément, mais les propriétés shape-outside libèrent de l'espace pour le reste du contenu.
Si les formes CSS ne sont pas prises en charge par le navigateur, cela produira des effets disgracieux en repoussant tout le contenu vers le bas. C'est pourquoi il est important d'utiliser la fonctionnalité de manière progressivement améliorée.
Dans les exemples d'animation de forme précédents, vous remarquerez que le déplacement du texte peut être gênant. Tous les cas d'utilisation ne justifient pas une forme animée. Mais vous pouvez animer d'autres propriétés qui interagissent avec les formes CSS pour ajouter un effet là où cela est pertinent.
Dans la démonstration Alice au pays des merveilles des formes CSS, nous avons utilisé la position de défilement pour modifier la marge supérieure du contenu. Le texte est coincé entre deux éléments flottants. Lorsqu'il se déplace vers le bas, il doit être réorganisé en fonction de la shape-outside des deux éléments flottants. Cela donne l'impression que le texte s'enfonce dans un terrier de lapin et ajoute à l'expérience de storytelling. À la limite du sans frais ? Peut-être. Mais ça a l'air cool.
Comme la mise en page du texte est effectuée de manière native par le navigateur, les performances sont meilleures qu'avec une solution JavaScript. Cependant, la modification de la marge supérieure lors du défilement déclenche de nombreux événements de relayout et de peinture, ce qui peut réduire considérablement les performances. À utiliser avec précaution ! Cependant, l'utilisation de formes CSS sans les animer n'entraîne pas de baisse de performances perceptible.
Amélioration progressive
Commencez par supposer que le navigateur n'est pas compatible avec les formes CSS, puis tenez-en compte lorsque vous détectez la fonctionnalité. Modernizr est une bonne solution pour détecter les fonctionnalités. Un test pour les formes CSS est disponible dans la section Non-core detects (Détections non essentielles).
Certains navigateurs permettent de détecter les fonctionnalités en CSS via la règle @supports, sans avoir besoin de bibliothèques externes. Google Chrome, qui est également compatible avec les formes CSS, comprend la règle @supports. Voici comment l'utiliser pour l'amélioration progressive :
.element{
/* styles for all browsers */
}
@supports (shape-outside: circle(50%)){
/* styles only for browsers which support CSS Shapes */
.element{
shape-outside: circle(50%);
}
}
Lea Verou a écrit un article sur l'utilisation de la règle CSS @supports.
Désambiguïsation des exclusions CSS
Ce que nous appelons aujourd'hui "formes CSS" s'appelait "exclusions et formes CSS" au début de la spécification. Le changement de nom peut sembler être une nuance, mais il est en réalité très important. Les exclusions CSS, qui constituent désormais une spécification distincte, permettent d'habiller du contenu autour d'éléments positionnés de manière arbitraire, sans avoir besoin d'une propriété float. Imaginez que vous souhaitiez envelopper du contenu autour d'un élément positionné de manière absolue : c'est un cas d'utilisation des exclusions CSS. Les formes CSS définissent simplement le chemin autour duquel le contenu s'enroulera.
Les formes et les exclusions ne sont donc pas la même chose, mais elles sont complémentaires. Les formes CSS sont disponibles dans les navigateurs aujourd'hui, tandis que les exclusions CSS ne sont pas encore implémentées avec l'interaction des formes.
Outils pour travailler avec les formes CSS
Vous pouvez créer des tracés dans les outils de création d'images classiques, mais aucun d'entre eux, au moment de la rédaction de cet article, n'exporte la syntaxe requise pour les valeurs de formes CSS. Même si c'était le cas, travailler de cette façon ne serait pas très pratique.
Les formes CSS sont destinées à être utilisées dans le navigateur, où elles réagissent aux autres éléments de la page. Il est très utile de visualiser les effets de la modification de la forme sur le contenu qui l'entoure. Voici quelques outils pour vous aider à suivre ce workflow :
Brackets : l'extension CSS Shapes Editor pour Brackets utilise le mode d'aperçu en direct de l'éditeur de code pour superposer un éditeur interactif permettant de modifier les valeurs des formes.
Google Chrome : l'extension CSS Shapes Editor pour Google Chrome ajoute des commandes aux outils pour les développeurs du navigateur afin de créer et de modifier des formes. Il place un éditeur interactif au-dessus de l'élément sélectionné.
L'inspecteur de Google Chrome est compatible de manière intégrée avec la mise en surbrillance des formes. Pointez sur un élément avec une propriété shape-outside pour qu'il s'illumine et illustre la forme.
Formes à partir d'images : si vous préférez générer des images et laisser le navigateur en extraire des formes, Rebecca Hauck a écrit un bon tutoriel pour Photoshop.
Polyfill : Google Chrome est le premier navigateur majeur à proposer les formes CSS. La fonctionnalité sera bientôt compatible avec iOS 8 et Safari 8 d'Apple. D'autres fournisseurs de navigateurs pourraient l'envisager à l'avenir. En attendant, un polyfill CSS Shapes fournit une compatibilité de base.
Conclusion
Sur un Web où le contenu est principalement enfermé dans des boîtes simples, les formes CSS permettent de créer des mises en page expressives, comblant ainsi l'écart de fidélité entre la conception Web et la conception imprimée. Bien sûr, les formes peuvent être utilisées de manière abusive et créer des distractions. Cependant, lorsqu'elles sont utilisées avec goût et discernement, les formes peuvent améliorer la présentation du contenu et attirer l'attention de l'utilisateur d'une manière qui lui est propre.
Je vous laisse avec une collection de travaux d'autres personnes, principalement issus de l'impression, qui illustrent des utilisations intéressantes de la mise en page non rectangulaire. J'espère que cela vous incitera à essayer les formes CSS et à tester de nouvelles idées de conception.
Un grand merci à Pearl Chen, Alan Stearns et Zoltan Horvath pour avoir examiné cet article et fourni des informations précieuses.