Écrire à l'imparfait du SVG
Par Kozlika le samedi 11 janvier 2014, 18:01 - Lien permanent
À la suite de discussions sur Twitter avec Nicolas Hoizey (@nhoizey) et Pascal Cauhépé (@eQRoeil), je publie ici mes démêlés avec l’intégration d’une bannière au format SVG dans une page html. Je les remercie tous deux de m’avoir accompagnée dans mes maugréements sur Twitter et de m’avoir aidée à chercher des solutions.
Dans le cadre de mon activité professionnelle, je dois intégrer dans l’entête une bannière qui se prête bien au format SVG (Scalable Vector Graphic) : essentiellement du texte et quelques formes simples. Comme j’avais lu pas mal d’articles et tutoriels sur le sujet et que je suis curieuse, j’ai sauté sur l’occasion pour tâcher de les mettre en pratique.
Les contraintes
J’ai des contraintes assez fortes, que je m’impose ou qu’on m’impose :
- la bannière doit être cliquable (c’est un lien de retour à l’accueil du site) ;
- elle doit s’afficher sur les vieilles versions d’Internet Explorer ;
- elle doit pouvoir subir des modifications au survol et au focus ;
- elle doit se transformer sur les petits écrans ;
- le fichier de l’image doit rester externe (pas de data directement dans la source) ;
- je ne souhaite pas utiliser de javascript ;
- le tout doit respecter les normes d’accessibilité (RGAA niveau 2 minimum).
Oui, je suis un peu maso. Enfin ce qui est surtout maso c’est de vouloir tout ça avec du SVG parce qu’avec une image classique ça n’a rien de bien sorcier. Si vous vous demandez pourquoi donc je m’embête avec le SVG je vous renvoie à ces explications du Pr Lapointe.
L’image
Les différents états de la bannière sont gérés directement par l’application de styles au SVG (j’y reviendrai). La voici donc telle qu’elle apparaît sur les écrans de bureau et tablette[1].
Si vous réalisez des images SVG pour le web, suivez les recommandations de Stéphanie Walter (voir ci-dessous), c’est très bien décrit.
Choix de la méthode d’insertion
Il existe plusieurs méthodes pour insérer une image SVG dans une page HTML. Chris Coyer les détaille dans son article “Using SVG” et Stéphanie Walter les évoque également dans « Un logo cliquable SVG avec alternatives ». Je vous renvoie à ces articles pour les découvrir.
Après avoir repoussé celles qui ne permettaient pas de manipuler le svg une fois inséré (en utilisant la balise <img />
ou en css via un background), celles qui auraient demandé d’insérer les données directement dans le html (svg inline) et celles qui sont trop hipster (et impliqueraient donc obligatoirement l’usage de javascript pour la compatibilité avec les vieux navigateurs), j’ai opté pour la robuste balise <object>
. Son utilisation est d’une simplicité biblique.
Code de l’étape 1 :
Fichier index.html.
<object type="image/svg+xml" data="img/logo-dali2.svg" id="object-logo"></object>
L’image s’affiche correctement sur les navigateur modernes.
Lien sur la bannière
Lorsqu’on entoure un object
avec un lien et qu’on veut que toute son étendue soit cliquable il faut donner à ce lien les mêmes largeur et hauteur que l’objet. Ça fonctionne très bien mais ce lien vient alors se placer au-dessus de l’objet et s’interpose donc entre lui et la souris. Ça ne convenait pas à mon usage puisque je veux que mon SVG reçoive des événements (le focus et le hover) car j’ai prévu que son survol déclenche des transformations (changement de couleur dans mon exemple simple).
Je dois donc placer le lien dans le fichier SVG lui-même. Rien de bien compliqué là non plus. J’ai suivi les instructions.
Attention : si vous voulez que toute la zone soit cliquable et pas seulement les éléments « pleins », il faut que votre dessin comporte un élément englobant le tout. (J’ai mis un rectangle transparent sur toute la zone, voir l’étape 5.)
Code de l’étape 2 :
Fichier logo-dali2.svg.
Ouverture du lien juste après la balise inaugurale <svg …>
, fermeture juste avant la balise finale </svg>
.
<svg …> <a xlink:href="url-de-mon-choix" target="_parent"> […] </a> </svg>
L’image est désormais cliquable.
Fallback
On peut placer un contenu entre l’ouverture et la fermeture de la balise object
pour indiquer aux navigateurs qui ne gèrent pas ce format ce qu’ils doivent afficher à la place. Plutôt que mettre une image en fallback, ainsi que beaucoup d’articles le préconisent, j’ai choisi d’insérer ici le titre du site et de placer l’image en background de cet élément. Cela me permettra de ne charger l’image de remplacement que dans la feuille de style pour IE et de faire en sorte que le survol offre lui aussi une autre présentation, comme pour sa sœur SVG. Comme j’ai placé le lien du SVG à l’intérieur de son fichier, il faut en mettre un sur le fallback.
Code de l’étape 3 :
Fichier index.html.
<object type="image/svg+xml" data="img/logo-dali2.svg" id="object-logo"> <a class="logo-fallback" href="url-de-mon-choix"><span class="visually-hidden">DALI - Accueil</span></a> </object>
Les vieux navigateurs afficheront une zone cliquable avec une image de fond. Notez le span class="visually-hidden"
: grâce à des règles css spécifiques je pourrai faire en sorte que le le contenu textuel ne soit pas visible sur l’écran mais lu par les aides techniques type Jaws.
Accessibilité
Puisqu’on parle d’accessibilité, il faut prévoir une alternative textuelle pour le cas où le navigateur peut afficher le SVG mais est utilisé par une personne ne pouvant pas le voir. Je modifie donc à nouveau le fichier SVG pour ajouter les éléments nécessaires sur les toujours excellents conseils de la maison Paciello.
Code de l’étape 4 :
Fichier logo-dali2.svg
Au sein de la balise inaugurale <svg …>
role="img" aria-labelledby="title"
et juste après elle (autrement dit juste avant la balise <a> placée à l’étape 2) :
<title>DALI - Accueil</title>
Le texte « DALI - Accueil » sera prononcé par les lecteurs d’écran. Je crois toutefois que ça n’est pas pleinement satisfaisant car ça ne fonctionnera que sur des versions récentes des navigateurs (?)
Styles
On peut appliquer des styles CSS sur des SVG de différentes manières : dans les différents éléments (path, g, circle…) eux-mêmes, dans l’entête du fichier, dans la feuille de style du site. J’ai choisi cette dernière option pour pouvoir y opérer des modifications plus facilement. Pour indiquer au fichier SVG où il peut trouver les styles qui le concernent, il faut insérer une ligne au début du fichier.
Puis on ajoute les styles dans la feuille de style qu’on vient d’indiquer. J’ai appliqué toute la colorisation via CSS, le fichier SVG n’embarque aucune instruction de couleur.
Code de l’étape 5 :
Fichier logo-dali2.svg
Au tout début, avant l’ouverture de la balise <svg …>
.
<?xml-stylesheet href="../css/screen.css" type="text/css" rel="stylesheet" ?>
Fichier screen.css :
.st0{fill:#137BBB;} .st1{fill:#E6007E;} .st2{fill:#4D96D2;} .st3{fill:#808080;} .st4{fill:#F39200;} .st5{fill:none;} #svg-logo a:hover #sigle path, #svg-logo a:hover #chocolat path, #svg-logo a:focus #sigle path, #svg-logo a:focus #chocolat path, #svg-logo a:hover path#coeur, #svg-logo a:focus path#coeur { fill: #FF2700; transition: all .7s ease; } #svg-logo a:hover #moustache path, #svg-logo a:hover #moustache path { fill:#000; } rect.svg-container { fill:transparent; }
Désormais le survol du lien change les couleurs à certains endroits de l’image. C’est bô !
Transformations sur petits écrans
Comme ci-dessus pour l’état de survol du lien, on peut aussi donner des instructions de transformation selon la dimension du conteneur de l’image. C’est ce que j’ai fait en supprimant la baseline, en remontant le sigle et en plaçant la moustache dessous. Attention : la largeur prise en compte n’est pas celle du body mais celle du conteneur de l’image.
Code de l’étape 6 :
Fichier screen.css.
@media screen and (max-width: 20em) { #svg-logo #sigle { transform: scale(.90) translate(6px, -12px); } #svg-logo #baseline { display: none; } #svg-logo #moustache { transform: scale(.95) translate(-288px, 20px); } }
Sur les mobiles la bannière est simplifiée.
C’est (presque) fini…
Vous pouvez regarder cet exemple dans mes ateliers. Si ça intéresse quelqu’un je peux déposer tout ça sur mon compte Github. Il reste des tests à faire sur plus de navigateurs et les styles sont à affiner pour le fallback mais grosso modo ça tourne :)
Si vous êtes parvenus jusqu’au bout de cet article et que vous avez des remarques ou suggestions à faire, soyez les bienvenus, je suis en plein défrichage, ça m’intéresse !
…et puis il reste le $%*£#& bien agaçant
C’est un souci inexplicable et mystérieux et énervant : lors de la navigation au clavier il y a un outline sur l’objet avant le focus sur le lien. Si quelqu’un sait comment l’éviter je suis preneuse. Je ne comprends pas du tout ce qui se passe, sans même parler de supprimer cette tabulation (mais il faudrait aussi) je n’arrive pas à enlever les pointillés, aucun de ces codes ne produit de résultat :(
Si vous êtes curieux du projet ou si vous souhaitez participer à son amélioration, j’ai ouvert un dépôt Github à cet effet.
Note
[1] Enfin pas tout à fait elle mais une dans le même esprit.
Commentaires
Génial toute cette description du cheminement que tu as suivi, merci !
Juste un petit point qui peut devenir ennuyeux si tu ne fais pas attention : tu charges dans le SVG la même CSS que pour la page, et du coup elle est chargée deux fois.
Ce sera surtout critique si tu charges effectivement la même CSS et non deux différentes, dédiées à chaque besoin. Mais vu que celle nécessaire au SVG ne sera sans doute utile que là, je te conseillerais plutôt de la mettre directement dans le SVG.
Ah oui tu as raison, je ferai ça en prod, merci beaucoup !
Bonsoir, après les mauvaises nouvelles sur twitter (pas de focus chrome, non support de svg sur vieux droid qui n’auront pas le fallback css ciblant vieux IE) une peut-être bonne…
Mais avant une autre mauvaise dans chrome (et opera sur android) les translate ne fonctionnent pas, la moustache reste à droite par exemple MAIS un <use> est une piste (réutiliser un élément sans dupliquer le code) en cachant un <g> avec les MQ.
La piste…
Parmi les contraintes tu as
et j’avoue ne pas comprendre pourquoi… (j’ai quelques idées mais de possibles réponses)Si c’est envisageable, je pense avoir une solution qui respecte les autres contraintes (il faudrait peut-être un tout petit petit peu de js pour l’ajout d’une classe si ma solution de replis css ne fonctionne pas ;) en cas de non support)
Dans les grandes lignes (j’ai un peu testé et semble prometteur mais c’est prématuré de crier victoire) :
un lien avec le svg inline dedans
a:focus path
en css fonctionne et permet de contrôler ton affichage (couleurs, transitions…)Pour le fallback le plus simple serait un test en js du support de svg inline et ajout d’une classe, un truc du genre (piqué à modernizr, adapté, mais pas testé ; c’est pour une idée de la quantité de code)
@@ var svgclass = ‘no-svg’ ;
function canWePlayWithSomeSVGPlease () {
j’ai peut-être une piste full css mais on risque d’en laisser de côté (ma remarque plus haut à propos du fallback css)
Pour garder un contrôle sur le svg sans toucher au HTML ou aux templates, un include php est envisageable.
Une solution en js serait envisageable avec le test ci-dessus. Si le svg est supporté, remplacer le innerHTML du <a> par le svg chargé en ajax.
En utilisant une image en bg (png) dans le css ça passerait inaperçu (le <a> devra avoir des dimensions mais un ratio à base de padding trick ferait l’affaire) .
Cela fait quand même un double download, évitable au prix d’un FOUC avec le fallback css activé uniquement si le test js a été réalisé (et a échoué)
Bon, je creuse de mon côté et te fais signe quand j’ai de quoi tester !
PS le texte en path c’est parce que les polices sont “spéciales” ?
SVG FTW et merci pour l’article, on a besoin de partage de contraintes & solutions pour progresser …
Pascal
Pascal, merci pour ces retours (et nos échanges sur Twitter). Quelques réponses :
Pour le translate, je vais faire comme tu dis, j’ai découvert ça hier soir moi aussi, en regardant le site de test depuis mon mobile (avec “resizer” sur mon Firefox, c’était nickel…).
Pour la compat des vieux Android, s’il n’y a qu’eux on laisse tomber, le pourcentage d’utilisateurs est est extrêmement faible – quasi inexistante – sur ce site ; au pire je mettrai la détection SVG de Modernizr si d’autres navigateurs sont concernés – hormis IE.
De façon générale, je sais que pas mal des problèmes rencontrés peuvent trouver une solution via js et j’en viendrai peut-être là quand mon chef trouvera que j’ai assez joué comme ça et qu’il serait temps de mettre en prod, mais pour l’instant je continue de me le fixer en contrainte ;-) (Cela dit je vais devoir en mettre quoi qu’il en soit pour le menu en dropdown, comme on le voit sur la démo car en full css l’accès aux sous-menus au focus n’est pas satisfaisante.)
Quoi qu’il en soit, quitte à ce que ce soit « gracieusement dégradé », il faut que tout fonctionne sans js.
Pour l’insertion des data dans le fichier html, pour ce site on est sous SPIP c’est forcément faisable, mais sur d’autres sites de la maison nous pourrions vouloir aussi insérer des svg ; or la plupart des autres sites sont gérés par des prestas, avec des techno diverses (php, pyton, java…) : outre qu’il faudrait une solution transverse, le moindre changement dans les templates est facturé beaucoup plus cher que pour les “assets”. Donc comme ci-dessus : oui si on ne trouve aucune solution potable sans en passer par là.
En réalité, il me semble que par rapport à mes contraintes il reste surtout ces problèmes de focus à gérer (celui en trop sur Firefox, celui qui n’y est pas sur webkit), non ?
J’ai une vague idée à creuser, je vais m’y atteler et voir ce que ça donne. On se tient au courant :-P
Merci encore !
Et hop !
La démo est à jour et c’est poussé sur Github.
Salut,
quelques remarques :
Pourquoi faire compliqué qd on peut faire simple ;)
Merci pour ton commentaire Aurélien :-)
Tu me perturbes pour ARIA car j’ai trouvé ces conseils chez Paciello, j’ai dû mal interpréter, je vais remplacer par label. Je sais que le RGAA ne le prend pas (encore ?) en compte mais peut-être dans sa future mise à jour ? En attendant si le “score” RGAA en est baissé mais que c’est accessible, ça me va.
Pour ta suggestion de mettre l’image en élément purement décoratif, mais je préfère conserver le lien dans l’objet car sinon je ne peux pas déclencher de transformations au survol ou au focus. J’ai essayé des trucs du genre
a:focus + object …
mais ça ne produit pas d’effet. Je me suis peut-être trompée quelque part.C’est pour ces mêmes raisons de besoins de transformation que je n’utilise pas l’image en background CSS. On ne peut pas changer la couleur de ceci ou faire disparaître cela sur une image de background.
L’autre truc que je pourrais essayer c’est de mettre l’image en :before du titre : je ne sais pas trop quelles transformations ça autorise à vrai dire. À tester !
Après, pour être honnête, c’est aussi un peu beaucoup pour le sport car je pourrais faire plusieurs images pour les différents états et largeurs d’écrans, mais ça me permet aussi de découvrir le sujet ! ;-)