CSS Shapes, une introduction

L'article de Sara Soueidan paru dans A List Apart a fait le buzz dernièrement. Sara présente ici avec clarté ce qui sera l'avenir des formes et du design dans CSS.

Par

Des rectangles dans des rectangles, telle est depuis toujours la structure de nos pages web. Depuis longtemps, nous tentons de nous libérer de leurs limitations en utilisant CSS pour créer des formes géométriques, mais ces formes n'ont jamais affecté le contenu situé à l'intérieur des éléments, ni la façon dont ces éléments affectent les autres éléments de la page.

La nouvelle spécification CSS Shapes change tout cela. Proposée par Adobe à la mi-2012, elle vise à fournir aux designers web une nouvelle façon de modifier le flux du contenu, à l'intérieur et autour de formes arbitrairement complexes - ce que nous n'avons jamais pu réaliser auparavant, pas même avec Javascript.

Dans l'image qui suit par exemple, remarquez comment le texte entoure les images circulaires. Sans Shapes, le texte serait rectangulaire, détruisant la touche sophistiquée qui élève son design à un niveau supérieur.

photos de recettes de cuisine, les textes suivent le contour des bols et des assiettes
Notez comment le texte suit la forme du bol dans cet exemple. Avec CSS Shapes, nous pouvons créer le même genre d'effets pour le web.

Regardons maintenant la façon dont fonctionne Shapes, et comment vous pourrez l'utiliser.

Compatibilité navigateurs

Pour l'instant, CSS Shapes est supporté par Webkit Nightly et par Chrome Canary, mais le module niveau 1 en est au statut de Recommandation, donc les propriétés et la syntaxe définies dans les spécifications sont stables et devraient être mises en oeuvre d'ici peu par les autres navigateurs. Ce niveau se concentre sur les propriétés qui modifient le flux du contenu autour d'une forme, et plus précisément sur la propriété shape-outside et celles qui lui sont associées.

Combinées avec d'autres fonctions de pointe telles que les masques (Clipping et Masking), les filtres CSS, et la composition d'images (Compositing et Blending), CSS Shapes va nous permettre de créer des design plus soignés et sophistiqués sans avoir à passer par des éditeurs graphiques comme Photoshop ou InDesign.

Les prochains niveaux de CSS Shapes concerneront l'insertion d'un contenu à l'intérieur d'une forme. Par exemple, aujourd'hui il est facile de créer un losange en CSS : il suffit d'appliquer à notre élément une rotation de 45° puis une rotation inverse au contenu situé à l'intérieur, de façon à ce qu'il retrouve son alignement horizontal sur la page. Cependant, le contenu ne sera pas affecté par la forme de son contenant et il restera rectangulaire. Lorsque la propriété shape-inside de CSS Shapes sera mise en oeuvre, le contenu pourra épouser la forme de son contenant, et nous aurons des design qui ressembleront à celui-ci.

les textes épousent la forme de losange de leur contenant
Bientôt, CSS Shapes permettra au texte situé à l'intérieur de formes telles que ces losanges, de s'ajuster aux limites de son contenant, au lieu de déborder ou d'être tronqué.

Pour utiliser CSS Shapes dans Chrome Canary, il vous faudra activer le flag “fonctions expérimentales”. Si vous n'êtes pas sûr de la marche à suivre, vous pouvez consulter cet article sur le blog d'Adobe.

Créer une forme avec CSS Shapes

Pour appliquer une forme à un élément, on utilise une des propriétés de Shapes et on lui passe une valeur qui est une fonction. C'est dans cette fonction que vous passerez les arguments définissant la forme que vous voulez donner à votre élément.

exemple de syntaxe CSS Shapes
Successivement : la propriété, la valeur (qui est une fonction), les paramètres de la fonction

Les formes seront créées en utilisant l'une des fonctions suivantes :

  • circle()
  • ellipse()
  • inset()
  • polygon()

