Chez Jérémie

Chez Jérémie, parfois c'est sérieux, parfois non !

:before et :after, des pseudo-éléments qui ont du style

- Web - Lien permanent

En ces temps de buzz autour de CSS3, on en oublie les supers outils qui existent dans CSS2 et qui sont largement exploitables. Parmi ceux-ci, on trouve les pseudo-éléments :before et :after. Ces pseudo-éléments ont longtemps été négligés par les designers et intégrateurs Web pour deux raisons essentiellement : Premièrement, IE6 et IE7 ne les comprennent pas, donc à l’époque ou ces deux navigateur était dominant c’était inutilisable ; Deuxièmement, les ayatollahs de l’accessibilité stigmatisent assez volontiers ces deux pseudo-éléments (parfois à raison) ce qui a tendance à effrayer les designers et intégrateurs qui finalement s’y intéressent peu.

Pourtant, ces deux paravent masquent la puissance de ces outils. Le premier point est facilement contournable avec un peu de Javascript et le deuxième point est totalement injustifié dès lors qu’on utilise ces pseudo-éléments pour ce qu’ils sont : des aides à la mise en forme. Je vais donc vous montrer comment fonctionnent ces pseudo-éléments, vous allez voir, c’est génial ;)

Des pseudo quoi ?

CSS2 définie donc deux pseudo-éléments sobrement accessible via les sélecteurs :before et :after. Hop, hop, hop… pseudo quoi ? Comme son nom l’indique, un pseudo-élément est un truc qui ressemble à un élément, qui a le gout d’un élément, qui se comporte comme un élément… mais qui n’en est pas un. Et oui, les pseudo-élément CSS sont à HTML ce que la bière sans alcool est… euh…  à la bière.

Diantre, un élément qui n’en ait pas un ! Aurais-je donc trop bu de l’alcool sus-nommé ? Que nenni. La principal différence technique entre un pseudo-éléments et un élément “normal” se résume assez simplement : Les pseudo-éléments n’apparaissent jamais dans l’arbre DOM des documents mais influencent sur le rendu visuel de celui-ci comme si c’était le cas.

Cet état de fait à deux conséquences :

  1. Toutes les aides à l’accessibilité qui reposent sur l’exploitation du DOM ne “verront” jamais les pseudo-éléments.
  2. Manipuler les pseudo-éléments via Javascript ne peut pas se faire via l’API DOM, mais uniquement via des modifications de style (et encore, difficilement).

Emplacement et comportement

Maintenant que vous avez compris ce que sont ces pseudo-éléments, comment ceux-ci s’intègrent dans le rendu visuel des éléments traditionnels ?

Un pseudo-éléments est toujours défini relativement à un élément réel (c’est à dire qui existe dans l’arbre DOM). Cela signifie, qu’il est visuellement rendu comme un enfant de cet élément réel (et pas comme un élément frère placé avant ou après l’élément de référence contrairement à ce qu’on pourrait croire). :before désignera donc le pseudo-élément qui sera visuellement placé avant le premier enfant de l’élément de référence et :after désignera le pseudo-élément qui sera visuellement placé après le dernier enfant de l’élément de référence. Notez que si vous utilisez ces deux sélecteurs tout seul, cela reviens à faire une utilisation implicite du sélecteur universel *. Ainsi, la règle :before{...} seule est équivalente à la règle *:before{...}.

En outre, un pseudo-élément va se comporter visuellement de la même façon qu’un élément HTML span. Arrêtons-nous ici un petit moment car c’est sans doute le point le plus important à comprendre pour arriver à devenir créatif avec les pseudo-élément. Je le redis : un pseudo-élément va se comporter visuellement de la même façon qu’un élément HTML span.

Un bon schéma valant mieux qu’une longue explication voila ce que ça implique.

before-after.png

Dans le premier cas, l’élément référent ne contient que du texte, les pseudo-éléments vont donc s’inclure dans le flux du texte. Dans le deuxième cas, l’élément référent contient un enfant de type bloc, les pseudo-éléments vont donc se placer avant et après ce bloc.

