Aller au contenu principal
divagations - Retour à l'accueil

Repasse CSS sur les notes

Guillaume Barbier

Temps de lecture : ~ 11 minutes

J'ai fait une petite repasse sur les notes, à la base pour ajuster leur positionnement et uniformiser un peu leur apparence.

Mais tout ne s'est pas passé comme prévu.

Corrections basiques

J'ai commencé par uniformiser mes notes en ajoutant un pictogramme à chacune d'entre-elle, y compris la version "générique" (le début d'une belle galère). J'ai aussi ajouté un petit correctif pour éviter le décrochage de leur "titre" (suite à ma refonte du CSS, ce paragraphe s'était retrouvé avec une largeur maximale provoquant un décroché disgracieux sur desktop).

Avertissement

Des "faux titres" pour les notes

J'ai choisi de ne pas donner à mes notes des en-têtes (balises <hx>) afin de ne pas polluer la hiérarchie de titres de ma page : si un contenu est suffisamment important pour remonter dans les grands titres de la page, il n'a rien à faire dans les notes (il lui faut alors sa propre section de contenu).

À la place, j'associe ce petit paragraphe au bloc entier via un aria-labelledby (ce qui marche[1] car le bloc porte un role="note").

Pour corriger ce décrochage, il faut contraindre la note sur la largeur maximale de son contenu. Je pourrais tout simplement utiliser width: fit-content; mais le problème est alors que si je contribue une note très courte (une seule ligne), alors celle-ci "décroche" dans l'autre sens et se retrouve moins large que le texte environnant :

.admonition {
  padding: 16px;
  max-width: calc(32px + 40em); /* j’additionne le padding 
    de la note et la largeur max des paragraphes */
  @media screen and (min-width: 1024px){
    margin-inline: -16px; /* j'ajoute ce décrochage sur 
      les grands écrans pour éviter que les notes se décalent 
      par rapport à la colonne de texte (ce n'es pas 
      un problème sur mobile où tout le contenu est contrainte 
      par la largeur de l'appareil) */
  }
  .admonition-title {
    max-width: unset; /* supprimer la longueur maximale sur 
      ce paragraphe pour s'assurer que la bande de couleur 
      aille bien jusqu'au bout de la boite. */
  }
}

À ce stade-là, je suis assez content de moi et je peux me concentrer sur le type de notes que je veux pouvoir gérer.

Types de notes

En comptant la note ci-dessus de type "avertissement léger" (warning, en anglais), j'ai prévu pour l'instant quatre types de notes :

  • Notes génériques,
  • Astuces,
  • Remarque / avertissement léger,
  • Point d'attention fort, crucial

Pour chacune de ces notes, j'ai prévu une apparence qui reflète le ton de l'information à transmettre.

Exemples de notes

Information

Ceci est un exemple de note générique

C'est mon style par défaut, avec un émoji "information".

Astuce

Ceci est un exemple d'astuce

Ce type de note est plutôt utilisé pour donner une recommandation, un conseil non contraignant.

Avertissement

Ceci est un exemple de remarque / avertissement léger

Le type de note parfait pour mettre en avant une information importante, qu'il vaut mieux ne pas ignorer.

Attention !

Ceci est un exemple de point d'attention fort, crucial

Ce type de note sert à signaler une information cruciale, vitale voir urgent.

À utiliser avec parcimonie pour éviter de surcharger la page : si toutes les informations sont "prioritaires", aucune ne l'est.

La couleur du bloc "avertissement léger"

Sur tous les autres blocs, j'ai utilisé la couleur de texte (d'avant-plan, ou foreground, en anglais) afin que ses bordures fines se détachent bien. Mais pour ce bloc, cela m'a posé problème : il est impossible d'obtenir un jaune suffisamment contrasté sur du blanc sans complètement dénaturer la couleur (le mieux que j'ai pu obtenir est une sorte d'ocre). J'ai donc décidé de faire fi de la perception des bordures (graphiquement, le traitement spécial des notes passe aussi par des marges supplémentaires).

J'applique donc un beau jaune or, et pour m'assurer qu'il ne s'inverse pas en dark mode, j'applique la couleur directement, sans passer par une custom property.

Galère et emojis

Une solution imparfaite

Les émoji sont la solution rapide que j'ai trouvé pour ajouter de l'information rapidement et sans efforts de design (les emojis sont restitués par les lecteurs vocaux et m'épargnent d'avoir à dessiner des pictogrammes).

Mais cette solution a deux défauts :

  • Accessibilité : Il n'existe pas d'emoji "Avertissement" ou "Astuce". À la place, il faut que je choisisse un emoji que mes lecteurs et lectrices associeront au ton de chaque note, qu'ils soient vu (représentation graphique) ou restitués (nom de l'emoji)…
  • Instabilité graphique : l'apparence des émoji changent selon les OS et les polices de caractères utilisés, et nous n'avons aucun contrôle dessus.