Chaque forme est définie par un ensemble de points. Certaines fonctions utilisent les points comme paramètres, d'autres utilisent des paramètres relatifs, mais toutes finissent par dessiner une forme comme un ensemble de points. Nous entrerons dans le détail des paramètres de chaque fonction dans les exemples qui suivent.

Une forme peut également être définie en l'extrayant d'une image, par un canal alpha. Lorsqu'on passe une image comme valeur d'une propriété Shapes, le navigateur extrait la forme de l'image à partir de shape-image-threshold (seuil), comme nous le verrons tout à l'heure. La forme est définie par les pixels dont la valeur est supérieure au seuil. L'image doit être compatible CORS (Cross-Origin Resource Sharing). Si l'image ne peut pas être affichée (par exemple si elle n'existe pas), aucune forme ne sera appliquée.

Les propriétés Shapes qui acceptent pour valeur ces fonctions sont :

  • shape-outside: Le contenu extérieur s'adapte à la forme
  • shape-inside: Le contenu intérieur s'adapte à la forme

Vous pouvez utiliser la propriété shape-outside avec la propriété shape-margin pour ajouter une marge autour de la forme pour créer plus d'espace entre elle et le contenu. De la même façon, shape-inside fonctionne avec une propriété complémentaire shape-padding qui ajoute un padding à l'intérieur.

Déclarer une forme sur un élément peut se faire en une seule ligne de CSS :

.element {
    shape-outside: circle(); /* le contenu s'enroulera autour du cercle défini sur l'élément */
}

ou bien :

.element {
    shape-outside: url(chemin/vers/image-avec-forme.png);
}

mais...

Pour que la forme soit appliquée à votre élément, il faut remplir deux conditions :

  1. L'élément doit être flotté. Il en sera peut-être différemment à l'avenir, mais pour l'instant, hors float point de salut.
  2. L'élément doit avoir des dimensions définies. Les hauteur et largeur de l'élément seront en effet utilisées pour établir un système de coordonnées.

Comme nous l'avons vu, les formes sont définies par un ensemble de points. Ces points ayant des coordonnées, le navigateur a besoin d'un système de coordonnées pour positionner chaque point sur l'élément. L'exemple qui précède fonctionnera donc si nous incluons quelque chose comme :

.element {
    float: left;
    height: 10em;
    width: 15em;
    shape-outside: circle();
}

Le fait de donner à un élément des dimensions spécifiques n'affecte pas son caractère responsif (nous y reviendrons).

Chaque forme étant définie par un ensemble de points positionnés en utilisant une paire de coordonnées, toute modification des coordonnées d'un point aura un effet sur la forme créée. Par exemple, l'image suivante montre une forme hexagonale créée à l'aide de la fonction polygon() . La forme consiste en six points. Modifier la coordonnée horizontale du point orange résultera en une forme différente, et affectera le flux de contenu à l'intérieur et/ou à l'extérieur de l'élément.

modification des coordonnées
Si l'élément est flotté à droite et que cette forme lui est appliquée, le flux du contenu situé sur sa gauche sera modifié si les coordonnées du point orange sont changées dans la fonction polygon().

La boîte de référence

Les formes CSS sont définies et créées à l'intérieur d'une boîte de référence, qui est la boîte utilisée pour dessiner la forme. En plus des hauteur et largeur de l'élément, les boîtes (nommées d'après le modèle de la boîte) - margin box, content box, padding box, et border box - sont utilisées pour spécifier l'étendue de la forme sur l'élément.

Par défaut, la référence est margin box, donc si l'élément auquel vous appliquez une forme a une marge en bas, la forme que vous définissez ne se limitera pas aux dimensions de l'élément lui-même, elle s'étendra jusqu'à la marge en question. Si vous voulez utiliser une des autres valeurs de boîtes, vous pouvez l'indiquer en même temps que la fonction passée dans les propriétés :

shape-outside: circle(250px at 50% 50%) padding-box;