Génération de contenu

Voila, les pseudo-éléments ont trouvé leur place et on peut leur appliquer tous les styles disponibles pour les mettre en forme, c’est génial… sauf que… Sauf que ce ne sont que des boites vides. Certes, les perspectives ouvertes sont déjà assez immenses quand on a un peu d’imagination (et Dieu sait qu’il faut en avoir pour faire de l’intégration Web !). Mais là ou ça devient vraiment fun, c’est quand on commence à remplir ces pseudo-éléments avec du contenu.

Normalement, c’est quand on en arrive à ce point qu’on assiste à une levée de boucliers des intégristes de l’accessibilité… les sots ! En général, leur argument principal est que les contenus générés n’étant pas présent dans le DOM, tout les outils d’accessibilité ne peuvent pas exploiter ces contenus (sans parler des navigateurs qui ne comprennent tout simplement pas les pseudo-éléments). C’est vrai, et il est important de s’en souvenir. Ceci étant dit, il est bon de rappeler que tout les contenus n’ont pas à êtres accessibles, surtout s’ils servent un objectif décoratif. Donc, OUI, c’est possible de générer du contenu en CSS si celui-ci n’a qu’une vocation décorative. Pire ! J’irai plus loin, je vous le recommande. En effet, générer des contenus décoratifs en CSS réduit le bruit autour des contenus devant être accessible. Et donc, bien utilisés, :before et :after peuvent vous aider à améliorer l’accessibilité de vos sites.

La génération de contenu dans un pseudo-éléments passe par l’utilisation de la propriété CSS content. Cette propriété peut accepter plusieurs valeurs différentes selon ce que vous voudrez générer. Notez que même si vous ne voulez qu’une boite vide, vous devrez utiliser la propriété content avec une chaine vide. En effet, la valeur par défaut de cette propriété (normal) fait que les pseudo-éléments ne sont pas affichés.

Du texte

Le plus simple à faire c’est de générer du texte. Cela ce fait très simplement de la manière suivante :

div.alert:before{
   content : "WARNING: ";
/* Ici, vous préfixez tous vos messages d'alerte avec un texte qui n'a pas besoin d'être accessible. C'est un choix de design acceptable s'il existe dans la page HTML un autre moyen d'identifier ces messages comme étant des alertes. Si ce n'est pas le cas, vous commettez une grave erreur d'accessibilité.. En effet, si vous générez beaucoup d'alertes, il peut être fastidieux d'entendre une revue d'écran vous dire "WARNING-DEUX-POINTS" en permanence. Ici, ce ne sera pas le cas ;-) */
}

MAJ du 24/11 : Suite au échanges dans les commentaires avec Stéphane “Roudoudou d’amour” Deschamps, il est nécessaire de comprendre que toute insertion de texte via CSS doit être soupesé avec attention car les conséquences en terme d’accessibilité peuvent être importante.

Notez que vous pouvez mettre n’importe quel caractère Unicode dans une chaine générée. La syntaxe pour insérer un code Unicode est \00XXXX ou la chaine XXXX représente le code Unicode hexadécimal de votre caractère. Petite exception à cela1, les retours à la ligne disposent d’un raccourcis : \A. Par exemple, si vous voulez rajouter une espace insécable avant un deux-points, ça donnera :

label:after{
   content : "\00202F:";
/* Là aussi, vous n'avez peut-être pas forcément envie que votre revue d'écran vous dise "DEUX-POINT" tout le temps hein ? */
}

Des images

Il est également possible d’inclure des images grâce à la fonction url(). Ce qui donnera :

div.alert:before{
   content : url(exclamation.png);
}

Des compteurs

Très utile si on veut numéroter des éléments (que ceux-ci soient ou non des listes ordonnées n’a aucune importance) comme des titres par exemples. Pour cela, il faut utiliser les fonctions counter() ou counters() en conjonction avec les propriétés counter-reset et counter-increment. Je ne vais pas vous détailler l’usage des compteurs qui mériterai un article à lui seul, mais cela fait également partis de ces outils sous-estimés de CSS2. Voici un petit exemple simple :

