Premiers pas avec les formes CSS

Encapsuler du contenu autour des chemins d'accès personnalisés

Razvan Caliman
Razvan Caliman

Pendant longtemps, les concepteurs Web ont été obligés de créer en respectant les contraintes du rectangle. La plupart des contenus sur le Web sont encore piégés dans de simples boîtes, car la plupart des aventures créatives dans des mises en page non rectangulaires finissent par être frustrées. Avec l'introduction des formes CSS, disponibles à partir de Chrome 37, cela va changer. Les formes CSS permettent aux concepteurs Web d'encapsuler du contenu autour de tracés personnalisés, tels que des cercles, des ellipses et des polygones, éliminant ainsi les contraintes du rectangle.

Les formes peuvent être définies manuellement ou déduites d'images.

Voyons 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 pensant que le contenu encapsulait et remplissait les lacunes, pour être déçu par la forme enveloppante rectangulaire persistante autour de l'élément. Vous pouvez utiliser des formes CSS pour résoudre ce problème.

Extraire une forme à partir 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 (en pixels) qui sera utilisé 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 d'une opacité de 50% ou plus seront utilisés pour créer la forme.

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 doit s'encapsuler, sans le float, vous ne verrez pas les effets de cette forme.

Les éléments ont une zone flottante de l'autre côté de leur valeur float. Par exemple, si un élément comportant une image de tasse à café est flottant vers la gauche, la zone flottante sera créée à droite de la tasse. Même si vous pouvez créer une image avec des blancs des deux côtés, le contenu n'entourera que la forme du côté opposé désigné par la propriété float, gauche ou droite, jamais les deux.

À l'avenir, vous pourrez utiliser shape-outside sur les éléments qui ne sont pas flottants avec les exclusions CSS d'introduction.

Créer des formes manuellement

En plus d'extraire des formes à partir d'images, vous pouvez également les coder manuellement. Plusieurs valeurs fonctionnelles permettent de créer des formes: circle(), ellipse(), inset() et polygon(). Chaque fonction de forme accepte un ensemble de coordonnées et est associée à un boîtier de référence qui établit le système de coordonnées. Vous en saurez plus sur les boîtes de référence dans quelques instants.

La fonctionCircle()

Illustration de la valeur de la forme loop()

La notation complète pour une valeur de forme de cercle 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 encapsule la partie externe d'un tracé circulaire. L'argument unique 50% spécifie le rayon du cercle, qui, dans ce cas précis, équivaut à 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. Cet exemple illustre la manière dont les formes CSS peuvent être responsives.

Avant d'aller plus loin, gardez à l'esprit 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, il 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 est très pratique, car elle suit la même notation que les formes CSS, ce qui vous permet de réutiliser les valeurs.

Illustration de la forme `circle()` + clip-path

Les illustrations de ce document utilisent le rognage 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 avec 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 circulaire obtenue 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 shape (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);
}

Cela permet de positionner le centre du cercle à l'origine du système de coordonnées. Quel est le système de coordonnées ? C'est ici que nous présentons les encadrés de référence.

Zones 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 doit s'encapsuler. La zone flottante s'étend jusqu'aux bords extérieurs de la zone définie par la propriété margin. Il s'agit de margin-box. Il s'agit de 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, 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 pixels vers le haut et 100 pixels à 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 le champ de référence margin-box.

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

Vous ne pouvez utiliser qu'un seul champ de référence à la fois avec une déclaration shape-outside. Chaque cadre de référence influencera la forme d'une manière différente et parfois subtile. Vous trouverez également dans cet article un autre article expliquant plus en détail les boîtes de référence pour les formes CSS.

La fonction ellipse()

Illustration de la valeur de forme ellipse()

Les points de suspension 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 en pourcentage seront calculées à partir des dimensions du système de coordonnées. Pas besoin de faire des calculs. Vous pouvez omettre les coordonnées du centre de l'ellipse : elles seront déduites du centre du système de coordonnées.

Vous pouvez également définir le rayon sur les axes X et Y à l'aide de mots clés : farthest-side permet d'obtenir un rayon égal à la distance entre le centre de l'ellipse et le côté le plus éloigné de cette zone de référence, tandis que closest-side indique 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 s'avérer utile lorsque les dimensions de l'élément (ou la zone de référence) peuvent changer de manière imprévisible, mais que vous souhaitez que la forme des ellipses 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()

Illustration de la valeur de la forme polygon()

Si les cercles et les ellipses sont trop limitants, la fonction de forme de polygone ouvre une multitude d'options. Le format est polygon(x1 y1, x2 y2, ...). Vous spécifiez des paires de coordonnées x y pour chaque sommet (point) d'un polygone. Un polygone doit comporter au moins trois paires, c'est-à-dire un triangle.

