Premiers pas avec les formes CSS

Encapsuler du contenu autour de chemins personnalisés

Razvan Caliman
Razvan Caliman

Pendant longtemps, les concepteurs Web ont été contraints de créer en respectant les contraintes du rectangle. La plupart des contenus sur le Web sont toujours enfermés dans des cases simples, car la plupart des tentatives créatives de mise en page non rectangulaire se terminent par de la frustration. Cela va changer avec l'introduction des formes CSS, disponibles à partir de Chrome 37. Les formes CSS permettent aux concepteurs Web d'encapsuler le contenu autour de chemins personnalisés, tels que des cercles, des ellipses et des polygones, et ainsi de s'affranchir des contraintes du rectangle.

Les formes peuvent être définies manuellement ou inférées à partir d'images.

Prenons un exemple très simple.

Peut-être avez-vous été aussi naïf que moi lorsque vous avez fait flotter pour la première fois une image avec des parties transparentes en attendant que le contenu enveloppe et remplisse les espaces vides, pour être déçu par la forme enveloppée rectangulaire qui persiste autour de l'élément. Les formes CSS peuvent être utilisées pour résoudre ce problème.

Extraire une forme d'une image
<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.

Dans ce cas, la propriété float est essentielle. Bien que la propriété shape-outside définisse la forme de la zone autour de laquelle le contenu sera mis en forme, sans le flottant, vous ne verrez pas les effets de la forme.

Les éléments ont une zone de flottaison de l'autre côté de leur valeur float. Par exemple, si un élément contenant une image de tasse de café est flottant à gauche, la zone flottante est créée à droite de la tasse. Même si vous pouvez créer une image avec des espaces de chaque côté, le contenu ne s'enroule que autour de la forme sur le côté opposé désigné par la propriété float, à gauche ou à droite, mais jamais les deux.

À l'avenir, vous pourrez utiliser shape-outside sur des éléments qui ne sont pas flottants avec 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. Plusieurs valeurs fonctionnelles sont disponibles pour créer des formes: circle(), ellipse(), inset() et polygon(). Chaque fonction de forme accepte un ensemble de coordonnées et est associée à un cadre de référence qui établit le système de coordonnées. Nous reviendrons sur les encadrés de référence dans un instant.

Fonction circle()

Illustration de la valeur de forme circle()

La notation complète d'une valeur de forme circulaire est circle(r at cx cy), où r correspond au 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 entoure l'extérieur 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 influence le rayon de la forme du cercle. Il s'agit d'un exemple de base de la façon dont les formes CSS peuvent être responsives.

Avant d'aller plus loin, notez que les formes CSS n'affectent que la forme de la zone de flottaison autour d'un élément. Si l'élément comporte un arrière-plan, celui-ci ne sera pas rogné par la forme. Pour obtenir cet effet, vous devez utiliser les propriétés du masquage CSS : clip-path ou mask-image. La propriété clip-path s'avère très pratique, car elle suit la même notation que les formes CSS. Vous pouvez donc réutiliser les valeurs.

Illustration de la forme &quot;circle()&quot; et de la propriété clip-path

Les illustrations de ce document utilisent le masquage pour mettre en évidence la forme et vous aider à comprendre les effets.

Retour à 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 qui en résultera 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 celle qui est suffisamment flexible ou rigide pour répondre à 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 placé à l'origine du système de coordonnées. Quel est le système de coordonnées ? C'est là que nous introduisons les encadrés de référence.

Boîtes de référence pour les formes CSS

Le cadre de référence est un cadre virtuel 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 son coin supérieur gauche, l'axe X pointant vers la droite et l'axe Y pointant vers le bas.

Système de coordonnées pour les formes CSS

N'oubliez pas que shape-outside modifie la forme de la zone flottante autour de laquelle le contenu sera encapsulé. La zone de flottaison 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 zone 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 pour l'élément. À ce stade, nous pouvons supposer que l'origine du système de coordonnées et le centre du cercle se trouvent dans le coin supérieur gauche de la zone de contenu de l'élément. Ce comportement 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 l'augmentation de la surface du système de coordonnées établi par la zone de référence margin-box.

Système de coordonnées de la marge-box avec et sans marge
Vous pouvez choisir parmi plusieurs options de boîte de référence : "margin-box", "border-box", "padding-box" et "content-box". Leurs noms impliquent leurs limites. Nous avons déjà expliqué la "margin-box". La "border-box" est limitée par les bords extérieurs des bordures de l'élément, la "padding-box" est limitée par la marge intérieure de l'élément, tandis que la "content-box" est identique à la surface réelle utilisée par le contenu d'un élément.
Illustration de tous les champs de référence

Une seule zone de référence peut être utilisée à la fois avec une déclaration shape-outside. Chaque zone de référence influence la forme d'une manière différente et parfois subtile. Vous trouverez un autre article qui approfondit le sujet et vous aidera à comprendre les champs de référence pour les formes CSS.

La fonction ellipse()

Illustration de la valeur de forme 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 les axes X et 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 calculs rigolos ici. Vous pouvez omettre les coordonnées du centre de l'ellipse. Elles seront alors déduites du centre du système de coordonnées.