body{
   counter-reset: alert; /* On défini le compteur avec un nom arbitraire */
}

div.alert:before{
   counter-increment : alert;
   content : counter(alert, decimal);
/* avant la virgule, c'est le nom du compteur, après la virgule, c'est son style. La valeur du style est la même que pour la propriété list-style-type */
}

Des valeurs d’attribut

Enfin, il est possible de récupérer et donc d’afficher les valeurs des attributs de l’élément référent qui contiendra les pseudo-éléments. Je ne vous met pas d’exemple, mais je vous invite à relire l’article que j’ai écris sur ce sujet : Améliorez vos liens avec la génération de contenu CSS.

Grand Schelem, Belote et Quinte Flush Royal

Et le plus beau dans tout ça, c’est que vous pouvez mixer tout ça ensemble :

body{
   counter-reset: alert;
}

div.alert:before{
   counter-increment : alert;
   content : url(exclamation.png) "\00202FWARNING\00202F" counter(alert) "\00202F: ";
/* Notez que je n'ai pas remis le style du compteur. Son style par défaut est "decimal" */
}

Ceci dit, soyez raisonnable, ce n’est pas parce qu’on peut le faire qu’on doit le faire ;) Pensez aussi et surtout à vos utilisateurs.

Cas d’usage

La théorie c’est bien, la pratique c’est mieux. Voici quelques cas d’usage, mais je suis sur que vous saurez en trouver d’autres ;)

puces et numérotation

Les conteurs vous permettent d’une part de reprendre le contrôle des puces et numéros sur les listes, mais vous permettent également de numéroter d’autre choses, des titres par exemple :

article{
   counter-reset : c1 c2 c3;
}

article h1{
   counter-increment : c1;
}

article h2{
   counter-increment : c2;
}

article h3{
   counter-increment : c3;
}

article h1:before{
   content: counter(c1) ". ";
}

article h2:before{
   content: counter(c1) "." counter(c2) ". ";
}

article h3:before{
   content: counter(c1) "." counter(c2) "." counter(c3, lower-alpha) ". ";
}

> Voir l’exemple des conteurs sur les titres

Bords arrondis

Voici une utilisation avancé des pseudo-éléments. Habituellement, lorsque vous voulez réaliser des bords arrondis avec CSS2, vous êtes obligé de rajouter du balisage HTML (ou SVG, ou VML) pour pouvoir avoir assez d’éléments à styler pour chacun des coins (peut importe que cet ajout de balises soit fait à priori dans la source HTML ou à posteriori via Javascript, le principe reste le même). Plutôt que de polluer le DOM du document (ce qui peut avoir des conséquences fâcheuses avec certains sélecteurs CSS2 et 3), vous pouvez avantageusement exploiter les pseudo-éléments pour afficher vos coins.

En effet, en positionnant votre élément de référence de manière relative et en positionnant vos pseudo-éléments de manière absolute (et en exploitant correctement la propriété z-index), vous vous retrouvez avec un élément qui potentiellement dispose de 3 couches d’arrière-plan et de deux emplacement pour des images supplémentaires. En claire, vous pouvez empiler jusqu’à 5 images pour créer des effets graphiques de malade.

Schématiquement, cela donnera un empilement des éléments de ce genre :

Ordre d'empilement des couches

Le code CSS associé à ce schémas est le suivant :

div{
   position : relative;
   z-index : 0;
}

div:before,
div:after{
   content : "";
   position : absolute;
   z-index : -1; /* Oui, z-index accepte les valeurs négatives */
   top : 0;
   left : 0;

   width : 100%;
   height : 100%;
}

div:after{
   z-index : -2;
}