.element{
  shape-outside: polygon(0 0, 0 300px, 300px 600px);
  width: 300px;
  height: 600px;
}

Les sommets sont placés dans 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;
}

Il existe un paramètre fill-rule facultatif, importé depuis SVG, qui indique au navigateur comment prendre en compte 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é de règle de remplissage dans le format SVG. Si aucune valeur n'est définie, fill-rule est défini par défaut sur 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 si l'on considère l'hypothèse initiale selon laquelle les formes CSS libèrent le contenu Web à partir de boîtes simples. C'est peut-être très bien. Je n'ai pas encore trouvé de cas d'utilisation pour inset() qui n'est pas déjà réalisable avec des flottants et des marges ni 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 par rapport aux bords de l'élément. Le dernier argument est l'arrondi de bordure de la forme rectangulaire. Elle est facultative. 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éation de 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'exotique jusqu'à présent, c'est comme ça que les chars d'assaut fonctionnent déjà. 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 coins d'un élément flottant, vous obtenez l'effet de rognage, mais la zone flottante reste rectangulaire. Ajoutez shape-outside: border-box pour encapsuler le contour créé par border-radius.

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

Bien entendu, vous pouvez utiliser tous les encadrés de référence de cette manière. Voici une autre utilisation des formes dérivées : les guillemets pull avec décalage.

Créer un devis d&#39;extraction décalé à l&#39;aide de la zone de référence de zone de contenu

Il est possible d'obtenir l'effet de guillemets pull avec décalage en n'utilisant que les propriétés de flottement et de marge. Toutefois, pour cela, vous devez positionner l'élément "guillemet" dans l'arborescence HTML à l'endroit où vous voulez qu'il s'affiche.

Voici comment obtenir le même effet de guillemets pull avec décalage avec davantage 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 les guillemets d'extraction définit la forme autour de laquelle le contenu extérieur s'encapsulera. La propriété margin-top est utilisée ici pour positionner (décaler) le devis d'extraction, quelle que soit sa position dans l'arborescence HTML.

Marge de la forme

Vous remarquerez que le fait d'encapsuler du contenu autour d'une forme peut le frotter trop étroitement contre l'élément. Vous pouvez ajouter un 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 que vous avez l'habitude d'utiliser avec la propriété margin standard, mais shape-margin n'affecte que l'espace autour de la valeur shape-outside. Il ajoute de l'espace autour de la forme uniquement s'il y a de la place pour celle-ci dans le système de coordonnées. C'est pourquoi, dans l'exemple ci-dessus, l'arrondi du cercle est défini sur 40%, et non sur 50%. Si le rayon avait été 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 sa margin environnante). Si la forme est plus grande et dépasse, elle est rognée sur la margin-box et vous obtenez une forme rectangulaire.

Il est important de comprendre que shape-margin n'accepte qu'une seule valeur positive. Il n'a pas de notation longue. Mais qu'est-ce qu'une forme-marge supérieure pour un cercle ?

Animer des formes

Vous pouvez combiner des formes CSS avec de nombreuses autres fonctionnalités CSS, comme des transitions et des animations. Cependant, je dois souligner que les utilisateurs trouvent cela très agaçant lorsque la mise en page du texte change pendant qu'ils lisent. Portez une attention particulière à l'expérience si vous décidez en faveur des formes animées.

Vous pouvez animer les rayons et les centres des formes circle() et ellipse() tant qu'elles sont définies dans des valeurs que le navigateur peut interpoler. Il est possible de passer de circle(30%) à circle(50%). Toutefois, l'animation entre circle(closest-side) et circle(farthest-side) entraîne un étouffement du navigateur.

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

.element:hover{
  shape-outside: circle(50%);
}
GIF représentant un cercle animé

Vous pouvez obtenir des effets plus intéressants lors de l'animation de formes polygon(). Notez toutefois que le polygone doit avoir le même nombre de sommets entre les deux états d'animation. Le navigateur ne peut pas effectuer d'interpolation 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 voulez moins d'arêtes perçues dans la forme.

.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 représentant un triangle animé

Encapsuler 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

Le brouillon initial de la spécification CSS Shapes comprenait une propriété shape-inside qui vous 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 le contenu positionné arbitrairement dans un chemin personnalisé demande 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 au niveau CSS Shapes Level 2 (Formes CSS de niveau 2) et les implémentations correspondantes ont été retirées.