Les rayons sur les axes X et Y peuvent également être définis à l'aide de mots clés : farthest-side génère un rayon égal à la distance entre le centre de l'ellipse et le côté du cadre de référence le plus éloigné, tandis que closest-side signifie exactement l'inverse : utilisez 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 être 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().

Fonction polygone()

Illustration de la valeur de la forme polygon()

Si les cercles et les ellipses sont trop limités, la fonction de forme de polygone offre une multitude d'options. Le format est polygon(x1 y1, x2 y2, ...), dans lequel 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, soit 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 réactifs, vous pouvez utiliser des valeurs en pourcentage pour tout ou partie des coordonnées.

.element{
  /* polygon responsive to font-size*/
  shape-outside: polygon(0 0, 0 100%, 100% 100%);
  width: 20em;
  height: 40em;
}

Un paramètre fill-rule facultatif, importé depuis le format SVG, indique au navigateur comment considérer l'intérieur d'un polygone en cas de tracés s'intersectant ou de formes fermées. Joni Trythall explique très bien comment fonctionne la propriété fill-rule dans SVG. Si elle n'est pas 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%);
}

Fonction inset()

La fonction de forme inset() vous permet de créer des formes rectangulaires autour desquelles encapsuler le contenu. Cela peut sembler contre-intuitif compte tenu de la prémisse initiale selon laquelle les formes CSS libèrent le contenu Web des boîtes simples. C'est très possible. Je n'ai pas encore trouvé de cas d'utilisation de inset() qui n'est pas déjà réalisable avec des floats et des marges ou avec une polygon(). Toutefois, inset() fournit une expression plus lisible pour les formes rectangulaires que polygon().

La notation complète d'une fonction de forme en 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 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 cadres 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'extraordinaire jusqu'à présent, car c'est ainsi que fonctionnent déjà les flottants. Toutefois, 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 d'écrêtage, mais la zone flottante reste rectangulaire. Ajoutez shape-outside: border-box pour encapsuler le contour créé par border-radius.

Extraire une forme à partir de l&#39;arrondi de bordure d&#39;un élément à l&#39;aide de la zone de référence de cadre de bordure
.element{
  border-radius: 50%;
  shape-outside: border-box;
  float: left;
}

Bien entendu, vous pouvez utiliser toutes les cases de référence de cette manière. Voici une autre utilisation des formes dérivées : les citations en retrait.

Créer une citation en retrait à l&#39;aide de la zone de référence de la zone de contenu

Il est possible d'obtenir l'effet de guillemet droit en n'utilisant que les propriétés de float et de marge. Toutefois, pour cela, vous devez placer l'élément "guillemet" 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 le guillemet tiré définit la forme autour de laquelle le contenu extérieur sera enveloppé. 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 qu'encapsuler le contenu autour d'une forme peut le faire frotter trop près de l'élément. Vous pouvez espacer la forme avec la propriété shape-margin.

.element{
  shape-outside: circle(40%);
  shape-margin: 1em;
  float: left;
}

L'effet est semblable à celui que vous connaissez avec la propriété margin standard, mais shape-margin n'affecte que l'espace autour de la valeur shape-outside. L'espacement n'est ajouté 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 aurait occupé tout l'espace du système de coordonnées, ne laissant aucune place à 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 son margin environnant). Si la forme est plus grande et qu'elle déborde, elle sera tronqué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'a pas de notation manuscrite. À quoi sert la marge supérieure de la forme pour un cercle ?

Animer des formes

Vous pouvez combiner des formes CSS avec de nombreuses autres fonctionnalités CSS, telles que des transitions et des animations. Toutefois, je dois souligner que les utilisateurs trouvent très gênant que la mise en page du texte change pendant qu'ils lisent. Si vous décidez d'animer des formes, soyez particulièrement attentif à l'expérience.

Vous pouvez animer les rayons et les centres des formes circle() et ellipse(), à condition qu'ils soient définis dans des valeurs que le navigateur peut interpoler. Il est possible de passer du circle(30%) au circle(50%). Cependant, l'animation entre circle(closest-side) et circle(farthest-side) va étouffer le navigateur.

.element{
  shape-outside: circle(30%);
  transition: shape-outside 1s;
  float: left;
}

.element:hover{
  shape-outside: circle(50%);
}
GIF d&#39;un cercle animé

Vous pouvez obtenir des effets plus intéressants lorsque vous animez des formes polygon(). 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 ait moins d'arêtes perçus.

.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%);
}
GIF d&#39;un triangle animé

Encapsulation du contenu dans une forme

Capture d&#39;écran de la démonstration d&#39;Alice au pays des merveilles utilisant des formes CSS pour encapsuler le contenu

La version initiale de la spécification des formes CSS incluait une propriété shape-inside qui permettait d'encapsuler du contenu dans une forme. Il y a même eu des implémentations dans Chrome et Webkit pendant un certain temps. Toutefois, encapsuler du contenu positionné de manière arbitraire dans un chemin 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 que les implémentations correspondantes ont été retirées.