Cette technique souffre tout de même de quelques limites (sinon, ce ne serait pas drôle) :

  1. Les contenus des pseudo-éléments étant considérés comme du texte en ligne, ils peuvent être délicat à positionner ailleurs que en haut et à gauche du pseudo-éléments (c’est possible, mais ça donne lieu à des contorsions techniques un peu disgracieuses à mon gout)
  2. Si vous voulez avoir des coins arrondis sur une boite extensible en largeur ET en hauteur, les coins ne pourront pas être tous transparent (par contre, pour une extension sur un seul axe, c’est possible).
  3. La gestion des margin et padding de l’élément de référence demande un peu d’attention pour être sur que les pseudo-éléments se comportent correctement (une petite révision du modèle de boite et du positionnement CSS n’est pas inutiles ;) ).

> Voir l’exemple des bords arrondis

Décoration de contenu

De manière général, les pseudo-éléments vous permettent de sortir du cadre (littéral) imposé par le modèle de boite CSS pour décorer les éléments.Voici quelques exemples simples.

La création de guillemets graphiques autour des éléments blockquote

Un exemple simple pour habiller les éléments blockquote consiste à créer des guillemets avec :before et :after et à les “faire sortir” pour leur donner une valeur graphique.

Notez que pour faire de “vrai” guillemet via CSS, il existe aussi la propriété quote qui est mieux adaptée (surtout pour gérer la localisation des règles typographiques)

> Voir l’exemple des blockquote

Réaliser des icônes sous forme de fontes

Avec la démocratisation de @font-face, on voit arriver certains usages un peu surprenant. C’est le cas des icônes sous forme de fontes. Plutôt que d’utiliser des images, on peut utiliser des polices typographiques qui contiennent des images vectorielles plutôt que des lettres. Ça permet d’avoir des icônes qui vont bien supporter l’agrandissement et la réduction. Au delà du fait que je trouve ça techniquement idiot (SVG est bien plus adapté à ce cas d’usage), cela pose de sérieux problèmes d’accessibilité. En effet, un caractère, même s’il ne ressemble pas à une lettre sera compris comme tel par les navigateurs (et donc lu comme tel par les revus d’écran !).

C’est là que :before et :after peuvent aider à l’accessibilité ! En positionnant vos icônes dans des pseudo-éléments, les aides à l’accessibilité n’y auront plus accès et ces icônes pourront donc être plus efficacement utilisées pour ce qu’elles sont : des artifices de décoration.

> Voir l’exemple des icônes typographique

La tentation du remplacement de texte

Ce cas d’usage est hyper limite, mais il faut que vous le connaissiez pour pouvoir lutter contre les abus et les dérives qui peuvent en découler. 

Imaginez le cas suivant :

<div>
   <span>Ici, mon texte d'origine</span>
</div>

Avec le code CSS suivant :

span{
   position : absolute;
   width : 0;
   height : 0;
   overflow : hidden;
}

div:before{
   content : "mon nouveau texte";
}

Le texte d’origine n’est plus visible mais continue de vivre dans le DOM par contre, le texte du pseudo-éléments est visible mais littéralement inaccessible (il n’existe pas dans le DOM et il n’est pas sélectionnable).

Réaliser un tel remplacement de texte est dangereux car c’est la porte ouverte au SPAM ou pire au hijacking. Mais il peut y avoir quelques cas légitimes (bien que très certainement discutables), comme par exemple la création de liens “lire la suite” qui disposent de libellés plus complets et accessibles dans le DOM. Dans tout les cas, faites bien attention à ce que vous faites et pensez toujours à vos utilisateurs (tous vos utilisateurs).

Régler les problèmes de flottaison

Un des problèmes récurant lorsqu’on utilise la propriété float, c’est que l’élément qui flotte, s’il est plus grand que son conteneur, peut en “sortir”. Pour éviter cet effet parfois disgracieux, nous sommes nombreux à avoir appris à agrémenter notre code HTML de <span style="clear:both"></span> et autre joyeuseté du même genre. Pour éviter de polluer nos sources HTML et nos arbres DOM, il y a peu de solution. En fait, il y en deux avec CSS.

  1. La première consiste à utiliser la propriété overflow avec la valeur hidden. C’est une technique très efficace, mais qui a un gros défaut : absolument rien ne peut sortir de l’élément, ce sera forcément tronqué. Cela peut donc être une contrainte forte, en particulier si vous utilisez des positionnements relatifs ou des marges négatives pour décaler des éléments vers l’extérieur.
  2. La deuxième consiste à utiliser un pseudo-élément :after et tout simplement de lui appliquer la propriété clear. Et là, c’est le bonheur, vous avez tous les avantages de la balise span sans avoir empoisonné votre source HTML et vous évitez tous les problèmes liés à l’usage de la propriété overflow.