Le mot-clé padding-box dans la règle ci-dessus indique que la forme sera appliquée et restreinte à la padding box de l'élément. La fonction circle() définit une forme circulaire, sa taille et sa position sur l'élément.

Définir des formes avec les fonctions

Commençons avec un avatar circulaire entouré de texte, les profils d'utilisateurs ou les témoignages de consommateurs sont souvent présentés ainsi.

un avatar, avec css shapes
Avec CSS Shapes, le texte s'enroule autour de la forme au lieu de rester rectangulaire

Nous allons utiliser la fonction circle() pour appliquer une forme circulaire à l'image de profil en utilisant le balisage suivant :

//HTML

<img src="http://api.randomuser.me/0.3.2/portraits/men/7.jpg" alt="profile image" />

<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Harum itaque nam blanditiis eveniet enim eligendi quae adipisci?</p>

<p>Assumenda blanditiis voluptas tempore porro quibusdam beatae deleniti quod asperiores sapiente dolorem error! Quo nam quasi soluta reprehenderit laudantium optio ipsam ducimus consequatur enim fuga quibusdam mollitia nesciunt modi.</p>

Peut-être vous demandez-vous pourquoi nous n'utilisons pas border-radius pour arrondir l'image ? La réponse est que la propriété border-radius n'a aucun effet sur le flux ou la superficie du contenu interne ou externe, il n'agit que sur la bordure d'un élément et sur le background. Ce dernier s'adapte en fonction des modifications de bordure, et c'est tout. Le contenu interne demeure rectangulaire et le texte à l'extérieur voit et traite l'élément comme s'il était rectangulaire - parce qu'en fait il l'est toujours.

Utilisons la propriété border-radius pour rendre l'image circulaire - comme on le fait habituellement pour arrondir une image ou un élément :

img {
    float: left;
    width: 150px;
    height: 150px;
    border-radius: 50%;
    margin-right: 15px;
}
le même avatar, avec border-radius
Sans CSS Shapes, le texte voit l'image comme un rectangle, il entoure une forme rectangulaire et non circulaire.

Dans un navigateur non compatible avec CSS Shapes, le contenu extérieur se positionnera autour de l'image comme si elle n'était pas circulaire. Avec les navigateurs anciens, ce sera donc la solution de remplacement (fallback).

Pour adapter le flux de contenu à une certaine forme, vous pouvez utiliser les propriétés Shapes :

img {
    float: left;
    width: 150px;
    height: 150px;
    border-radius: 50%;
    margin-right: 15px;

    shape-outside: circle();
    shape-margin: 15px;
}

De cette façon, le texte “verra” la forme circulaire appliquée à l'image et se lovera autour d'elle, comme dans la première illustration. En cas d'incompatibilité navigateur, c'est la deuxième illustration qui s'affichera.

On peut utiliser la fonction circle() telle quelle, ou lui passer des paramètres. La syntaxe officielle est :

circle() = circle([<shape-radius]? [at <position>]?)

Le point d'interrogation indique que ces paramètres sont optionnels et peuvent être omis. En cas d'omission d'un paramètre, celui-ci prend une valeur par défaut. Lorsque vous utilisez circle() tel quel, sans définir explicitement sa position, il positionnera par défaut le cercle au centre de l'élément.

On peut spécifier le rayon du cercle avec n'importe quelle unité de longueur (px, em, pt, ...). On peut également spécifier le rayon en utilisant closest-side (côté le plus proche) ou farthest-side (côté le plus éloigné), mais closest-side est la valeur par défaut, ce qui signifie que le navigateur définira le rayon comme étant la longueur entre le centre de l'élément et son côté le plus proche.

shape-outside: circle(farthest-side at 25% 25%); /* définit un cercle dont le rayon est égal à la moitié du côté le plus long, et qui est positionné aux points de coordonnées 25% 25% dans le système de coordonnées de l'élément */

