Ce blog est encore en construction
Notes de la conférence The Other C in CSS
- Conférence : CSS Days, cssday.nl
- Speaker : Sara Soueidan
- Liens de la conférence : Video Youtube (en), slides
- Publié le : 26 Décembre 2024
Quelques mots sur la conférence et le talk
CSS Days est une conférence se déroulant à Amsterdam chaque année depuis 2013 et produisant une quinzaine de talks internationaux sur des sujets divers liés aux fonctionnalités avancées (et parfois très poussées) du CSS et du HTML. Les speakers et les sujets sont sélectionnés par les organisateurs (pas de CFP) et les videos sont toutes présentes sur Youtube.
Sara Soueidan est présentée comme une independant inclusive design engineer from Lebanon. C’est aussi une personnalité du web reconnue sur le sujet de l’accessibilité et l’inclusivité, ainsi qu’une conférencière expérimentée, à l’international et sur le sujet de l’UI Engineering depuis 2014. Elle anime aussi des keynotes et des workshops régulièrement et a récemment créé un cours intitulé Practical accessibility.
Cette conférence porte sur le CSS et l’accessibilité, et plus précisément sur comment le CSS peut affecter l’accessibilité des interfaces utilisateurs que l’on crée. Plus largement, c’est une conférence qui va montrer comment le CSS peut affecter la sémantique du HTML.
Introduction
Qu’on le veuille ou non, le CSS a des impacts sur plus que la présentation visuelle des éléments. Ses impacts descendent le long de la cascade CSS jusqu’au HTML et à la couche d’accessibilité.
L’arbre d’accessibilité
Comme pour le DOM, l’arbre d’accessibilité est un arbre composé d’objets, chacun représentant un élément de la page et contenant des informations relative à son accessibilité.
Contrairement à l’arbre du DOM, l’arbre d’accessibilité n’est pas restitué visuellement à l’utilisateur. Il est utilisé par les technologies d’assistance, afin de créer un espace de navigation pour leurs utilisateurs.
Il existe 4 types d’informations principales dans un objet de l’arbre d’accessibilité pour décrire un élément:
- son rôle, reflétant le type HTML (le plus souvent relié à un rôle ARIA) et indiquant comment l’élément va être utilisé. Tous les éléments n’ont pas de rôle, et par conséquent tous ne sont pas accessibles.
- son nom, indiquant comment identifier l’élément, et devant aussi aider à comprendre ce qu’il fait (exemple: un bouton s’intitulera valider mon panier mais pas clique moi).
- sa description optionnelle permettant de fournir des informations succinctes utiles (par exemple, des précisions sur le format attendus dans la saisie d’un champ). Il est possible de relier un champ à une description avec ARIA.
- son état, permettant pour les composants interactifs de conserver un état, par exemple est-ce qu’une case à cocher est cochée ou décochée. L’état change selon les interactions utilisateurs et ce changement doit lui être annoncé. Tous les composants interactifs natifs viennent avec des états que le navigateur met à jour et expose aux lecteurs d’écrans via l’arbre d’accessibilité.
Un objet de l’arbre d’accessibilité peut aussi contenir des propriétés liées à l’élément ou des informations sur ses relations avec d’autres éléments (est-ce qu’il fait partie d’un groupe, est-ce qu’il est labellisé par un autre élément etc.).
Toutes ces informations sont nécessaires pour que les technologies d’assistance puisse correctement retransmettre les informations à leurs utilisateurs.
Mais le CSS que l’on écrit peut avoir un impact sur les informations d’accessibilité exposées dans l’arbre d’accessibilité.
Comment le CSS peut affecter les informations d’accessibilité
Le CSS peut avoir un impact sur le role, le nom ou encore la description d’un élément. Il peut même avoir un impact sur le fait que l’élément soit exposé ou non dans l’arbre d’accessibilité.
Les propriétés qui impactent les rôles
Certaines propriétés peuvent avoir un effet direct sur le rôle d’un élément. A noter, ça n’est parfois relatif qu’à un navigateur, par exemple, certaines (anciennes) versions de Safari retirent la sémantique liste dans ce cas:
ul {
list-style: none;
}
L’impact est que les technologies d’assistance ne peuvent plus présenter cet élément comme étant une liste. Et donc permettre à l’utilisateur d’utiliser toutes les options associés (connaître le nombres d’éléments par exemple) ce qui peut contraindre sa navigation. Il ne peut pas sauter une liste très longue par exemple.
La raison présentée par un développeur de Webkit (NDLR: l’engine utilisé par Safari), est la recrudescence de listes sur le web ces dernières années, posant un soucis aux utilisateurs d’assistances. Son postulat est donc qu’une liste dont on a masqué le style de puce n’a pas à être plus comprise comme une liste pour un utilisateur utilisant une assistance que par les utilisateurs visuels.
Il reste néanmoins possible de restaurer la sémantique liste sur l’élément en y ajoutant l’attribut ARIA role="list", ou alors via des modifications CSS, comme cela:
/* Pour s'assurer de ne jamais l'oublier, cette déclaration va préserver la sémantique de la liste en garantissant que le style ne sera retiré que sur les éléments sur lesquels on a restauré le rôle (via l'ajout du rôle ARIA) */
ul:where([role="list"]) {
list-style: none;
}
/* Si on veut éviter le soucis initial, Safari ne modifiera pas la sémantique avec cette déclaration */
ul {
list-style-type: "";
}
Note: cette première partie montre l’importance de tester avec un lecteur d’écran nos applications, et ce sur plusieurs navigateurs. C’est d’autant plus important de s’y contraindre régulièrement que la montée en compétence n’est pas très complexe et qu’on ne sait jamais quand un navigateur (ou un outil d’assistance) peut changer son fonctionnement. Une habitude à prendre.
Quand le CSS retire la présence d’un élément dans l’arbre d’accessibilité
Il existe de nombreuses techniques pour masquer du contenu en CSS et elles peuvent avoir des implications sur l’exposition aux APIs d’accessibilités.
La capture ci-dessous les présente toutes (en anglais), mais on ne va rentrer dans le détails que de celles qui ont un impact.

