Guidelines : E-mailing

Statut : Working Draft (WD)

Cette présente convention rassemble les bonnes pratiques E-mailing en production appliquées par l'agence web Alsacreations.fr. Elle a pour but d'évoluer dans le temps et de s'adapter à chaque nouveau projet.

Ressources et outils

Les clients e-mails sont capricieux, ils interprètent à leur manière, ils redimensionnent s'ils le souhaitent, ils reconnaissent uniquement les styles qui leur conviennent, etc.

Les bonnes pratiques habituelles y sont remplacées par des tableaux de mise en forme, de styles inline et une foule de bugs en tout genre.

Voici quelques ressources à garder dans ses favoris :

Utiliser un Framework de construction : Maizzle

Un framework spécialisé dans l'e-mailing, tel que Maizzle, est une aide précieuse pour les raisons suivantes :

  • Moteur de template (gabarits réutilisables, inclusion de fichiers partiels, transmission de variables, ajout de conditions, de boucles, etc.)
  • Logique "email" respectée, fichiers de configuration et de variables, prise en compte native du Responsive Webdesign
  • Respect natif des (non)Standards de l'emailing : styles en ligne dans les balises, tableaux de mise en forme, ajout automatique d'attributs ou balises propriétaires, cellpadding, cellspacing, etc.
  • Nettoyage du code en phase de production : CSSpurge, minification, obfuscation, suppression d'attributs et classes inutiles, etc.
  • Divers apports : doctype, langue, notation sur 6 digits au-lieu de 3 pour les couleurs hexa, ajout de rôles ARIA tel que role=presentation, etc.
  • Vue en direct dans le navigateur pour déboguage (BrowserSync).

La documentation de Maizzle (indispensable pour modifier / mettre à jour les fichiers) est disponible à l'adresse https://maizzle.com, et un Tutoriel a été rédigé sur Alsacréations.

Les outils indispensables pour développer sous Maizzle

  • Visual Studio Code : éditeur HTML (optionnel mais recommandé)
  • Node.js : Permet d'installer les autres outils, dont la commande npm
  • Maizzle : Framework de construction d'e-mails HTML
  • Tailwind CSS : Framework CSS sur lequel est basé Maizzle