shape-inside: circle(250px at 500px 300px); /* définit un cercle dont le centre est positionné à 500px horizontalement et 300px verticalement, avec un rayon de 250px */
illustration de closest side et farthest side, pour un même élément

La fonction ellipse() est similaire à la fonction circle() , elle utilise les mêmes listes de valeurs, mais au lieu d'avoir un seul paramètre de rayon, elle en a deux : un pour la longueur du rayon sur l'axe des x, l'autre pour l'axe des y.

ellipse() = ellipse([<shape-radius>{2}]? [at <position>]?)

La fonction inset() est utilisée pour créer des formes rectangulaires. Dans la mesure où les éléments sont déjà rectangulaires, nous pouvons l'utiliser d'une manière plus fine, par exemple en créant des rectangles à bords arrondis, avec un flux de contenu qui s'adapte.

des rectangles avec bords arrondis, le texte suit la forme avec CSS shapes

La fonction inset() prend de 1 à 4 valeurs offset qui définissent les retraits (offset) depuis les bords des boîtes de référence vers l'intérieur. Ils spécifient l'endroit où le rectangle sera inséré à l'intérieur de l'élément. Les coins arrondis sont définis exactement de la même façon que les border-radius en utilisant de 1 à 4 valeurs, en conjonction avec le mot-clé round .

inset() = inset(offset{1,4} [round <border-radius>]?)

Voici comment on créera un rectangle arrondi sur un élément flotté :

.element {
    float: left;
    width: 250px;
    height: 150px;
    shape-outside: inset(0px round 100px) border-box;
}

Vous pouvez aussi jouer avec l'exemple live sur CodePen pour mieux comprendre le fonctionnement d'inset() .

La dernière fonction Shapes est le polygon() , qui permet de définir des formes arbitraires plus complexes en utilisant une quantité de points. La fonction prend un ensemble de paires de coordonnées, chaque paire spécifiant la position d'un point, et l'ensemble des points définissant la forme.

Dans l'exemple qui suit, une image flottée à droite prend toute la hauteur de l'écran en utilisant les unités viewport. Nous voulons que le texte de gauche suive les contours du sablier situé à l'intérieur de l'image, et nous utilisons la fonction polygon() pour définir une forme irrégulière.

sablier à droite, texte à gauche suivant la forme de l'image, mais



Pour l'image ci-dessus, notre CSS sera :

img.right {
    float: right;
    height: 100vh;
    width: calc(100vh + 100vh/4);
    shape-outside: polygon(40% 0, 100% 0, 100% 100%, 40% 100%, 45% 60%, 45% 40%);
}

Pour les coordonnées des points définissant l'image, vous pouvez utiliser des unités de longueur ou des pourcentages, comme je l'ai fait ici.

Ces quelques lignes suffisent à produire l'image ci-dessus, cependant vous avez pu constater que la fonction n'a pas eu d'effet sur le reste de l'image à l'extérieur de la forme définie. En fait, appliquer une fonction Shapes à un élément - que ce soit une image, un container, ou quelque chose entre les deux - n'affectera rien d'autre que l'aire du flux de contenu. Les backgrounds, les bordures et tout le reste demeurent inchangés.

Si nous voulons visualiser la forme du polygone que nous avons créé, nous devons découper les parties de l'image qui se trouvent à l'extérieur de la forme. Pour cela, nous pouvons utiliser la propriété clip-path du module CSS Masking.

La propriété clip-path prend les mêmes fonctions et valeurs que la propriété Shapes. Si nous passons la définition du polygone utilisée dans la propriété shape-outside à la propriété clip-path , nous découpons la partie de l'image correspondante.

img.right {
    float: right;
    height: 100vh;
    width: calc(100vh + 100vh/4);
    shape-outside: polygon(40% 0, 100% 0, 100% 100%, 40% 100%, 45% 60%, 45% 40%);
    /* clip the image to the defined shape */
    clip-path: polygon(40% 0, 100% 0, 100% 100%, 40% 100%, 45% 60%, 45% 40%);
}