Les deux propriétés suivantes retirent l’élément sur lequel elles s’appliquent de l’arbre d’accessibilité et le rendent donc inaccessible à l’ensemble des utilisateurs.
display: none
L’objectif de la propriété display est de contrôler le type de boite qu’un élément va générer (grid, flex, inline, …). La valeur none indique que aucune boîte ne doit être générée pour cet élément ni ses descendants.
display: contents; est similaire mais les descendants ne sont pas affectés. Par conséquent ils seront toujours disponibles dans l’arbre d’accessibilité. En tout cas c’est ce que la spécification prévoit. En réalité, cette propriété a de nombreux bugs depuis des années, notamment en terme d’accessibilité, ce qui l’a rendu inutilisable pendant des années. La plupart des navigateurs ont résolu ces soucis mais ils en restent encore, ce qui la rend difficilement recommandable, et uniquement sur des contenus sans importance.
.visually-hidden (et autres .sr-only)
La plupart des développeurs frontend connaissent et ont déjà utilisé des classes utilitaires du type .sr-only destinées à masquer visuellement du contenu, sauf aux lecteurs d’écrans. Sara préfère les appeler .visually-hidden parce qu’en fait cette déclaration ne porte pas l’information qu’aux lecteurs d’écrans mais à toute technologie d’assistance. Ces classes permettent de masquer visuellement du contenu tout en les laissant accessibles, ce que HTML et CSS ne permettent pas nativement. Elles fonctionnent en retirant l’élément du flux et les isolant en dehors de l’écran pour qu’il ne soit pas visible.
Elle insiste aussi sur la nécessite de ne pas appliquer ces déclarations sur les éléments :focus et :active, et en général sur les éléments interactifs, pour éviter que des éléments avec lesquels on interagisse ne soit invisibles. C’est notamment le cas des liens d’évitement destinés à facilement la navigation dans la page en permettant de sauter des blocs de contenus. Ceux-ci sont généralement rendus invisibles tant que l’utilisateur ne les a pas activés. Alors ils doivent devenir visibles.
masquer un élément pour le styliser, exemple des checkbox
On masque parfois aussi des éléments HTML pour pouvoir les styliser plus précisément que ce que permet le langage. Un exemple fréquent est celui des cases à cocher. Sara recommande de passer par un remplacement via des images en svg.
Par contre, les svg sont des éléments HTML qui peuvent, et doivent, être masqués via la propriété aria-hidden. Cette dernière fait disparaître l’élément de l’arbre d’accessibilité. Cela ne pose pas forcément de soucis car on va conserver la véritable case à cocher et la masquer via, par exemple, la classe utilitaire vue précédemment (en désactivant cette fois la visibilité au focus) ou en la déplaçant en dehors de l’écran. Mais cela a des effets sur la navigation tactile, sur les smartphones, où certains utilisateurs manipulent parfois l’interface un peu différemment. Pour eux, ils est nécessaire que l’élément HTML reste présent à l’endroit où l’utilisateur manipule l’interface.
La solution est la suivante: on va sortir l’élément du flux, le positionner en haut du svg, lui en donner les même dimensions puis enfin le cacher (via une opacity: 0). Cette technique est utilisable pour tous les éléments natifs que l’on souhaiterait masquer et remplacer par une image.
CSS peut influencer le nom d’un élément
Le nom accessible d’un élément résulte d’un algorithme utilisé par les navigateurs que l’on peut retrouver dans la spécification présente sur le site du W3C. En version simplifiée, voici ce que va faire le navigateur :
- En premier, il va chercher si l’élément est labellisé via un attribut
aria-labelledbyindiquant qu’un élément spécifique fournit un nom accessible pour lui. - Sinon, il va chercher la présence d’un attribut
aria-label - Enfin, et à moins que l’élément soit indiqué comme étant de présentation, via l’attribut
role="presentation ou none", il cherchera à récupérer le nom depuis :- un attribut HTML comme
altoutitle - un autre élément comme un
<label>ou une<legend> - son contenu si possible, par exemple le texte d’un bouton
- un attribut HTML comme
La plupart des éléments HTML peuvent avoir un nom d’accessible de plusieurs manières. Et si le navigateur en trouve plusieurs, il les prendra dans un certain ordre qui peut dépendre du type de l’élément en question. Tips: Pour les voir facilement, il est possible de les retrouver dans les options développeurs des navigateurs, par exemple dans l’onglet Elements puis Accessibilité sous Chrome.
Néanmoins la règle a suivre quand on fourni un nom accessible à un élément, c’est de ne pas suivre la spécification. Au contraire, on va :
- utiliser le contenu HTML (dans un lien, un bouton)
- sinon, utiliser un attribut ou une élément HTML (un alt, un
<label>) - sinon, utiliser un
aria-labelledby, - sinon, quand rien n’est possible autrement: utiliser un
aria-label
La règle est de n’utiliser ARIA qu’en dernier recours, la priorité est toujours de commencer par ce que propose le HTML.
le cas des pseudo-éléments :before et :after
Concernant le cas de pseudo-éléments :before et :after, il faut savoir que leur contenu doit, et sera, restitué par les lecteurs d’écrans dans le nom accessible. Attention donc aux emojis utilisés comme contenu. A noter qu’il va bientôt être possible de fournir 2 valeurs, dont la 2ème sera utilisé pour le nom accessible (et peut être laissé vide si le contenu est décoratif):
.info::before {
content: <valeur> / <valeur textuelle>
content: "ℹ️" / "Info:";
}
Néanmoins, il reste important de ne pas utiliser le CSS pour créer du contenu essentiel à la compréhension de la page. Et ce pour plusieurs raisons:
- le contenu CSS ne sera pas traduit par les outils automatisés
- ce contenu n’est disponible que quand le CSS est disponible, hors certains utilisateurs ou outils ne le chargent pas
- ce contenu n’est pas facilement personnalisable et peut devenir inaccessible dans le cas où les couleurs sont forcées
- si le contenu est généré via une image en CSS et que le lien devient invalide, l’alternative de l’image ne prendra pas la place du texte mais contribuera au nom accessible. Ce n’est pas accessible (le texte visible doit toujours être repris par le nom accessible, notamment pour les synthèses vocales).
En bref, si le contenu est essentiel à la compréhension de la page, ajoutez-le en HTML
CSS peut supprimer le nom d’un élément par effet de bord
Ce cas peut intervenir lorsqu’un élément donne le nom accessible d’un autre, et qu’il devient inaccessible.
Prenons l’exemple suivant:
<label for="recherche">Rechercher</label>
<input type="text" id="recherche" />
Souvent, on va chercher à masquer ce label qui n’est là que pour des raisons d’accessibilité. En y attribuant la règle display: none par exemple. Mais cela provoque son retrait de l’arbre d’accessibilité et par conséquent cela rend le nom accessible inopérant pour le champ auquel il était relié. Mais ce n’est pas forcément une mauvaise chose car d’après la spécification, le développeur peut explicitement surcharger cette information cachée et inclure du texte (caché) pour intégrer le nom accessible en utilisant les attributs aria-labelledby ou aria-describedby
<label for="search" id="searchLabel">
Rechercher
</label>
<input type="text" id="search" aria-labelledby="searchLabel"/>
CSS n’a pas d’impact sur l’état d’un élément
Les navigateurs fournissent des comportements interactifs pour les éléments interactifs. Ils mettent à jour puis exposent les états de ces composants lorsque les interactions utilisateurs agissent dessus. De cette façon, l’utilisateur sait qu’un état a changé en résultat de son action.
Mais en ce qui concerne les changements d’états non-natifs causés par l’apparition ou la disparition d’un élément lors d’une action, il est nécessaire de s’assurer que l’utilisateur à conscience des effets de ses actions. ((en) Article de blog de Adrian Roselli sur la non-accessibilité des widgets en CSS). Attention à l’engouement pour créer des composants en CSS, sans prendre en compte des contraintes d’accessibilité: utiliser uniquement les standards HTML et CSS ne veut pas dire être accessible. En particulier il convient d’être attentif aux patterns de navigation, qui peuvent être impossible à re-créer en CSS.
Certains composants comme les dialog étaient attendus de longue date mais sont désormais disponibles nativement. Il faut être vigilant à ne pas dénaturer leur comportement. De même, les popover disposent désormais d’une API permettant d’en faciliter la création mais aussi d’en décrire le comportement attendu. ((en) Article de blog de Hidde de Vries et Scott O’Hara sur l’accessibilité des popover).
Chercher à éviter l’utilisation du Javascript pour le comportement doit se faire en connaissance de cause, et en réfléchissant systématiquement aux barrières que cela peut engendrer. La bonne pratique préconisée par la conférencière est la règle de least power, à savoir le fait de choisir le langage le moins puissant (mais adapté) pour répondre à un objectif donné.