Toutefois, avec un peu d'effort et de compromis, vous pouvez toujours obtenir l'effet d'encapsulation du contenu dans une forme personnalisée. Le piratage 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 sens sémantique, mais qui servent de pattes 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 de strut .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%;
}
Illustration du correctif pour la fonctionnalité shape-inside pour la démonstration d&#39;Alice

Ce style fait que les deux poutres flottantes occupent tout l'espace de l'élément, mais les propriétés shape-outside dégagent de l'espace pour le reste du contenu.

Si le navigateur n'est pas compatible avec les formes CSS, cela aura des effets indésirables en poussant tout le contenu vers le bas. C'est pourquoi il est important d'utiliser cette fonctionnalité de manière progressive.

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 l'utilisation d'une forme animée. Toutefois, vous pouvez animer d'autres propriétés qui interagissent avec les formes CSS pour ajouter un effet le cas échéant.

Dans la démonstration des formes CSS Alice au pays des merveilles, nous avons utilisé la position de défilement pour modifier la marge supérieure du contenu. Le texte est compressé entre deux éléments flottants. Lorsqu'il descend, il doit se réorganiser en fonction de la shape-outside des deux éléments flottants. Cela donne l'impression que le texte descend dans le terrier du lapin, ce qui renforce l'expérience de narration. à la limite du gratuit ? Peut-être. Mais ça a l'air cool.

Comme la mise en page du texte est effectuée en mode natif par le navigateur, les performances sont meilleures qu'avec une solution JavaScript. Toutefois, modifier la marge supérieure lors du défilement déclenche de nombreux événements de recomposition et de peinture, ce qui peut réduire considérablement les performances. À utiliser avec précaution ! Toutefois, l'utilisation de formes CSS sans les animer n'entraîne pas de baisse de performances perceptible.

Amélioration progressive

Commencez par partir du principe que le navigateur n'est pas compatible avec les formes CSS, puis développez-les lorsque vous détectez la fonctionnalité. Modernizr est une bonne solution pour détecter des caractéristiques. Un test des formes CSS est disponible dans la section Détections non liées aux cœurs.

Certains navigateurs fournissent la détection de 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 améliorer progressivement:

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

Disambiguation des exclusions CSS

Ce que nous appelons aujourd'hui "formes CSS" s'appelait "exclusions et formes CSS" au début de la spécification. Ce changement de dénomination peut sembler être une nuance, mais il est en réalité très important. Les exclusions CSS, désormais spécifiées séparément, permettent d'encapsuler le contenu autour d'éléments positionnés de manière arbitraire, sans avoir besoin d'une propriété de flottaison. Imaginez encapsuler du contenu autour d'un élément positionné de manière absolue. Il s'agit d'un cas d'utilisation des exclusions CSS. Les formes CSS ne définissent que le chemin autour duquel le contenu sera mis en forme.

Les formes et les exclusions ne sont donc pas la même chose, mais elles se complètent. Les formes CSS sont disponibles dans les navigateurs, 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 des outils d'authoring d'images classiques, mais aucun d'entre eux n'exporte la syntaxe requise pour les valeurs des formes CSS au moment de la rédaction de cet article. Même si c'était le cas, travailler de cette manière 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. Plusieurs outils peuvent vous y aider:

Brackets : l'extension Éditeur de formes CSS 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 de forme.

Google Chrome: l'extension de l'éditeur de formes CSS pour Google Chrome complète les outils pour les développeurs du navigateur en y ajoutant des commandes permettant de créer et de modifier des formes. Il place un éditeur interactif au-dessus de l'élément sélectionné.

L'outil d'inspection de Google Chrome est compatible avec la mise en surbrillance des formes. Pointez sur un élément avec une propriété shape-outside. Il s'illumine pour illustrer la forme.

Formes à partir d'images : si vous préférez générer des images et demander au navigateur d'en extraire des formes, Rebecca Hauck a rédigé un bon tutoriel pour Photoshop.

Polyfill : Google Chrome est le premier grand navigateur à proposer les formes CSS. La fonctionnalité sera bientôt disponible sur iOS 8 et Safari 8 d'Apple. D'autres fournisseurs de navigateurs pourraient l'envisager à l'avenir. En attendant, un polyfill CSS Shapes est disponible pour fournir une compatibilité de base.

Conclusion

Sur un Web où le contenu est principalement piégé dans des cases simples, les formes CSS permettent de créer une mise en page expressive, comblant l'écart de fidélité entre la conception Web et la conception papier. Bien sûr, les formes peuvent être utilisées de manière abusive et créer des distractions. Toutefois, lorsqu'elles sont appliquées avec goût et bon sens, les formes peuvent améliorer la présentation du contenu et attirer l'attention de l'utilisateur de manière unique.

Je vous laisse une collection d'œuvres réalisées par d'autres personnes, principalement sous forme imprimée, qui illustre des cas d'utilisation intéressants de la mise en page non rectangulaire. J'espère que cela vous donnera envie d'essayer les formes CSS et de tester de nouvelles idées de design.

Un grand merci à Pearl Chen, Alan Stearns et Zoltan Horvath pour la lecture de cet article et les précieuses informations fournies.