> Voir les différentes gestion de float

Le cas Internet Explorer

Tous les exemples précédent sont relativement simple et fonctionnent dans tous les navigateur de dernière génération mais malgré cela il y a toujours un petit problème : Internet Explorer et plus particulièrement ses versions 6 et 7 (mais achevez les, vous voyez bien qu’ils souffrent !)

Mais d’abord, un petit mot sur Internet Explorer 8 qui est un excellent navigateur en terme de support de CSS2 et qui est donc capable de faire tout ce qui a été dis ci-avant. Enfin… presque. Il existe un petit bug dans l’implémentation des pseudo-éléments qui rend impossible la possibilité d’afficher un pseudo-élément au dessus du contenu de l’élément de référence. Cela peut parfois être un peu gênant lorsque vous positionnez les pseudo-éléments de manière absolue, mais rien d’insurmontable graphiquement parlant, il faut juste le savoir.

Par contre, IE6 et 7 ne supportent pas du tout les pseudo-éléments. Alors que faire ?

Hélas, il n’y a pas 36 solutions, et, à moins que vous ne soyez très à l’aise avec les questions de dégradation harmonieuse, vous allez devoir vous tourner vers Javascript. En effet, la meilleur façon de simuler les pseudo-éléments avec ces navigateurs consiste à inclure de vrai élément dans l’arborescence du DOM et à leur appliquer les même style qu’a vos pseudo-éléments.

Si vous utilisez jQuery, ça peut donner ça :

$('div.alert')
.prepend('<span class="before"></span>')
.append('<span class="after"></span>');

Cette solution marche plutôt bien mais souffre de quelques inconvénient qu’il faut connaitre :

  1. IE6 et 7 ne connaissent pas non-plus la propriété content, il vous faudra donc aussi générer votre contenu via Javascript.
  2. Les éléments étant véritablement dans le DOM, ils seront visibles par les différentes aides à l’accessibilité ce qui peut avoir des conséquences déplaisantes pour l’utilisateur.
  3. Les déclarations de style pour les classes .before et .after devront être dupliquées. En effet, IE6 et 7 ignoreront tous les blocs de styles avec au moins une déclaration de sélecteur contenant :before et :after, même si les différents sélecteurs sont séparés par une virgule.

Conclusion

Voila pour ce tour d’horizon des pseudo-éléments. Comme vous avez pu vous en rendre compte, ce sont des outils très puissant qui offrent un potentiel créatif énorme (mais qui requièrent aussi une certaine dose de responsabilité pour ne pas faire n’importe quoi).

Bien comprendre les possibilités de CSS2 à son plein potentiel est une étape importante pour aller vers CSS3. En effet, le module dédié à la génération de contenu en CSS3 fait littéralement exploser les limites du possible en la matière avec des fonctionnalités démentes comme le fait de pouvoir créer des pseudo-éléments dans des pseudo-élément et de pouvoir même carrément recréer un arbre DOM fantôme. Bon, pas de panique, d’ici à ce que les fabricants de navigateurs se soient mis d’accord sur la norme et commence à l’implémenter, il va couler de l’eau sous les ponts (d’autant que c’est un module non-prioritaire au W3C). Mais si déjà vous maitrisez ce qu’offre CSS2, vous vous ouvrer de grandes portes… amusez-vous bien :)


Notes :

  1. En fait ce n’est pas une exception, mais la gestion des échappements de caractères avec CSS n’est pas forcément très simple, donc, je vous encourage à vous familiariser avec la notation \00XXXX et à considérer \A comme une exception.