Vous avez un beau projet web, mais les obligations (morales et légales) d'accessibilité ont du mal à cohabiter avec la conception graphique, et parfois techniques.
Normalement, vous avez été contactés par au moins une société « bienveillante » qui vous propose la solution magique pour, entre énormes guillemets, « rendre votre site accessible grâce à notre solution miracle ». Sauf que oui… mais non.
Parlons des plus ou moins fameux « outils de mise en conformité d'accessibilité ».
Plutôt que de reprendre tout ce qui a déjà été dit et re-dit, lire l'état des lieux fait par Karl Groves.
Pour résumer : ça ne fonctionne que rarement, voir pas du tout. Et surtout, ça ne rend pas un site conforme ! Au mieux, ça va changer des choses visuellement, quand ça ne casse pas tout. En aucun cas ça ne corrigera les erreurs de code, pas plus qu'une mauvaise sémantique et encore moins les interactions claviers attendues.
Aucun produit de superposition sur le marché ne peut entraîner la conformité complète d'un site Web avec toute norme d'accessibilité existante et ne peut donc pas éliminer les risques juridiques.
Bref, c'est une fumisterie purement commerciale qui surfe sur la méconnaissance de beaucoup d'acteurs du web sur ce qu'est vraiment l'accessibilité. Ce qui se revendique comme solution d'inclusion numérique est le plus souvent une solution d'exclusion.
Exemple : sur la première image, le site orignal sans option activée, la présentation est correcte.
Sur la deuxième image, l'option « malvoyance » est activée. Résultat, les textes sont blancs sur fond blanc,
tout devient illisible et la mise en page explose.
Finalement, la surcouche produit exactement l'effet inverse de ce qu'elle est supposée faire.
Une chose marrante à faire un jour de pluie, c'est d'aller regarder la liste des « clients » sur le
site de n'importe quel fournisseur de surcouche et de regarder si l'option est toujours présente.
C'est parlant et instructif…
Et encore plus drôle, c'est quand un fournisseur de surcouche ne l'implémente pas (ou plus) sur son propre site ! Ou bien
qu'il ne fonctionne pas.
J'ai des noms… 😉
Ou un exemple…
- Sur la vue n°1, on voit que tout est conforme.
- Sur la vue n°2, après avoir activé le filtre « Osthéoartrite », on perd le contraste sur le lien situé à gauche. Et ses confrères dans la page.
- Sur la vue n°3, la taille de texte n'est pas configurée, tout s'affiche correctement.
- Sur la vue n°4, en sélectionnant une taille à 17 (sans que l'on sache 17 quoi…), magie ! Le titre du bloc est plus petit. Et le reste du texte ne bouge pas.
Un grand merci de démontrer que les surcouches ne fonctionnent pas, même sur le site d'un fournisseur.
On a du mal à imaginer comment ils puissent le faire sur le vôtre.
Il n'en reste pas moins que proposer des options, manuelles ou automatiques, est un bon choix
pour améliorer
l'expérience utilisateur.
Pourquoi ? Tout simplement parce qu'il y a des personnes qui en ont besoin, mais aussi pour permettre de
concevoir des interfaces avec des effets visuels longuement réfléchis par les Mozart du design. Il faut bien
reconnaitre qu'une page web avec des alphas d'arrière-plan, des couleurs moins tranchées, etc. ça convient à
la majorité des utilisateurs. Mais n'oublions pas les autres !
Oui, il y a des gens qui zooment leur écran ou qui configurent leur navigateur pour afficher une taille minimale de texte, d'autres qui sont dyslexiques, encore d'autres qui ont besoin d'un contraste plus fort, certains qui ne supportent pas tout ce qui bouge sans l'avoir demandé, etc. etc.
Comment faire ?
L'alternative ? Coder ! Qui mieux que ceux qui conçoivent et codent une interface utilisateur peut connaitre le besoin ? Vous.
Attention : le but de cet article n'est pas de fournir une solution clé en main, car la recette magique
applicable
sur tous les environnements n'existe pas.
Mais, honnêtement, ce n'est vraiment pas compliqué !
Ça passe par trois choses :
- Des variables de style (CSS) ;
- Des requêtes média (media queries) ;
- Une couche légère de Javascript
Ah non ! Une quatrième chose : vouloir bien faire côté code… On parle de « Qualité », tout simplement. Et sans qualité de code, on ne peut pas faire d'accessibilité.
La conception CSS
Comme dit précédemment, l'utilisation des variables de CSS est une solution magique.
Comment ça fonctionne ? tout est là.
Dans un premier temps, il faut définir la totalité des couleurs « par défaut », les polices utilisées, les hauteurs de lignes, etc.
Exemple avec la feuille de styles de Kortic :
:root {
/* Requis : light-dark() et les couleurs système se résolvent selon ce schéma */
color-scheme: dark light;
font-size: max(100%, 13px);
--font-stretch: 100%;
--initial-font: system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, sans-serif;
--size-body: 75rem;
&.full-viewport {
--size-body: min(100%, 160rem);
}
--size-body-largest: calc(75rem* 2);
@media (max-width: 47.9375rem) {
--font-size: 0.875rem;
}
@media (min-width: 48rem) and (max-width: 119.9375rem) {
--font-size: clamp(1.0625rem, calc(1.0313rem + 0.1dvw), 1.1875rem);
}
@media (min-width: 120rem) {
--font-size: 1.25rem;
--size-body: 90rem;
}
&:not(.contrast) {
--font-weight: 270;
@media (prefers-color-scheme: light) {
--font-weight: 320;
}
}
&.contrast {
--font-weight: 400;
}
@media (max-width: 47.9375rem) {
--gutter: 2.5rem;
}
@media (min-width: 48rem) {
--gutter: 3rem;
}
/* Couleurs système, suivent color-scheme */
/* --canvas et --canvasText sont définis plus bas */
--buttonFace: ButtonFace;
--buttonText: ButtonText;
--field: Field;
--fieldText: FieldText;
--highlight: Highlight;
--highlightText: HighlightText;
--selectedItem: SelectedItem;
--selectedItemText: SelectedItemText;
--mark: Mark;
--markText: MarkText;
--accentColor: AccentColor;
--accentColorText: AccentColorText;
/* Sans paire */
--buttonBorder: ButtonBorder;
--grayText: GrayText;
--linkText: LinkText;
--visitedText: VisitedText;
--activeText: ActiveText;
/* Fin couleurs système */
--cols: 12;
--radius: var(--small-gutter, 0.625rem);
--font-weight-headers: unset;
--lh: 1.618; /* φ phi */
--font-family: var(--base-font);
/* Palette fixe (identique clair et sombre) */
--darkblue: rgb(0,36,94);
--lightblue: rgb(184,207,241);
--lightgreen: rgb(212,249,201);
--orange: rgb(193,71,5);
--orangered: rgb(255,69,0);
--raspberry: rgb(205,0,55);
--red: rgb(213,43,30);
--yellow: rgb(255,182,18);
--maroon: rgb(154,90,12);
--antigua: rgb(130,196,206);
--lightgray: rgb(211,211,211);
--whitesmoke: rgb(252,252,252);
--teal: rgb(0,122,124);
/* Palette adaptative : light-dark(clair, sombre) */
--blue: light-dark(#2F6EBE, rgb(138,180,249));
--applegreen: light-dark(#5A820C, rgb(130,190,0));
--green: light-dark(#237A37, yellowgreen);
--limegreen: light-dark(#D2E100, rgb(255,253,0));
--carbon: light-dark(#6F7174, rgb(137,139,143));
--basic-color: light-dark(black, white);
--basic-color-reverse: light-dark(white, black);
--basic-color-alpha: light-dark( rgba(0, 0, 0, 0.8), rgba(255, 255, 255, 0.8));
--basic-color-reverse-alpha: light-dark( rgba(255, 255, 255, 0.8), rgba(0, 0, 0, 0.6));
--text-decoration-style: solid;
--text-decoration-color: currentColor;
--text-decoration-thickness: 0.0625em;
--small-radius: calc(var(--radius) / 2);
--font-family-monospace: Consolas, Courier, monospace;
--sticky-top: var(--header-outer-height);
--negative-gutter: calc(var(--gutter) / 2 * -1);
--half-gutter: calc(var(--gutter) / 2);
--small-gutter: calc(var(--gutter) / 4);
--tiny-gutter: calc(var(--gutter) / 8);
--double-gutter: calc(var(--gutter) * 2);
--negative-half-gutter: calc(var(--negative-gutter) / 2);
--negative-small-gutter: calc(var(--negative-gutter) / 4);
/* Surfaces et textes */
--canvas: light-dark(#f3f4f7, rgb(20,20,30)); /* sombre : rgb(16,16,23) en alternative */
--canvasText: light-dark(#56626F, rgb(197,213,231));
--background-color-secondary: light-dark(white, rgb(26,26,41)); /* sombre : rgb(26,26,33) en alternative */
--background-color-hero: light-dark( rgba(0, 0, 0, 0.05), rgba(0, 0, 0, 0.6));
--background-color-menus: light-dark(#fff, rgb(37,37,37));
--background-error-color: rgb(246,197,237);
--background-forms-elements: light-dark(#fcfcfc, rgb(44,46,54));
--heading-color: light-dark(#111, white);
/* Ombres */
--box-shadow: light-dark(#DBDBDB, rgba(42, 34, 90, 0.81)); /* sombre : #34316Fa0 en alternative */
--box-shadow-light: light-dark(#DBDBDB, rgb(49,54,65));
--box-shadow-forms-elements: light-dark(var(--box-shadow), rgba(255, 255, 255, 0.3));
--under-box-shadow-color: light-dark( rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.9));
--under-box-shadow: 0 0.4375rem 0.3125rem -0.375rem var(--under-box-shadow-color);
/* Liens et états */
--link-color: light-dark(#0050b3, rgb(102,170,255)); /* alternatives : rgb(4,76,159) rgb(166,48,220) / rgb(205,189,248) */
--link-color-alternate: light-dark(#0032FF, rgb(102,133,255)); /* sombre : rgb(149,113,255) en alternative */
--button-radius: 0.4375rem;
--error-color: light-dark(var(--raspberry), rgb(255,169,174));
--message-dialog-error: var(--basic-color-reverse);
--highlight-sentence: light-dark(#6336de, var(--antigua));
/* Code */
--code-area-background: light-dark(rgba(99, 54, 222, 0.05), rgb(21,13,31));
--code-area-foreground: var(--canvasText);
--code-area-border: rgb(104,68,148);
&.admin {
--size-body: var(--size-body-largest);
}
@media (any-hover: none) {
&:not([class*="dys"]) {
--base-font: var(--initial-font);
--font-family-headers: var(--initial-font);
}
}
@media (any-hover: hover) {
&:not([class*="dys"]) {
--base-font: "Roboto Flex", var(--initial-font);
--font-family-headers: "Roboto Flex", var(--initial-font);
}
}
&.dyslexic {
--base-font: 'Accessible DfA', var(--initial-font);
--font-family-headers: 'Accessible DfA', var(--initial-font);
.no-dyslexic {
font-family: var(--initial-font);
}
}
&.dyslexic-comic {
--base-font: 'Comic Sans MS', var(--initial-font);
--font-family-headers: 'Comic Sans MS', var(--initial-font);
}
&.initial-font {
--initial-font: "";
--base-font: var(--initial-font);
--font-family-headers: var(--initial-font);
}
&[class*="dys"] .no-dyslexic {
font-family: var(--initial-font);
}
/* Focus */
--outline-offset: 0.1875rem;
--outline-width: 0.125rem;
--outline-style: solid;
--outline-color: light-dark(var(--orangered), springgreen);
&.use-keyboard {
--outline: var(--outline-width) var(--outline-style) var(--outline-color);
}
&:not(.use-keyboard) {
--outline: none;
}
/* Seules les valeurs non colorimétriques restent ici : light-dark() n'accepte que des couleurs */
@media (prefers-color-scheme: dark) {
--under-box-shadow: 0 0.4375rem 0.1875rem -0.3125rem var(--under-box-shadow-color);
}
@media (max-width: 47.9375rem) {
--size-icon-header: calc(var(--header-outer-height) * .35);
@media (max-height: 47.9375rem) {
--header-outer-height: 2.8125rem;
}
@media (min-height: 48rem) {
--header-outer-height: 3.125rem;
}
}
@media (min-width: 48rem) and (max-width: 89.9375rem) {
--header-outer-height: 3.4375rem;
--size-icon-header: calc(var(--header-outer-height) * .3);
}
@media (min-width: 90rem) {
--size-icon-header: calc(var(--header-outer-height) * .35);
@media (max-height: 47.9375rem) {
--header-outer-height: 4.375rem;
}
@media (min-height: 48rem) {
--header-outer-height: 5rem;
}
}
}
.initial-font {
font-family: var(--initial-font);
}
.font-family-code {
font-family: var(--font-family-monospace) !important;
}
Dans ce code, on peut voir que c'est le :root qui est majoritairement utilisé. Question de « poids » et de scope.
Ensuite, on trouve des sélecteurs du type :root.contrast qui vont être utilisés pour effectuer les variations. La définition des variables de couleurs est configurée par défaut sur le mode « light » car il s'agit encore du mode d'affichage par défaut de la majorité des utilisateurs.
La prise en charge du mode sombre est effectuée avec la requête media @media (prefers-color-scheme: dark).
Pour les deux types d'affichage, nous pouvons redéfinir de nouvelles valeurs qui seront appliquées via une
classe CSS posée sur le :root par une petite fonction Javascript. Regarder sur le blog.
Par exemple, :root.contrast, :root.dyslexic,
:root.dyslexic-comic, etc.
Ensuite, ces classes CSS vont pouvoir être activées et désactivées par du Javascript.
Les requêtes média
Comme pour les variables CSS, tout est là.
Pour ce qui concerne ce site, les plus utilisées sont :
- prefers-reduced-motion ;
- prefers-contrast ;
- prefers-color-scheme ;
- prefers-reduced-transparency, mais qui n'est pas encore compatible avec tous les navigateurs.
En supplément de la détection par CSS, on peut ajouter la détection programmatique.
Exemple : window.matchMedia("(prefers-contrast:more)").
En haut de cette page, il y a un bouton qui ouvre un menu contenant des options d'affichage. Toute la mécanique se passe dans ce menu. Si vous lisez cet article, c'est que vous avez déjà mis les mains dans du code, donc cette partie ne doit pas vous être inconnue.
Le principe est simple (mais pensez à ouvrir la console du navigateur pour regarder le contenu du menu et ce qui se passe sur la balise <html>).
A chaque activation ou désactivation des options, l'attribut value de l'input est appliquée comme classe CSS ou retirée du :root. De cette manière, toutes les options sont compatibles entre elles.
La donnée est enregistrée en sessionStorage ou localStorage pour être récupérée au
chargement des pages et réactiver les input concernés.
C'est tout !
Application
Le mieux est encore de voir en direct l'application de l'idée.
Et ça fonctionne sur l'ensemble des pages de Kortic.
-
Votre terminal est configuré en mode sombre.
Changez de mode pour voir le résultat. -
Le mode sombre n'est pas activé sur votre terminal.
Vous devriez essayer, c'est très joli, sain pour les yeux et écologique… - Les animations sont désactivées selon votre configuration.
- Le mode contrasté est activé sur votre terminal.
- La transparence est réduite sur votre terminal.
- Au passage, désactivez les animations et testez !
Bonne pratique à retenir
Dans tous les cas, il est préférable de gérer les préférences des utilisateurs plutôt que d'imposer un point de vue (souvent discutable).
Si je configure mon terminal pour limiter les animations, il est logique qu'on ne m'en impose pas. C'est par
exemple le cas
pour le menu de ce site qui disparait (sauf sur mobile) et réapparait selon qu'on monte ou descende dans la
page. Ça ne s'applique que si les animations sont autorisées.
Même chose pour le mode sombre, par exemple. Pourquoi
livrer une interface « blanche » si le terminal est configuré en mode sombre ?
A vous de jouer !