Améliorez vos liens avec la génération de contenu CSS
- Web - Lien permanent
Les liens hypertextes sont la colonne vertébrale du Web. La plus part du temps, ils se résument à un texte graphiquement mis en évidence (historiquement, bleu et souligné). Néanmoins, techniquement et sémantiquement parlant, les liens peuvent bénéficier de méta-données qui offrent un certain nombre d'informations qui pourraient être utile à l'utilisateur mais ne sont pas mises en évidence par les navigateurs. Par exemple, l'attribut target
permet de définir dans quel fenêtre ou cadre va s'ouvrir le lien, ou l'attribut hreflang
qui permet de connaitre la langue de la page où mène le lien et je ne vous parle même pas de l'attribut rel
. Dans cet article je vais vous montrer comment utiliser la génération de contenu CSS pour afficher certaines de ces informations à l'utilisateur.
Le résultat attendu
L'idée est d'afficher au survol d'un lien le contenu de son attribut hreflang
et target
dans un badge en surimpression. Une image valant mieux qu'un long discourt, voila le résultat attendu :
L'arrière plan des liens
Afin de facilité la lecture du badge et surtout son association visuelle avec le lien, on va attribuer aux liens un arrière plan coloré qui va légèrement déborder de la zone de lecture "normal".
On commence par leurs donner une couleur d'arrière plan au survol :
a:hover{
background : #CCD;
}
L'effet de débord est réalisé en jouant sur les propriétés padding
et margin
. L'utilisation de padding
va permettre d'élargir la zone visuelle. Malheureusement, cela agrandi aussi réellement la zone occupée par le lien qui va repousser ce qui l'entoure. Pour éviter cet effet, on va attribuer une valeur négative de margin
équivalente à chacune des valeurs de padding
. Cet artifice permet de donner assez facilement l'illusion d'un élément qui "déborde" tout en étant à sa place naturel dans le flux HTML.
a{
display : inline-block;
padding : 0.1em 0.6em;
margin : -0.1em -0.6em;
}
Notez que la balise a
est une balise de type inline
. Cela veux dire que certains navigateurs ne vont pas appliquer (ou seulement partiellement) le padding
et le margin
à l'élément. Pour corriger cela, vous devez ajouter la propriété display: inline-block
. Si vos intitulés de liens sont longs, il faut se poser la question de savoir si on veut réellement réaliser un tel effet graphique. En effet, en cas de retour à la ligne avec une balise inline-block
, c'est l'intégralité du contenu de la balise qui passe à la ligne (ce qui peut casser la continuité et la lisibilité de votre texte) et si le contenu continue d'être trop grand pour tenir sur une seule ligne, le retour à la ligne se fera à l'intérieur de la balise inline-block
. C'est donc à vous de voir ce que vous voulez faire selon le contexte sachant que l'utilisation de la valeur inline-block
est obligatoire avec Internet Explorer 8 lorsque vous voulez créer un contenu généré, positionné de manière absolue (ce qu'on va faire ci-après). Si vous n'utilisez pas cette valeur, le badge s'affichera sous le lien !
Avec le truc du débord que je viens de vous montrer, vous allez constater un effet assez déplaisant. Au survol de votre lien le texte qui précède le lien sera masqué par l'arrière plan alors que le texte qui suit restera visible par dessus l'arrière plan. Vous pouvez régler l'empilement des éléments en jouant avec la propriété z-index
. Vous pouvez soit lui donner la valeur -1, mais dans ce cas, les liens vont se retrouver sous les paragraphes et ils ne seront jamais survolés par la souris (vous les voyez, mais en fait ils sont "masqués" par l'élément contenant le texte), soit vous lui donnez la valeur 1 et dans ce cas le lien passera entièrement au dessus du texte qui l'entoure. C'est assez intéressant graphiquement, mais pas forcément très satisfaisant en terme de lisibilité je vous montrerai un peu plus loin comment s'en sortir avec CSS 3.
a{
position : relative; /* nécessaire pour la bonne application de z-index */
z-index : 1;
}
La génération de contenu
Le badge qui contiendra les informations supplémentaires va être créer à la volé au survol d'un lien directement en utilisant CSS. Pour cela, nous allons utiliser le pseudo-élément :after
en conjonction avec la propriété content.
Le pseudo élément :after
va simuler la présence d'un éléments après le contenu du lien. On appel cela un pseudo élément car ce sélecteur va créer une zone qui va avoir le même comportement qu'un élément HTML traditionnel, exactement comme si vous agissiez sur une balise span
. La seul différence, c'est que ce pseudo-élément n'apparait pas dans l'arbre DOM du document. Je vous reparlerai un peu plus loin de ce sujet quand on abordera les questions d'accessibilité. Ce qu'il faut retenir ici c'est que vous allez pouvoir ajouter des styles à un élément sans que vous ayez besoin de rajouter quoi que ce soit dans le balisage HTML de vos liens.
La propriété content
, elle, va permettre de rajouter du texte et des images au sein de votre pseudo-éléments. Là encore, rien n'apparait dans l'arbre DOM de votre document HTML.
Nous allons gérer ici quatre cas de figure pour notre badge :
- Si l'attribut
hreflang
est présent, on affichera sont contenu dans notre badge : l'objectif est de dire visuellement à l'internaute quelle est la langue du lien qu'il va ouvrir. - Si l'attribut
target
est présent et à la valeur_blank
, on affichera une icône qui montre l'ouverture d'une nouvelle fenêtre dans notre badge. - Si les deux attributs sont présents, on affichera et la langue et l'icône.
- Si aucun des deux attributs n'est présents, on n'affichera pas le badge du tout.
a:after{
/* On masque le badge, mais on utilisera le sélecteur générique pour définir le style global du badge */
display : none;
}
a[hreflang]:hover:after{
/* On affiche le contenu de l'attribut hreflang */
content : attr(hreflang);
display : block;
}
a[target="_blank"]:hover:after{
/* Si l'attribut hreflang est définie, on affichera son contenu. S'il n'existe pas, il ne s'affichera pas. Dans tous les cas, on affichera une icône qui matérialise le changement de fenêtre */
content : attr(hreflang) " " url("external.png");
display : block;
}
Assez simplement, la fonction attr
permet d'extraire le contenu de l'attribut et la fonction url
permet d'inclure une image. Notez que dans la déclaration ci-avant, dans le cas ou les deux attributs hreflang
et target
sont tous les deux définis, c'est la dernière règle CSS qui sera appliquée car elle a, selon les règles de la cascade, le même poids que la règle précédente, mais elle la surcharge du fait de son positionnement dans le code source.
Le positionnement et le design du badge
Maintenant que le contenu du badge est générer il faut lui donner un peu de style (oui, je sais, elle un peu facile celle là :-p )
On va commencer par le sortir du flux en le positionnant de manière absolu et on le fera légèrement déborder en haut et à droite. En suite, on va détailler le minimum syndical graphique : Couleur d'arrière plan, bordures, styles typographiques, marges pour aérer un peu tout ça.
a{
/* nécessaire pour que le positionnement du badge soit relatif à son lien*/
position : relative;
}
a:after{
position : absolute;
top : -1.3em;
right : -1.3em;
/* L'arrière plan est rouge foncé et le texte blanc */
background : #009;
color : #FFF;
/* On ajoute une bordure blanche de 2 pixels de larges */
border : 2px solid #FFF;
/* Le texte est graissé et 1/3 plus petit que le texte du lien. Il est également mis systématiquement en majuscule */
font : bold 0.6em Verdana, Helvetica, Arial, sans-serif;
text-transform : uppercase;
/* L'ajout de marge permet de laisser le texte respirer */
padding : 0.4em 0.5em;
}
CSS 3 is sexy !
Voila ! Avec ce que vous venez de voir, vous avez fait tous ce qu'il est possible de faire simplement avec CSS 2. Cependant, on n'est pas encore au résultat attendu. On va donc rajouter une petite touche de CSS 3 pour rendre ça plus fin, plus youpi-tralala, plus waow !
Couleur transparentes
Actuellement, l'arrière plan de nos liens est opaque au survol, ce qui fait qu'on masque une partie du texte à cause de l'effet de "débord" de l'arrière plan. En utilisant une couleur semi-transparente on peut atténuer ce problème. pour cela, il suffit d'utiliser la notation rgba
définie dans le module "Border & Background" de CSS 3.
a:hover{
background : #CCD;
background : rgba(128, 128, 160, .4);
}
Notez dans l'exemple ci-avant que la propriété background
est déclarée deux fois. C'est normal. En effet, de cette manière si un navigateur ne comprend pas la notation rgba
(IE8 par exemple), il ignorera la déclaration inconnu et se rabattra sur la déclaration précédente qui respecte la norme CSS 2. Le mécanisme qui consiste à ignorer une déclaration de style inconnue ou mal formée est un comportement standard. C'est une façon simple de gérer les capacités des navigateurs sans avoir à recourir à des artifices comme Modernizr.
Une autre solution pour régler ce problème de sur-impression consisterai à appliquer un z-index négatif au lien et à utiliser la propriété css pointer-events
définie par SVG. Néanmoins, cette solution est plus lourde à mettre en œuvre (il faut appliquer au conteneur de chaque lien la valeur none
et réapliquer la valeur auto
à tous les liens). A noter que seul Firefox, Chrome et Safari supportent la propriété pointer-events
sur les éléments HTML (je n'ai pas eu l'occasion de tester avec IE9, mais IE8 et Opera 10.53 ne la supportent pas).
Bords arrondis
Pour adoucir un peu l'effet on va rajouter des bords arrondis aux liens et aux badges
a:hover{
-moz-border-radius : 1.3em;
-webkit-border-radius : 1.3em;
border-radius : 1.3em;
}
a:after{
-moz-border-radius : 50%;
-webkit-border-radius : 1.3em;
border-radius : 1.3em;
}
Comme souvent avec les propriété issu de CSS3, vous aller devoir faire plusieurs déclarations. En effet, peu de modules ont atteint le statut de Candidate Recommendation
au W3C, or, c'est seulement à partir de ce statut que les constructeurs peuvent retirer leur préfixe spécifique aux propriétés qu'ils ont implémenté. le module Border & Background a atteint ce statut et tous les constructeurs sont d'accord pour dire qu'en ce qui concerne la propriété border-radius, il est plus que temps de retirer ce préfixe. Opera 10.53 supporte déjà cette propriété sans préfixe et les futures IE9 et Firefox 4 (en cour de discussion) devraient aussi la supporter sans préfixe.
Notez que pour le badge, avec Firefox, j'ai utilisé la valeur 50%. Cette valeur permet de créer des demi-cercles parfaits aux extrémités gauche et droite. Néanmoins, c'est un comportement spécifique à Firefox et mes tests ont montrés que certaines implémentations de Webkit (Chrome 5 par exemple) ainsi qu'Opera ne supporte par les pourcentages avec cette propriété. En outre, de ce que je comprend de la spécification, cette déclaration devrait produire une boite ovale plutôt que deux demi cercle réparti de part et d'autre de la boite, donc, pour la propriété standard, j'ai préféré mettre également une valeur absolue.
Ombres et lumières
Notre badge commence à être pas mal, on va le finaliser en lui donnant un peu de relief avec des effets d'ombres et de lumières.
a:after{
background : #900;
background : -moz-radial-gradient(35% 35% 0deg, ellipse cover, #E00 0%, #800, 90%);
background : radial-gradient(35% 35% 0deg, ellipse cover, #E00 0%, #800, 90%);
-webkit-box-shadow : rgba(0, 0, 0, .8) 0 2px 5px;
-moz-box-shadow : rgba(0, 0, 0, .8) 0 2px 5px,
rgba(255, 247, 0, .9) 0 0 3px inset;
box-shadow : rgba(0, 0, 0, .8) 0 2px 5px,
rgba(255, 247, 0, .9) 0 0 3px inset;
}
On commence par ajouter un dégradé radial en arrière plan pour donner l'illusion d'un éclairage venant du haut et de la gauche (positionnement du centre du dégradé à 35% du haut de la boite et à 35% du bord gauche de la boite). Ce dégradé aura la forme d'une ellipse horizontal (0 degrés de rotation) qui couvrira toute la boite. Le dégradé va d'un rouge vif au centre à un rouge sombre prés des bords). La syntaxe des dégradés sous Webkit est très différentes de celle de Gecko et honnêtement je n'y comprend rien donc, tant pis pour Safari et Chrome (faite vous plaisir dans les commentaires si vous avez la solution qui va bien). La syntaxe de Gecko étant celle qui est en cour de standardisation, je reproduit la déclaration sans le préfixe -moz-, mais c'est un parie sur l'avenir. En effet, les dégradés CSS font partie du module "Image Values" qui en est à son tout premier draft. Autant dire que ça peut (va ?) sérieusement bouger.
Ensuite on ajoute une ombre porté qui va permettre de "détacher" le badge de la surface de la page. Cette ombre est décalé de 2 pixels vers le bas et sont rayon de flou est de 5 pixels. Dans la mesure ou tout les navigateurs majeurs du marché qui supportent la propriété box-shadow
supportent également les couleurs rgba
, on ne va pas s'amuser à gérer une alternative sans couleur transparente (mais on pourrait :-p ).
Comme box-shadow
supporte les déclarations multiples séparées par un virgule, on va en profiter pour rajouter une ombre intérieur (mot clé inset
). C'est une ombre de couleur jaune pour créer un effet de lumière spéculaire (on aurait pu le faire via le dégradé radial, mais personnellement je trouve ça plus simple via box-shadow
). Vous noterez que je n'ai pas appliqué cette deuxième ombre à Webkit. Cela tiens au fait que l'implémentation des ombres internes couplé à l'utilisation de border-radius
est une catastrophe sous Chrome 5 et Safari 4 (je n'ai pas eu l'occasion de tester avec Safari 5). En effet, l'ombre reste carré la où la boite est arrondie... horrible !
Attention à l'accessibilité
Voilà, on a créer un jolie badge qui illumine la vie de notre internaute. Mais le loup se cache au coin du bois !
Au royaume des aveugles les borgnes sont rois.
Comme je vous le disait ci-avant, le problème majeur de la génération de contenu via CSS, c'est que ce contenu n'est pas inclus dans le DOM du document HTML. Ainsi, toute les outils d'aide à l'accessibilité qui repose sur le DOM ne pourront pas exploiter ce contenu généré. C'est par exemple le cas des revus d'écran qui vocalisent les pages Web. A ma connaissance, aucune ne prend en charge les contenus généré via CSS. Pour cette raison, il est vivement déconseillé de générer un contenu signifiant à l'aide de CSS. Dans le cas qui nous occupe ici, cette question n'est pas un problème. En effet, il s'agit uniquement d'enrichir l'expérience utilisateur, donc, tant pis pour les aveugles et les personnes utilisant de vieux navigateur (vous voyez de quoi je parle là, n'est-ce pas), ils pourront toujours utiliser les liens tel qu'ils ont l'habitude de les pratiquer, pour les autres, c'est cadeau, c'est bonheur
Les souris, ce fléau de l'humanité !
Vous aurez noté que l'apparition et la disparition du badge sont conditionnées par l'application de la pseudo-classe :hover
. Cette pseudo-classe correspond au survol de la souris sur le lien. Pas de bol, certains excentriques utilisent leur clavier, d'autres utilisent des interfaces tactiles (et même si Safari sous iPhone sait s'accommoder de cette pseudo-classe, ce n'est pas le cas de tous le monde) ... pire, votre souris pourrait tomber en panne ! Vous pourriez avoir la même attitude que précédemment et décréter que "tant pis pour ces êtres étranges qui sont mal équipé" et en rester là. C'est toujours possible mais ce serait dommage car il est assez facile de leur offrir la même expérience en étendant légèrement nos sélecteurs CSS en incluant les pseudo-classes :active
et :focus
.
a[hreflang]:hover:after,
a[hreflang]:active:after,
a[hreflang]:focus:after{
...
}
a[target="_blank"]:hover:after,
a[target="_blank"]:active:after,
a[target="_blank"]:focus:after{
...
}
Mon royaume pour un cheval.
Certains navigateurs sont comparables à des piétons et d'autres à des calèches grand luxe tractées par au moins 4 chevaux (ce qui sous entend également que même les navigateurs actuels les plus en pointe ont encore un long chemin à parcourir pour pouvoir être comparé à des coupés sport à injection directe !). Vous pouvez bien sur tester ce badge avec votre propre navigateur, et si vous n'avez pas tout un panel de navigateurs à votre disposition, voila quelques exemples de rendus :
Navigateur | OS | Résultat du rendu |
---|---|---|
IE 6 | Windows XP | |
IE 7 | Windows XP | |
IE 8 | Windows XP | |
IE 9 (Test Drive) | Windows 7 | |
Firefox 3.5 | Mac OS Leopard | |
Firefox 3.6 | Windows XP | |
Firefox 3.7a5 | Ubuntu 10.04 |
|
Chrome 5 | Windows XP | |
Safari 4 | Windows XP | |
Safari 5 | Mac OSX Leopard | |
Opera 10.53 | Windows XP | |
Opera 10.60 beta | Ubuntu 10.04 |
Vous noterez que, au delà des dégradations prévisibles (Dégradés, ombre, coins arrondis), c'est le rendu typographique qui diffère le plus d'un OS à l'autre (d'où l'importance d'utiliser des em
pour définir la taille des arrondis plutôt que des pixels). Vous noterez également un bug (?) avec IE8 (non, ne levez pas les yeux au ciel !), où le badge est positionné au dessus de l'arrière plan du lien, mais au dessous du texte (!!). Il semblerai que pour l'instant, l'équipe de IE9 ne se soit pas encore penchée sur la gestion des pseudo éléments qui se comportent exactement comme avec IE8.
Conclusion
Comme vous pouvez le voir, il n'est pas très difficile d'enrichir l'expérience des utilisateurs. On est dans un cas typique d'enrichissement progressif. Avec un navigateur ancien, l'utilisateur ne gagne rien, mais il ne perd rien non plus. Avec un navigateur moderne, il gagne en confort. Personne n'est lésé, tout le monde est gagnant.
Certes, cela est intéressant pour l'utilisateur, mais cela peut également être intéressant pour vous à titre personnel. En effet, rien ne vous empêche d'utiliser cette technique dans la feuille de style utilisateur de votre navigateur préféré pour enrichir votre propre expérience de navigation sur tous les sites sur lesquels vous surfez
Enfin, c'est grâce à ce genre de test qu'on peut remarquer que Webkit n'est pas aussi bon que l'affirme Apple et Google en terme de support de CSS3 (quand il ne font pas un honteux amalgame avec HTML5). Je ne parle pas des dégradés radiaux (là, c'est moi qui fait volontairement l'impasse) mais plutôt du support de box-shadow
couplé à border-radius
qui est loin de ce que propose les dernières versions de Gecko. Bon soyons claire, il ne sert à rien de se lancer dans un pinaillage du genre "Ah oui mais Webkit supporte mieux tel truc que Gecko". Ce que je veux vous dire, c'est qu'il ne faut pas prendre pour argent comptant ce que racontent les constructeurs de navigateurs. Testez, testez et testez encore ! Il n'y a que ça pour vous permettre de savoir ce qui marche dans les cas qui vous intéressent.