Installation initiale

  1. Dans un Terminal (ou dans l'outil Terminal de Visual Studio Code), installation de toutes les dépendances du projet à l'aide de npm install
  2. Installation de Maizzle via npm i -g @maizzle/cli

Les deux commandes principales de Maizzle sont :

# génération des pages HTML à la volée (dossier de développement)
maizzle serve

# génération finale, optimisée (dossier de production build)
maizzle build production

Tableaux de mise en forme

Certains clients e-mails ajoutent une bordure de 1 pixel autour de chaque cellule de tableau. Maizzle corrige automatiquement le problème en appliquant border-collapse: collapse; sur les tableaux.

Gmail ne reconnaît pas systématiquement les largeurs de 100%. Lorsque width ne suffit pas, il est parfois nécessaire d'ajouter min-width. Ainsi certains tableaux de structure doivent être stylés ainsi : <table class="w-full min-w-full">.

Outlook impose une hauteur minimale de 17px aux <td>. Pour créer des séparateurs très fins, l'élément <hr> est parfait :

<hr class="border-0 h-px bg-gray text-gray" />
<!-- ^ séparateur gris 1 pixel-->
<hr class="border-0 h-10 bg-gray text-gray" />
<!-- ^ séparateur gris 10 pixels-->

Pour de simples espacements verticaux, <div> fait l'affaire :

<div class="leading-64 sm:leading-32">&nbsp;</div>
<!-- ^ séparateur 32px mobile, 64px desktop (leading = line-height)-->

Grilles responsive

Une grille de colonnes responsive peut être simplement réalisée en passant les cellules en display: block en mobile.

Ici, deux cellules de tailles égales sur grand écran, et l'une sous l'autre sur mobile :

<table class="w-600 sm:w-full">
  <tr>
    <td class="w-half sm:block sm:w-full">4 cols</td>
    <td class="w-half sm:block sm:w-full">8 cols</td>
  </tr>
</table>

Ici, deux cellules de tailles 4/12 et 8/12 sur grand écran, et l'une sous l'autre sur mobile :

<table class="w-600 sm:w-full">
  <tr>
    <td class="w-4/12 sm:block sm:w-full">4 cols</td>
    <td class="w-8/12 sm:block sm:w-full">8 cols</td>
  </tr>
</table>

Ici, deux cellules inversées sur mobiles via table-header-group et table-footer-group :

<table class="w-full">
  <tr>
    <th class="w-1/2 sm:table-footer-group"></th>
    <th class="w-1/2 sm:table-header-group"></th>
  </tr>
</table>

Voir la démo sur Codepen.

Images

Généralités

  • Microsoft Outlook n'applique pas de margin ou padding sur les images. Il faut contourner le problème à l'aide d'éléments supplémentaires (balises ou cellules de tableau par exemple).
  • Renseigner l'attribut alt pour l'accessibilité mais également pour être affiché sur les clients mails qui refusent d'afficher les images par défaut.

Images fluides

Sur Outlook, lorsqu'une image est affichée à une taille différente de sa taille originelle, pour être fluide par exemple, il n'est pas possible de la dimensionner via CSS. L'attribut HTML width est obligatoire sinon l'image s'affichera toujours à la taille originelle.

Ceci ne fonctionne pas :

<img src="https://picsum.photos/600/600" style="width: 300px;" />

Ceci fonctionne :

<img src="https://picsum.photos/600/600" width="300" style="width: 300px; max-width: 100%;" />

Et la version Maizzle :

Ceci fonctionne :

<img src="https://picsum.photos/600/600" width="300" class="w-300; max-w-full;" />

Images de fond

Outlook, depuis la version 7, n'affiche plus les images de fond et nécessite un langage particulier pour les reconnaître (Vector Markup Language - VML).

Pour intégrer des images de fond, il est nécessaire de détecter Outlook (<outlook></outlook> dans Maizzle) puis de respecter une structure particulière (<v:rect>, <v:fill>, <v:textbox>, …) uniquement pour ce client mail.

D'autre part, le chemin absolu vers les images doit être précisé spécifiquement.

Plus d'information concernant ce sujet passionnant sur https://backgrounds.cm/

Listes à puces

Les listes à puces ne sont pas vraiment des Composants mais nécessitent des adaptations si particulières qu'elles méritent une documentation rien que pour elles.

  • Une liste à puce nécessite un code HTML sémantique pour être reconnue par des Assistances Techniques (pas de tableaux ou de bidouilles donc, mais des vrais <ul></ul> et <li></li>)
  • Gmail a des marges/paddings totalement différents des autres clients emails
  • Les versions récentes d'Outlook ne reconnaissent pas la moitié des propriétés de listes (dont list-style-type: none)
  • Les images pour remplacer les puces d'origine sont décalées verticalement sans solution possible

Au final, la solution la plus pérenne (et simple à adapter) est l'usage d'un pseudo-élément :before, reconnu sur la moitié des clients email, les autres afficheront une version dégradée en puce simple colorée.

Le code HTML et CSS pour styler les listes à puces est aussi élégant que ceci :

<!-- liste -->
<ul type="disc" class="list-disc pl-16">
  <!-- item de liste de puce couleur "pink" -->
  <li class="bullet text-pink" style="mso-list: bullet">
    <!-- contenu d'un item de liste -->
    <div class="ml-4 text-black">Contenu d'un item de liste</div>
    <!-- fin d'un item de liste -->
  </li>
</ul>
@list bullet {
  mso-level-number-format: bullet;
  mso-level-text: ●;
}
@media screen and (-webkit-min-device-pixel-ratio: 0) {
  .bullet {
    list-style-type: none;
  }
  .bullet:before {
    content: '\25CF';
    float: left;
    margin-left: -1.4em;
  }
  [class='x_bullet'] {
    list-style: initial !important;
  }
  [data-outlook-cycle] .bullet {
    list-style: initial !important;
  }
}

Google Fonts

Documentation Maizzle

  • La famille de police est déclarée sous forme de variable globale dans le template. Par exemple googleFonts: "Prompt:wght@200;400;700&display=swap"
  • Elle est importée dans le Layout master.html à l'aide d'un <link href="https://fonts.googleapis.com/css2?family={{ page.googleFonts }}" rel="stylesheet" media="screen" />
  • Elle devient également une classe utilitaire dans tailwind.config.js sous le nom de font-prompt par exemple.

Les polices de caractères "exotiques", importées, telles que Prompt sont mal reconnues des clients email. Le choix a été fait de proposer une famille de police classique "sans-serif" considérée comme "safe" sur tous les devices : "-apple-system", "Segoe UI", "Helvetiva", "Arial", "sans-serif".

Boutons

Outlook ne reconnaît pas les coins arrondis (border-radius) ni les margin et padding sur les éléments inline (même en les transformant en inline-block)

Voici comment réaliser un lien-bouton "simple" via Maizzle :

<a
  href="TODO:"
  class="inline-block py-16 px-24 text-sm leading-none no-underline text-white font-semibold rounded bg-indigo-500 hover:bg-indigo-600"
>
  <outlook>
    <i class="tracking-24" style="mso-font-width: -100%; mso-text-raise: 26pt;">&nbsp;</i>
  </outlook>
  <span style="mso-text-raise: 13pt;">Read more</span>
  <outlook>
    <i class="tracking-24" style="mso-font-width: -100%;">&nbsp;</i>
  </outlook>
</a>

Explications :

  • leading-none = line-height: 1;
  • tracking-24 = letter-spacing: 24;
  • <outlook></outlook> = <!--[if mso]><![endif]-->
  • L'élément <i> sert à contourner les margin/paddings manquants. Sa taille de police est réduite à zéro (mso-font-width: -100%;) et la largeur occupée est définie par tracking-24
  • mso-text-raise sert à rectifier la position verticale des éléments sur Outlook.

Pour un bouton plus complexe, nécessitant par exemple une hauteur précise ou un picto, une structure tabulaire sera nécessaire :

<table>
  <tr>
    <th class="bg-indigo-500 hover:bg-indigo-600 rounded" style="mso-padding-alt: 12px 48px;">
      <a href="TODO:" class="block text-white text-sm leading-full py-12 px-48 no-underline">Button</a>
    </th>
  </tr>
</table>

Et voici une version sous forme de Composant :

<!-- Composant Bouton (index.html) -->
<component
  src="src/components/button.html"
  locals='{
  "buttonURL": "TODO:",
  "buttonBg": "bg-green",
  "buttonColor": "text-black",
  "buttonIcon": "picto-03.png",
  "buttonIconAlt": "OK"
}'
>
  Créer un compte
</component>
<!-- components/button.html -->
<table>
  <tr>
    <th class="{{ buttonBg }} rounded-full" style="mso-padding-alt: 8px 12px">
      <a href="{{buttonURL}}" class="leading-full block py-8 px-12 text-12 font-bold {{ buttonColor }} no-underline">
        <if condition="buttonIcon">
          <img src="images/{{buttonIcon}}" width="16" alt="{{buttonIconAlt}" />
          <outlook>
            <i class="tracking-4" style="mso-font-width: -100%">&nbsp;</i>
          </outlook>
        </if>
        <span style="mso-line-height-rule: exactly; mso-text-raise: 4px">
          <content></content>
        </span>
      </a>
    </th>
  </tr>
</table>