Cependant, avec un certain effort et un peu de compromis, vous pouvez toujours obtenir l'effet d'encapsuler le contenu dans une forme personnalisée. L'astuce consiste à utiliser deux éléments flottants avec shape-outside, positionnés de chaque côté d'un conteneur. Le compromis est le suivant : vous devez utiliser un ou deux éléments vides qui n'ont pas de sens sémantique, mais qui servent de lacets 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 .left-shape et .right-shape en haut du conteneur est importante, car ils seront flottés vers la gauche et vers la droite afin d'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 d&#39;une solution de contournement pour la démo d&#39;Alice

Avec ce style, les deux tiges flottantes occupent tout l'espace dans l'élément, mais les propriétés shape-outside préservent de l'espace pour le reste du contenu.

Si les formes CSS ne sont pas prises en charge par le navigateur, des effets gênants peuvent être créés en poussant tout le contenu vers le bas. C'est pourquoi il est important d'améliorer progressivement l'utilisation de cette fonctionnalité.

Dans les exemples précédents d'animations de forme, vous remarquerez que le déplacement du texte peut être gênant. Tous les cas d'utilisation ne justifient pas 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 avec Alice au pays des merveilles, nous avons utilisé la position de défilement pour modifier la marge supérieure du contenu. Le texte est placé entre deux éléments flottants. À mesure qu'il descend, il doit être remis en page en fonction du shape-outside des deux éléments flottants. Cela donne l'impression que le texte est en train de se lancer, ce qui améliore l'expérience de storytelling. Vous êtes à la limite d'une utilisation limitée ? 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. Toutefois, le fait de modifier la marge supérieure lors du défilement déclenche de nombreux événements de remise en page et de peinture, ce qui peut sensiblement réduire les performances. À utiliser avec précaution ! Toutefois, l'utilisation de formes CSS sans les animer n'entraîne pas d'impact sur les performances perceptible.

amélioration progressive

Commencez par supposer que le navigateur n'est pas compatible avec les formes CSS et utilisez-le pour détecter cette fonctionnalité. Modernizr est une bonne solution pour détecter les fonctionnalités. Un test des formes CSS est disponible dans la section "Détection d'une autre application".

Certains navigateurs permettent 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.

Clarification des exclusions CSS

Au début de la spécification, ce que nous connaissons aujourd'hui sous le nom de "formes CSS" s'appelait "exclusions" et "formes". Le changement de dénomination peut sembler nuancé, mais il est en fait très important. Les exclusions CSS, désormais une spécification distincte, permettent d'encapsuler du contenu autour d'éléments positionnés arbitrairement, sans avoir besoin d'une propriété flottante. Imaginez que du contenu s'encapsule autour d'un élément positionné de façon absolue. C'est comme cela qu'il s'agit d'un cas d'utilisation des exclusions CSS. Les formes CSS définissent simplement le chemin autour duquel le contenu sera encapsulé.

Ainsi, les formes et les exclusions ne sont pas la même chose, mais elles se complètent. Les formes CSS sont actuellement disponibles dans les navigateurs, mais les exclusions CSS ne sont pas encore implémentées avec l'interaction avec les formes.

Outils d'utilisation des formes CSS

Vous pouvez créer des chemins dans les outils de création d'images classiques, mais aucun d'entre eux, au moment de la rédaction de ce document, n'exporte la syntaxe requise pour les valeurs des formes CSS. Même si c’est le cas, travailler comme ça ne serait pas trop 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 aider dans cette tâche:

Parenthèses: 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 étend les outils pour les développeurs du navigateur avec des commandes permettant de créer et de modifier des formes. Elle place un éditeur interactif par-dessus l'élément sélectionné.

L'outil d'inspection de Google Chrome prend en charge la mise en surbrillance des formes. Pointez sur un élément avec une propriété shape-outside pour qu'il s'allume pour illustrer la forme.

Formes à partir d'images: si vous préférez générer des images et que le navigateur en extrait les formes, Rebecca Hauck a écrit un tutoriel pour Photoshop.

Polyfill: Google Chrome est le premier navigateur principal à proposer des formes CSS. Cette fonctionnalité sera bientôt compatible avec iOS 8 et Safari 8 d'Apple. D'autres fournisseurs de navigateurs pourraient en tenir compte à l'avenir. En attendant, il existe un polyfill CSS Shapes pour fournir une assistance de base.

Conclusion

Sur un site Web où le contenu est la plupart du temps emprisonné dans de simples boîtes, les formes CSS permettent de créer une mise en page expressive, comblant le fossé de fidélité entre la conception Web et la conception papier. Bien sûr, les formes peuvent être détournées et créer des distractions. Mais, lorsqu'elles sont appliquées avec goût et bon jugement, 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 présente une collection de travaux réalisés par d'autres personnes, principalement sur papier, qui illustre des utilisations intéressantes 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 conception.

Un grand merci à Pearl Chen, Alan Stearns et Zoltan Horvath pour avoir lu cet article et partagé des informations précieuses.