Le mois dernier, j’ai eu l’honneur de donner une conférence à The Email Design Conference de Londres intitulée « Thinking Outside the <table> ». C’est un mélange de certains de mes précédents articles, comme Super Mail Forward, Flexbox dans un e‑mail ou la technique des Fab Four, mais avec une nouvelle narration et de nouveaux contenus.
J’ai publié mes slides bruts en anglais. Et voici une retranscription en français de ma conférence (une version en anglais est aussi disponible ici).
Je suis un intégrateur Web mais j’intègre aussi des e‑mails. La question qu’on me pose le plus souvent quand je dis ça à d’autres intégrateurs est « Comment va ta santé mentale ? ». Probablement parce qu’ils pensent à leur dégoût de devoir encore utiliser des tableaux pour faire de la mise en page, d’avoir à écrire des styles en ligne et autres bizarreries propres aux e‑mails. Mais je me suis toujours dit qu’il y avait bien plus à faire si on arrivait à dépasser ces premières impressions.
Aujourd’hui, je veux vous présenter trois exemples de choses que j’ai fait dans l’année passée qui illustrent ce qu’il peut se passer quand vous commencez à « penser en dehors de la <table> ».
Mon premier exemple est un e‑mail que j’ai appelé Super Mail Forward. L’an dernier, Litmus a organisé un concours communautaire dont le thème était « des e‑mails qui valent le coup d’être transféré ». Le but était de « tirer parti des comportements qui se produisent lorsqu’un e‑mail est transféré ». J’ai trouvé que c’était un sujet très intéressant, en particulier parce que je n’y connaissais rien du tout.
Tout d’abord, il fallait que je comprenne ce qu’il se passe quand vous transférez un e‑mail (en particulier depuis un webmail). Je voulais travailler principalement avec des webmails car c’est beaucoup plus simple de lancer un inspecteur web pour voir comment les styles et le code HTML sont transformés depuis un webmail que depuis la plupart des clients mails natifs. Donc que se passe-t-il quand vous transférez un e‑mail ?
Et bien il s’avère que : pas grand chose de plus que quand le webmail reçoit l’e‑mail en premier lieu. Cela signifie que si un webmail ne supporte pas les images de fond en CSS, comme Outlook.com par exemple, il filtrera la propriété background
quand il recevra l’e‑mail, et transférera l’e‑mail ainsi après ça.
En ayant ça en tête, je n’arrêtais pas de demander… Combien de fois est-ce qu’on pourrait transférer un e‑mail avant que son code ne devienne une incompréhensible bouillie ? Ça m’a donné l’idée de faire une sorte de jeu où l’on pourrait transférer un e‑mail entre les quatre webmails d’AOL, Yahoo, Outlook.com et Gmail. À chaque fois que vous transférez l’e‑mail vers le bon webmail dans le bon ordre, une case correspondant à ce webmail s’activerait en vert, et resterait ainsi jusqu’à la fin. J’en ai alors fait un prototype.
Et voici le code pour une version basique de cet e‑mail.
Une partie de ce code sert à activer des cases en vert. Et l’autre partie sert à maintenir les autres cases en gris selon certaines conditions.
Quand vous envoyez l’e‑mail pour la première fois sur AOL, le webmail entoure le code HTML avec une <div class="aolReplacedBody">
et préfixe chaque règle CSS avec la même classe .aolReplacedBody
. La première cellule est activée avec un style en ligne.
Afin de maintenir cette cellule en gris lorsqu’on reçoit l’e‑mail dans un autre webmail, j’ai utilisé un bug d’AOL. Quand AOL rencontrais une déclaration d’image de fond dans une balise <style>
, le webmail remplaçait incorrectement le chemin de l’image par du code HTML. Ça n’avait absolument aucun sens, et du coup ce style et tous ceux qui suivaient étaient ignorés sur AOL, et conservés ainsi lors du transfert vers les autres webmails.
Quand vous transférez l’e‑mail de AOL vers Yahoo, Yahoo va une nouvelle fois préfixer chaque nom de classe avec son propre préfixe (comme yiv113
) ainsi que par un identifiant qui lui est propre (comme yui_123456789
).
Afin d’activer la cellule en vert pour Yahoo, j’ai utilisé le hack @media yahoo
(trouvé initialement par Mark Robbins). L’an dernier, Yahoo a ajouté le support des media queries, mais seulement pour certaines cibles (comme min-width
ou max-width
). Yahoo supprime n’importe quel mot clé non reconnu ou non supporté after une déclaration @media
. Ainsi « @media yahoo
» sera transformé en « @media
« , activant alors les styles contenus dans cette media query.
Ensuite, lorsque vous l’e‑mail de Yahoo vers Outlook.com, ce dernier va encore une fois préfixer tous les attributsclass
et id
avec son propre préfixe (ecx
), et préfixer chaque règle CSS avec une classe « .ExternalClass
« . (NB: Je parle ici de l’ancienne version d’Outlook.com, encore disponible en France.)
Outlook.com ne supporte pas les images de fond déclarées en CSS. Donc le webmail supprime tous les styles en ligne qui servaient à maintenir les cellules en gris grâce à une image spacer. C’est chouette, parce que ça active la troisième cellule en vert pour Outlook.com. Mais ça active aussi la dernière, normalement réservée à la dernière étape pour Gmail. Afin d’éviter ça, je prévois une règle pour maintenir cette quatrième cellule en gris à cette étape.
Enfin, lors du transfert de l’e‑mail de Outlook.com vers Gmail, Gmail va supprimer tous les attributs id
ou class
. Cela rend tous les styles dans le <head>
inutiles. Et c’est comme ça que toutes les cellules se retrouvent activées en vert.
Et voilà comment j’ai fait un premier prototype. Et puis je me suis dit : si j’arrive à jouer avec une couleur de cellule à la fois, pourquoi ne pas pimenter les choses et jouer avec une grille plus complexe. Comme, par exemple, 256 cellules. C’est suffisant pour faire du pixel art. Et comme je suis un grand fan de Mario, j’ai choisi d’utiliser les blocs d’objets de Super Mario World.
Voici à quoi ressemble l’e‑mail final. (Et voici son code.)
Et voici des captures d’écran de sa transformation lors de ses transferts entre chaque webmail.
Quand j’ai codé cette version finale, j’ai rapidement fait face à la limite de 100 Ko où les webmails bloquent le reste de l’e‑mail. J’ai donc passé pas mal de temps à optimiser la grille de pixels afin d’avoir aussi peu de cellules que possible, en fusionnant des cellules ayant un même modèle entre le bloc d’objet et le champignon.
Quelques jours après avoir soumis ma participation pour le concours de Litmus, j’ai eu la joie d’apprendre que j’avais gagné. Sauf que, bon, j’étais en fait le seul à avoir participé. Je suppose que parfois on se sent seul quand on fait de l’intégration d’e‑mails.
Mais la vraie bonne nouvelle est qu’AOL a corrigé son bug de background. L’an dernier, j’ai ouvert un dépôt Github appelé Email Bugs où je liste certains problèmes et bugs que je rencontre. J’ai créé un rapport détaillé de ce bug. Et il a été rapporté à des gens travaillant chez AOL via Twitter. Il s’avère qu’AOL est très réceptif en ce qui concerne les bugs de leur webmail, et j’ai pu leur soumettre et aider à corriger d’autres bugs depuis.
La plupart des bugs que je trouve sont sans danger. Mais parfois, je trouve des choses bien plus sérieuses et importantes. Ce qui m’amène à la seconde partie de ma conférence sur la sécurité des webmails.
La sécurité des webmails est très importante. Pensez à toutes les données personnelles que vous avez dans vos e‑mails. Des messages de votre famille, de vos amis, avec des photos personnelles. Des messages de votre banque. Des messages de tous les sites auxquels vous vous êtes jamais inscrits.
À côté de ça, certaines balises HTML ou propriétés CSS peuvent être dangereuses dans un email. Par exemple, position:fixed
en CSS permet de positionner un élément par dessus tout le reste, même la propre interface d’un webmail. L’exemple suivant pourrait positionner un lien vers un site malicieux par dessus tout le reste.
<a style=”position:fixed; left:0; right:0; top:0; bottom:0;” href=”http://example.com">…</a>
Donc certaines balises HTML et certaines propriétés CSS doivent être filtrées par les webmails. Mais parfois c’est difficile de comprendre pourquoi et comment c’est fait.
L’an dernier, j’ai remarqué que l’un des webmails français les plus populaires filtrait la propriété overflow en CSS. Mais il faisait ça en remplaçant le mot « overflow
» par « java-script
». Ainsi le code suivant…
<div style="overflow:hidden;">…</div>
…sera transformé en :
<div style="java-script:hidden;">…</div>
Mais non seulement le webmail faisait ça à l’intérieur d’un attribut style en HTML, mais il faisait aussi ça dans le texte de l’e‑mail lui-même. Cela signifie que si vous receviez un e‑mail de Stack Overflow, il serait alors écrit « Stack java-script
». Et il faisait ça aussi pour les URL. Ce qui signifie que n’importe quel lien à l’intérieur de l’e‑mail vu depuis ce webmail redirigeait vers stack-javascript.com
.
Un attaquant pourrait acheter ce domaine, créer une page de phishing qui ressemble à Stack Overflow, et récupérer des informations de l’utilisateur. C’est terrible pour la sécurité des utilisateurs.
Mais ce qui m’obsédait par dessus tout, c’était… pourquoi java-script
? Pourquoi un webmail remplacerait le nom de la propriété overflow
en CSS par le mot-clé java-script
?
Après avoir parcouru une liste de mots-clés en JavaScript, j’ai découvert que overflow
était en fait un évènement en JavaScript, spécifique au moteur de Mozilla. J’ai donc pris cette liste d’évènements en JavaScript, et j’ai testé chacun de ses évènements pour voir si d’autres mots-clés étaient remplacés. Bien heureusement, la plupart des attributs d’évènements en HTML comme onload
, onmouseover
ou onclick
étaient filtrés par ce webmail. Mais sur les 301 évènements que j’ai testé, seuls 126 étaient filtrés.
Parmi les évènements non filtrés, il y avait onanimationend
. C’est un évènement déclenché lorsqu’une animation CSS se termine sur un élément. J’ai pensé qu’il serait possible d’utiliser ça pour exécuter du JavaScript sans aucune interaction utilisateur. Alors j’ai construit l’e‑mail suivant pour déclencher une alerte en JavaScript.
<style>
@keyframes bar {}
.foo { animation:bar; }
</style>
<div class="foo" onanimationend="alert('Hi!');"></div>
Et ça a fonctionné.
J’étais sous le choc. Parce qu’une fois que vous laissez le moindre bout de JavaScript passer, tout devient possible. J’étais alors en mesure de charger un script externe qui pouvait lire tous les e‑mails d’un utilisateur, d’envoyer des e‑mails de la part d’un utilisateur, et même de supprimer des e‑mails. J’ai testé ça avec succès entre deux de mes comptes test.
Alors bien sur j’ai rapporté ce bug. Mais c’était dur, parce que ce webmail n’a aucun formulaire pour soumettre des bugs. J’ai du demandé de l’aide sur Twitter, et j’ai fini par trouver quelqu’un qui connaissait quelqu’un dans la société concernée. J’ai fini par avoir un rendez-vous téléphonique avec des membres de l’équipe de sécurité de cette société. J’ai expliqué tous les bugs et failles que j’avais trouvé. Et leur réponse a été pour moi une douche froide. On m’a expliqué qu’un webmail était par nature une faille de sécurité, et qu’ils devaient bien faire des compromis entre la sécurité et l’expérience utilisateur. Je leur ai dit que ça m’intéressait de travailler avec eux pour les aider à rendre leur webmail plus sécurisé pour leurs utilisateurs. Et puis c’est tout.
J’ai reçu un timide merci par e‑mail quelques jours après ce coup de fil. Et il leur a fallu plus de cinq mois pour corriger ce problème.
La sécurité des webmails c’est très important. Et voici un exercice de pensée intéressant. Comment filtreriez vous JavaScript dans un e‑mail ? Vous pouvez supprimer toutes les balises <script>
, ce qui est un bon début. Mais vous devez aussi supprimer tous les attributs d’évènements en HTML. Il y en a des centaines, et certains sont spécifiques à certains navigateurs. Et de plus en plus d’évènements sont ajoutés avec chaque nouvelle spécification.
Plus j’y pense, et plus je crois que le meilleur moyen de rendre un webmail sécurisé est d’utiliser une liste blanche. N’autorisez que certaines balises, certains attributs HTML et propriétés CSS qui sont sans danger.
C’est quelque chose que Gmail fait extrêmement bien. Je dirais même peut-être un peu trop bien pour nous, développeurs d’e‑mails. Gmail n’autorise aucun attribut class
ou id
, pas de balise <style>
sur mobile, ce qui signifie pas de media queries sur mobile.
Il y a une citation célèbre que j’adore (attribuée à Phil Karlton).
Il y a deux choses compliquées en informatique : l’invalidation d’un cache et nommer les choses.
J’aimerais vraiment qu’on en ajoute une troisième.
Il y a trois choses compliquées en informatique : l’invalidation d’un cache, nommer les choses et les e‑mails responsive.
C’est compliqué de coder des e‑mails responsive parce qu’il faut jongler entre des clients comme Outlook 2016, qui utilise le moteur de rendu de Word et ne supporte presque rien à part des tableaux, et plein de webmails avec un support de CSS assez inégal.
Heureusement, les développeurs d’e‑mails ont appris à s’adapter. Et certaines techniques sont devenues assez populaires, comme la méthode fluide/hybride pour créer des e‑mails responsive sans media queries. Cette technique en particulier repose sur le fait d’avoir des éléments alignés côte à côte (en utilisant des positionnements flottants en CSS ou la propriété display:inline-block
), et de les faire passer les uns sous les autres quand il n’y a plus suffisamment de place. Mais j’ai toujours eu un problème avec ça : sur un grand écran mobile sur Gmail, on se retrouve avec de toutes petites colonnes centrées.
Alors comment faire grandir une colonne sur mobile sans media queries ?
Les principaux styles nécessaires pour faire une telle grille sont les suivants.
.grid {
display: flex;
flex-wrap: wrap;
flex-direction: row;
}
.grid > * {
flex: 1 1 auto;
}
Ça fonctionne très bien dans un navigateur. Mais dans un webmail comme Gmail, le seul style supporté est display:flex
. Cela signifie que les valeurs par défaut de flexbox sont appliquées, et la grille précédente ressemble à ça.
Flexbox est donc à mettre à l’écart pour un e‑mail. Alors comment faire grandir une colonne sur mobile sans media queries et sans flexbox ?
J’ai trouvé une solution. Et il y a trois idées qui m’ont mis la puce à l’oreille.
La première concerne le support CSS de Gmail. À quelques exceptions près, Gmail filtre les styles selon le nom des propriétés, mais pas leurs valeurs. Cela signifie que, dans l’exemple suivant, on ne peut pas utiliser la propriété background-size
car Gmail ne la supporte pas.
.hero {
background: url(image.jpg) 0 0;
background-size: cover;
}
Cependant, Gmail supporte bien la propriété background
. On peut donc utiliser une déclaration background-size
dans la propriété raccourcie background
, et Gmail ne la filtrera pas.
.hero {
background: url(image.jpg) 0 0 / cover;
}
Cela signifie qu’on peut aussi utiliser des choses comme linear-gradient
ou radial-gradient
en CSS.
.hero {
background:radial-gradient(circle at 30% 107%,
#fdf497 0%, #fdf497 5%, #fd5949 45%,
#d6249f 60%, #285aeb 90%);
}
(Cet exemple est tiré de l’excellente newsletter EmailWeekly en hommage au nouveau logo d’Instagram il y a quelques mois.)
Tout cela signifie que Gmail supporte aussi la fonction calc()
en CSS. Et ça c’est intéressant car cela signifie qu’on peut faire des calculs basés sur la largeur d’éléments. Par exemple, le code suivant calcule une colonne fluide prenant toute la largeur de son élément parent, moins 200px
d’une autre colonne fixe.
.main {
width: calc(100% — 200px);
}
La seconde idée qui m’a mis sur la piste d’une solution est cet article de Mike Riethmuller à propos de typographie responsive. Il a créé la longue formule suivante mélangeant des limites de tailles de polices, des points de ruptures, des unités de viewport avec une division et une multiplication. Et ça fonctionne.
font-size: calc( 12px + (24 - 12) * ( (100vw - 400px) / ( 800 - 400) ));
Même si c’est un extraordinaire exemple de ce qu’il est possible de faire avec calc()
, cette méthode nécessite toujours des media queries comme garde fou pour éviter que la taille du texte diminue ou grandisse trop. Mais quand même, ça reste vraiment intéressant.
La dernière idée qui m’a aidé à trouver une solution est ce quiz par mon confrère Raphaël Goetter. Il demande : « sans tricher, quelle est la taille du div dont les propriétés CSS sont indiquées ci-dessous ? »
min-width: 480px;
width: 320px;
max-width: 160px;
La réponse est… 480px. Voici comment c’est défini dans la spécification de CSS 2. On peut résumer ça comme ça :
- Si
width
est plus grand quemax-width
, c’estmax-width
qui gagne. - Si
min-width
est plus grand quewidth
oumax-width
, c’estmin-width
qui gagne.
Avec tout ça en tête, j’ai passé des heures à chercher une solution, à jouer avec la fonction calc()
, à essayer des choses avec unités de viewport en CSS. Jusqu’au jour où… Bingo ! J’avais trouvé !
La technique que j’ai trouvé se base sur les trois propriétés width, min-width et max-width, et la fonction calc() en CSS.
Et j’ai appelé ça… la technique des Fab Four.
Il y a principalement deux raisons pour lesquelles j’ai appelé ça comme ça :
- J’ai trouvé ça en janvier dernier, et la discographie des Beatles venait juste de sortir sur Spotify. Alors j’écoutais beaucoup les Beatles à cette période.
- Il y a deux choses compliquées en informatique : l’invalidation d’un cache et nommer les choses.
La technique des Fab Four permet de construire une grille qui va (par exemple) passer de quatre colonnes au-dessus d’un point de rupture défini à une seule colonne au-dessous. Voici le code correspondant.
max-width: 100%;
min-width: 25%;
width: calc((480px — 100%) * 480);
- La propriété
max-width
définit la largeur des éléments sous le point de rupture. À 100%, chaque élément prendra toute la largeur. - La propriété
min-width
définit la largeur des éléments au dessus du point de rupture. À 25%, on obtient quatre colonnes. - La propriété
width
fait le calcul à partir du point de rupture désiré. Ici on définit un point de rupture à 480px.
Voici ce que ça donne à deux différentes tailles : l’une au dessus du point de rupture, et l’autre en dessous.
À 500px (au dessus du point de rupture), la largeur calculée vaut -9600px. Alors la largeur du min-width de 125px est appliquée (créant ainsi une mise en page sur quatre colonnes).
À 400px (en dessous du point de rupture), la largeur calculée vaut 38400px. Alors la largeur du max-width de 400px est appliquée (créant ainsi une mise en page sur une seule colonne).
Tout ça fonctionne bien dans Gmail (du webmail desktop, même pour les comptes Google Apps, aux applications iOS et Android), mais aussi sur Apple Mail, l’ancien Outlook.com, Thunderbird, le webmail d’Orange et le webmail d’AOL. Mais il y a quand même quelques bidouilles à faire pour que ça fonctionne bien partout.
L’ancienne version d’Outlook.com (encore présente en France) supporte calc()
. Mais le webmail filtrera chaque propriété avec un calc()
qui contient des parenthèses. Cela signifie que calc(480px - 100%)
est supporté, mais pas calc((480px - 100%) * 480)
. Vu que ma formule initiale comporte des parenthèses, il faut qu’on la refactorise pour éviter les parenthèses. La formule modifiée pour supporter l’ancienne version d’Outlook.com ressemble alors à la suivante.
width:calc(480px * 480 — 100% * 480);
Les anciennes versions d’Android (avant Android 5.0) ou iOS (avant iOS 7) ont besoin des préfixes -webkit-
pour que ça fonctionne. Notre code ressemble alors au suivant.
max-width: 100%;
min-width: 25%;
width: -webkit-calc(480px * 480 — 100% * 480);
width: calc(480px * 480 — 100% * 480);
Pour les autres clients qui ne supportent pas calc()
(comme Yahoo), il faut prévoir une dégradation gracieuse. On peut prévoir une solution de repli fluide/hybride avec les styles suivants.
display:inline-block;
min-width:120px;
width:25%;
Mais une fois combiné avec le code des fab four, on se retrouve avec deux déclarations de la propriété min-width
. Afin d’éviter que les deux ne rentrent en conflit, on va entourer celle des fab four avec un calc()
également. Ce qui donne le code suivant.
display:inline-block;
min-width:120px;
width:25%;
max-width:100%;
min-width:-webkit-calc(25%);
min-width:calc(25%);
width:-webkit-calc(480px * 480 – 100% * 480);
width:calc(480px * 480 – 100% * 480);
Le nouvel Outlook.com ne supporte pas calc()
non plus, donc notre version dégradée gracieusement devrait prendre le pas. Sauf que ce webmail a un bug qui va complètement vider un attribut style
en ligne s’il y a un signe de multiplication à l’intérieur. Afin d’éviter, il faut faire le calcul à la main. Ce qui donne le code suivant.
width:calc(230400px - 48000%);
Le code complet final ressemble alors à ça.
display:inline-block;
min-width:120px;
width:25%;
max-width:100%;
min-width:-webkit-calc(25%);
min-width:calc(25%);
width:-webkit-calc(230400px - 48000%);
width:calc(230400px - 48000%);
J’aime cette technique parce que c’était sous notre nez depuis les premiers jours d’IE9. Mais apparemment, personne n’y avait pensé, ou n’avait trouvé ça suffisamment intéressant pour en parler. Alors que pourtant, on se rapproche tout doucement de la notion d’Element Queries, le Saint Graal moderne en CSS.
Si vous ne deviez retenir qu’une chose de tout ça, c’est que les contraintes poussent à la créativité.
Les développeurs d’e‑mails ont tendance à se plaindre du manque de bon support de CSS dans les webmails. Et c’est vrai qu’il y a des bugs. Mais il s’avère qu’on peut aider à les corriger. Et les webmails ont leurs propres contraintes de sécurité, ce qui signifie qu’ils ne pourront jamais tout supporter.
Je suis convaincu que si l’on garde ça en tête en travaillant sur des e‑mails, on peut commencer à penser en dehors de la boîte, et ces contraintes peuvent devenir un moteur pour la créativité.
Aurélie L, le
Bravo, ya rien à dire de plus !!! Et merci, pour le partage des infos !
Nelson, le
Encore une fois … grand bravo ;)
J’ai pu assister en live à cette conf et c’était parfait. J’ai appris énormément de choses …
Il ne reste plus qu’à tester ;)
bwenders, le
Excellent article !!
Petit bémol … Etant dans la production d’e-mail responsive, je trouve la technique Fab Four vraiment complexe à mettre en place et assez restrictive en terme de design (vous savez tous comment sont les graphiste lorsqu’on leur demande de changer leurs méthode de travail).
Mais je la garde sous le coude !
Continue comme ça :)
Yohann, le
Je suis vraiment halluciné par tout ce que je lis, cet article est un vrai puit de savoir ! Merci !
Rémi, le
@Aurélie, @Nelson et @Yohann : Merci !
@bwenders : Je suis d’accord. Il n’y a malheureusement pas de solution technique universelle qui passe partout.
Aurélie, le
Quand je vois le dernier email de Limtus avec les effets de feu d’artifice, je me dis que j’adore mon métier, encoder des emails, c’est quand même le kiffe
http://pages.litmus.com/webmail/31032/305555204/0cae0162119875db7d080f1185a30009
Ma santé mentale va bien, merci :)
Mike, le
Excellent!!!
Mossroy, le
Excellent article : chapeau pour toutes ces recherches et merci de la diffusion de toutes ces infos sur les comportements des différents clients mail, et des hacks correspondant : très éclairant.
Concernant le signalement de failles de sécurité, je ne suis pas surpris des difficultés avec certains acteurs, j’ai vécu une expérience similaire dans un autre domaine : grosse difficulté à joindre la bonne personne/équipe, et lenteur de correction.
En tous cas merci pour l’article
Stéphane, le
Woah ! Merci Rémi pour cette leçon de CSS et d’intégration, c’est très didactique et très bien écrit :)
MatteoBZ, le
Wouha ! Je rejoins tout le monde et vous disant bravo. Bravo pour le partage, bravo pour la recherche, bravo pour le résultat !