Mes notes et leurs emojis ont subis beaucoup d'itérations pour tenter de pallier à ces deux défauts, en suivant ces deux principes :

  • Utiliser des emojis dont le sens soit proche/communément associé au type de note dans lequel je l'utilise (j'expliquerai plus loin comment ce fut un grossier échec)
  • Garantir que les emojis restent distinguables quelque soit le système (les emojis que j'ai utilisés m'ont réservé quelques surprises)

Information

Exemple : Affichage de l'emoji "panneau avertissement"

Lorsque j'affiche ma note "avertissement léger" sur Mac OS, le symbole "panneau avertissement" est affiché en filaire noir et se détache très bien sur mon fond jaune. Mais sur iOS, l'emoji s'affiche sous la forme d'une image de panneau jaune, impossible à distinguer sur mon fond jaune.

Accessibilité : Sécuriser la restitution

J'ai essayé de choisir des emojis dont le sens soit proche du ton de chaque note :

  • Une ampoule électrique (💡) pour mes astuces
  • Un panneau "avertissement" (⚠️) pour mes avertissement légers
  • Un point d'exclamation rouge (❗) pour mes point d'attention prioritaires

Mais pour mes notes génériques, j'ai rencontré un conflit d'intérêt :

  • l'émoji le plus proche en terme de sens est l'emoji "source d'information" (U+2139 – ℹ️)
  • Sauf que, sur Mac OS et iOS, je le trouve moche (à cause de la "boîte" avec fond dégradé que lui appose le système)

Du coup, à la place je suis allé chercher un emoji un peu plus obscur : Circled Latin Small Letter i (U+24D8)(Opens in a new window)

Et là, patatra ! Sur iOS, l'emoji était restitué dans une autre langue (sans que je l'ai demandé) et la restitution était tellement déformée que je n'arrivais même pas à identifier les mots. Sur Mac OS, la restitution est encore différente : là, l'OS se contentais de basculer en anglais et je comprenais à peu près les termes… mais c'était affreusement long et assez peu parlant. 😞

Pas le choix, soit j'acceptais un emoji qui ne me convenait pas, soit il fallait explicitement fournir une alternative texte à mes emojis… J'ai choisi la solution de l'alternative texte, plus pertinente, même s'il devenait alors impossible de conserver la structure HTML simpliste fournie par mon extension markdown (markdown-it-admon(Opens in a new window)) :

<div aria-labelledby="adm-1-note" role="note" class="admonition tips">
  <p class="admonition-title" id="adm-1-note">Titre de l'astuce</p>
  <p>Contenu de l'astuce</p>
</div>

J'ai donc du transformer le code généré (en essayant d'ajouter plutôt que de retirer) par l'extension pour ajouter une alternative textuelle et masquer mon emoji aux lecteurs d'écrans.

<div aria-labelledby="adm-2-note" role="note" class="admonition generic">
  <div id="adm-2-note" class="admonition-title-wrapper">
    <p class="admonition-type-marker">
      <span aria-hidden="true" class="admonition-type-icon"></span>
      <span class="visually-hidden">Information</span>
    </p>
    <p class="admonition-title">Titre de la note générique</p>
  </div>
  <p>Contenu de la note.</p>
</div>

J'ai choisi de placer mon alternative textuelle dans le texte puis de la masquer visuellement (plutôt que de la glisser dans un attribut par dessus l'emoji). Grâce à cette approche, quand le CSS est désactivé (par exemple en activant un mode "lecteur" dans le navigateur), l'information est toujours retranscrite :

  • L'emoji est masqué (car je continue à l'intégrer via le CSS, plus souple)
  • sans CSS, l'alternative textuelle est affichée à la place.

Gérer l'instabilité graphique des emojis

Pour gérer l'inconsistance des emojis sur les différents systèmes, j'ai décidé de créer une "zone de sécurité" autour de mes émojis, avec le même fond que la page.

Petit problème : le fond de la page change en mode sombre et le contraste obtenu n'était clairement pas suffisant pour distinguer certains d'entre eux… et ce juste sur les systèmes testés (Mac OS et iOS).

Information

Garantir le contraste des emojis, hypothèse de travail

Je n'ai pas du tout vérifié ce que j'avance, mais j'ai l'impression que sur tous les OS que j'ai pu voir (pas juste Mac OS et iOS), il n'a absolument pas été prévu d'inverser leur affichage en fonction du schéma de couleur affiché (clair ou sombre).