Le résultat est le suivant :

même sablier que précédemment, mais image découpée, le texte repose sur background neutre, pas sur l'image



La propriété clip-path nécessite encore d'utiliser des préfixes, elle marchera dans Chrome avec le préfixe -webkit- . Vous pouvez voir une démo live ici. (NdT: pour vous faciliter la vie, Autoprefixer peut faire ce travail de préfixage pour vous, vous trouverez toutes les infos dans l'article traduit ici-même).

La propriété clip-path accompagne à merveille les propriétés Shapes, car elle permet de visualiser les formes en découpant toute partie de l'élément qui se trouve en dehors de la forme définie. Vous l'utiliserez sans doute souvent avec les propriétés Shapes.

La propriété polygon() prend par ailleurs un mot-clé optionnel, qui peut être soit nonzero soit evenodd . Ce mot indique la façon de traiter les surfaces à l'intérieur de la forme polygonale qui pourraient être en intersection. Pour les détails, vous pouvez consulter la propriété SVG fill-rule.

Définir une forme en utilisant une image

Pour définir une forme en utilisant une image, cette dernière doit avoir un canal alpha à partir duquel le navigateur peut extraire la forme.

Une forme est définie par les pixels dont la valeur alpha est supérieure à un certain seuil. Celui-ci est par défaut égal à 0.0 (entièrement transparent), mais vous pouvez le définir explicitement en utilisant la propriété shape-image-threshold . Dans le premier cas, tout pixel qui n'est pas transparent sera utilisé comme partie de la forme définie à partir de l'image.

Dans un niveau futur de CSS Shapes, il est possible que l'on utilise la luminance d'une image plutôt que les données alpha, auquel cas shape-image-threshold sera étendu pour appliquer un seuil alpha ou luminance.

Nous allons utiliser l'image suivante pour définir une forme sur un élément et envelopper celui-ci de texte.

image découpée, forme de feuille

Avec shape-outside et url() pointant vers notre image, nous pouvons définir le flux du contenu autour de notre élément en forme de feuille.

.leaf-shaped-element {
    float: left;
    width: 400px;
    height: 400px;
    shape-outside: url(leaf.png);
    shape-margin: 15px;
    shape-image-threshold: 0.5;
    background: #009966 url(path/to/background-image.jpg);
    mask-image: url(leaf.png);
}

Bien sûr, si vous deviez appliquer un background à l'élément, ce background devrait être découpé en dehors de la forme définie, ce que nous permet la propriété mask-image (accompagnée des préfixes appropriés) du Module Masking, puisque la propriété clip-path ne prend pas d'image alpha comme valeur. Voici le résultat :

contenu à l'intérieur de la forme de feuille