Si c'est bien le cas, je peux assez raisonnablement partir du principe que tous les emojis sont conçus (même sur les systèmes que je ne connais pas) pour fonctionner sur un fond blanc (c'est après tout le schéma de couleur en vigueur lorsqu'on a commencé à implémenter les emojis avec Unicode… enfin, je suppose).

Pour m'assurer que mes émojis soient toujours affichés sur un fond blanc, j'au donc évité d'utiliser les custom properties et j'ai appliqué directement la valeur hexadécimale des fonds de base (celle utilisée pour les fonds en mode "clair").

Positionner texte et emoji

L'avantage d'avoir du réécrire le HTML de mes notes, c'est que j'ai pu en profiter pour "mettre à la poubelle" tous les bricolages successif que j'ai pu faire pour essayer de positionner correctement texte et emoji afin de :

  • imposer une marge entre l'emoji et le texte
  • s'assurer que si mon titre revient à la ligne (ce qui est fréquent sur mobile), il ne reviennent pas sous l'emoji et sa zone de sécurité (surtout si c'est pour "mordre" dessus)
  • garder un texte correctement espacé, même quand il est mis en forme avec du gras ou de l'italique

Grâce aux conteneurs que j'ai ajouté pour "enrober" titre et emoji, j'ai pu tout simplement appliquer un display: flex; sur le conteneur et avoir une disposition fluide de l'ensemble.

Avertissement

Ne mettez pas display: flex; sur un paragraphe !

C'est un truc dont je me suis rendu compte durant mes bricolages : tant que le paragraphe ne contient que du texte simple, sans mise en forme, la disposition flex ne pose aucun problème, l'espacement entre les mots est respecté. Mais dès qu'on ajoute des balises (comme <strong> ou <em>), la disposition flex s'applique à celles-ci et leur contenu vient se coller (ou au contraire s'écarte fortement, tout dépends des paramètre appliqués à la disposition), rompant complètement la lisibilité du texte.

Donc pour éviter d'avoir à réparer "à la main" les espacements de vos mots mis en avant, ne mettez pas de flex sur un paragraphe !

En isolant l'emoji dans son propre conteneur, il est aussi plus facile de styler sa zone de "sécurité" et de le positionner dans celle-ci.

Réflexions : Utiliser l'unité ch ou non ?

Durant mes errances autour de l'emoji et de son positionnement, j'ai expérimenté avec l'unité ch. C'est une unité popularisé par certains "reset" CSS modernes proposant de restreindre la longueur des paragraphes à un certain nombres de caractères (genre 70-75 caractères), pour une meilleur lisibilité. C'est comme ça en tout cas que j'ai découvert l'unité.

1ch correspond à "une largeur de 1 caractère" ou plus précisément à la largeur du caractère "0", comme l'explique cet article d'Eric Meyer What is the css ch unit ?(S'ouvre dans un nouvelle fenêtre))

C'est en lisant cet article que je me suis rendu compte que cette approche populaire est incorrecte et non-fiable.

Incorrecte car un paragraphe avec une longueur max de 75ch ne garanti pas qu'une ligne n'affichera que 75 caractères, seulement que le paragraphe peut contenir 75 "0", sans nécessiter un retour à la ligne (en général le paragraphe contiendra plus de caractères sur une seule ligne, le "0" étant souvent un des caractères les plus larges d'une police de caractère).

Non-fiable car si je change la police de caractère de deux paragraphes dimensionnés avec cette unité, il est tout à fait possible que ces deux paragraphes n'aient pas la même longueur (tout dépend de la largeur des "0" pour ces polices).

Pour garantir la constance de la longueur de mes paragraphes, j'ai donc remplacé cette longueur dans les fondations de mon CSS par le bien plus fiable em (j'ai choisi em plutôt que rem car pour une règle générique, je préférais qu'elle s'adapte à la taille de mon texte… à voir si c'est une bonne idée ou non)

p, li, figcaption {
  max-width: 40em;
}

  1. Le support de note est encore imparfait : VoiceOver iOS ne le restitue simplement pas. Sur macOS, il est énoncé par VoiceOver avec de légères variations selon le navigateur (Chrome et Safari énonce le paragraphe en arrivant sur le bloc, puis ajoute une mention "Remarque" pour rappeler le rôle de la région. Firefox fait la même chose, en doublant l'énoncé – je ne sais pas trop pourquoi – avant de conclure par "note" pour rappeler le rôle). Je n'ai pas testé NVDA (optimiste, je me dis que NVDA sera plus rigoureux que VoiceOver iOS) ↩︎