Si vous créez des formes complexes, il pourra être préférable de définir une forme en utilisant une image (par exemple en utilisant le canal alpha d'une image sur Photoshop) plutôt que de définir des points.

Il est également préférable d'utiliser une image plutôt qu'une fonction lorsque vous avez plusieurs float ou plusieurs surfaces réservées à l'intérieur d'un élément - parce qu'on ne peut pas, pour l'instant du moins, déclarer plusieurs formes sur un élément. Par contre, si votre image est constituée de plusieurs zones, le navigateur pourra les extraire et les utiliser.

CSS Shapes et design responsif

CSS Shapes est-il compatible avec mon workflow responsif ? La spécification en cours (shape-outside) a déjà réglé la question, elle vous permet de spécifier les dimensions de l'élément soit en pourcentages, soit en unités de longueur, et les points composant la forme (paramètres de la fonction forme) peuvent également être définis en pourcentages.

shape-inside n'est pas aussi avancé, mais c'est parce qu'il a été repoussé au Module Niveau 2. Ses limitations actuelles seront résolues au prochain niveau.

Les outils pour Shape

La création de formes complexes à travers les fonctions Shapes peut faire peur, en particulier s'agissant de polygon() . Heureusement, l'équipe d'Adobe a créé des outils interactifs qui nous facilitent la vie. Bear Travis a créé une collection d'outils Shapes qui nous permettent de créer des formes polygonales de manière visuelle. L'outil génère ensuite la fonction pour nous. C'est très utile avec une limite : vous ne pourrez pas insérer une image pour créer votre forme.
Un outil plus avancé et plus interactif a été développé par Adobe. Il a été publié récemment en tant qu'extension de l'éditeur de texte d'Adobe, Brackets. Il vous permet de visualiser et éditer des formes directement dans votre navigateur, et il comporte une fonction live preview qui met à jour la page lorsque vous modifiez les valeurs Shapes de votre CSS. Vous pouvez ainsi voir instantanément la façon dont vos formes interagissent avec les autres éléments de la page.

Création et modification d'une forme polygonale avec le mode live preview de Bracket. Capture d'écran par Razvan Caliman.

Razvan Caliman a écrit un article sur le blog de Bracket qui explique comment vous pouvez intégrer l'éditeur Shapes dans Bracket et l'utiliser dès aujourd'hui.

L'avenir : Exclusions CSS

La spécification CSS Shapes couvrait autrefois Shapes et Exclusions, mais les deux ont été séparées. Shapes définit les propriétés shape-inside et shape-outside , Exclusion de son côté définira les propriétés nous permettant d'entourer de contenu les éléments non flottés, tels que les éléments positionnés absolument. On pourra de la sorte entourer de contenu la forme tout entière, de tous côtés, comme on le voit dans l'image qui suit.

À l'avenir, CSS Exclusions nous permettra d'envelopper de texte une forme, telle que cette citation au milieu. Si la forme était circulaire, le texte en suivrait le contour. Image par Justin Thomas Kay.

Des mises en page similaires, avec des éléments positionnés absolument au centre d'un article et un flux de contenu environnant, seront possibles.

CSS Shapes, un peu plus loin

La spécification en cours est un premier pas. Bientôt, de nouvelles options nous donneront plus de contrôle sur la création de formes et le flux de contenu, et la création de designs sera une question de quelques lignes de code. Pour l'instant, les éditeurs du spec sont concentrés sur la publication de shape-outside , et il est vraisemblable que CSS Shapes sera utilisable avec les meilleurs navigateurs avant la fin de cette année.

Vous pouvez d'ores et déjà utiliser CSS Shapes dans le cadre d'un workflow conçu en amélioration progressive, puisque les fallbacks sont faciles à mettre en oeuvre. Je les utilise depuis peu dans mon propre site web, et le fallback est très “normal”. Pour des designs plus complexes, vous pouvez utiliser un script pour vérifier la compatibilité navigateur et fournir une solution de rechange si la réponse est négative. Vous pouvez étendre les tests de Modernizr avec ce script pour tester la compatibilité avec shape-outside.

CSS Shapes construit un nouveau pont entre les design web et imprimé. Les exemples fournis dans cet article sont simples, mais devraient vous donner une bonne base pour créer des designs aussi complexes que ceux d'un magazine ou d'un poster, sans avoir à vous soucier de savoir si votre maquette pourra être recréée à l'écran. Quel que soit votre domaine d'exploration, depuis les mises en pages non rectangulaires jusqu'à l'animation de formes, l'heure des expérimentations est venue.


Ressources complémentaires :


Intéressé par CSS ? Sur la Cascade, retrouvez des articles et ressources.


original paru le dans A List Apart.

Sur l'auteur : est une intégratrice web libanaise. Elle aime enseigner et écrit des tutoriels sur son blog et sur Codrops, dont elle est un des membres actifs. Vous pouvez la suivre sur Twitter et sur Github.

Traduit avec la permission de A List Apart et de l'auteur.