Display:contents n’est pas un reset CSS

Une traduction de Display : Contents Is Not a CSS Reset, par Adrian Roselli. Translated with permission.

Les resets CSS sont une collection de styles CSS qui défont le style par défaut du navigateur pour la plupart des éléments CSS.

Dernièrement j’ai vu des cas où des développeurs utilisaient display:contents sur des listes et des titres pour supprimer leurs marges et leurs rembourrages (margin et padding), et d’une façon générale faire visuellement ce qu’un reset CSS pourrait faire. En résumé, ils utilisent display:contents comme un reset CSS à deux sous.

C’est dangereux pour l’accessibilité.

Je vais vous expliquer pourquoi ci-dessous, parce que si j’ai dû faire des recherches pour m’en assurer, vous auriez plutôt intérêt à le lire.

Qu’est-ce que display:contents ?

Il vous serait utile de bien savoir de quoi nous sommes en train de parler. Vous pouvez aussi sauter cette partie si le sujet vous est déjà familier (c’est un peu long).

Vu par les développeurs

Dans sa forme la plus simple, display:contents existe pour supprimer visuellement le conteneur de l’élément (la « box ») et le remplacer par son contenu. Sur le principe, il traite l’élément comme si les balises ouvrante et fermante avaient été supprimées et que le contenu était resté tout nu sur la page.

Ce qui peut être très utile quand on l’applique à une page pleine de soupe de <div>s que vous voulez mettre en page avec les méthodes CSS grid ou flex. Ou peut-être que vous avez hérité d’une page en Bootstrap et de ses flots de <div>s, et que vous voulez faire de l’amélioration progressive avec une grille CSS.

Ire Aderinokun fournit une bonne synthèse de la question dans How display : contents ; Works, bien que les navigateurs ne fassent pas exactement ce qu’elle annonce.

Hidde de Vries l’explique aussi dans son article More accessible markup with display : contents. Son article, cependant, n’approche pas display:contents comme je l’ai vu se comporter.

D’après le W3C

La spécification CSS3 fournit aussi des indications :

L’élément lui-même ne génère pas de boîte, mais ses enfants et les pseudo-éléments génèrent toujours des boîtes et des flux textuels normaux. Pour pouvoir générer les boîtes et assurer la mise en page, l’élément doit être traité comme s’il avait été remplacé dans l’arbre des éléments par son contenu (y inclus ses enfants dans le code source et les pseudo-éléments, tels que ::before et ::after, qui sont générés avant/après les enfants de l’élément comme des éléments normaux).

Note : alors que seul l’arbre de la boîte est affecté, toute sémantique basée sur l’arbre du document, tels le filtrage par sélecteur, la gestion d’événements, et l’héritage de propriétés, n’est pas affectée.

Cette valeur se comporte comme display:none sur les éléments remplacés et autres éléments dont le rendu n’est pas entièrement contrôlé par CSS ; voir Appendice B : effets de display:contents sur des éléments inhabituels pour plus de détails.

[Note du traducteur : cette traduction n’est pas officielle, elle est faite uniquement pour les besoins de cet article et ne se substitue pas à une traduction française entérinée par le W3C le cas échéant.]

La liste des cas particuliers est peut-être encore plus intéressante :

<br>
<wbr>
<meter>
<progress>
<canvas>
<embed>
<object>
<audio>
<iframe>
<img>
<video>
<frame>
<frameset>
<input>
<textarea>
<select>

display: contents se comporte comme display: none.

<legend>

D’après HTML, une legend avec un display: contents n’est pas une légende rendue visuellement, elle n’a donc pas de capacité magique d’affichage. (Ainsi elle réagit à display: contents de façon normale.)

<button>
<details>
<fieldset>

Ces éléments n’ont pas de comportement particulier; display: contents supprime simplement leur conteneur principal, et leurs contenus sont rendus visuellement de façon normale.

tout autre élément HTML

Se comporte normalement pour display: contents.

Quelques implications pour l’accessibilité

De nos jours les navigateurs prennent chaque élément atteint par display:contents et le font disparaître de l’arbre d’accessibilité. Si vous avez lu l’article où je parle de remettre de la sémantique de tableau dans les éléments <table> avec ARIA (une fois les propriétés CSS d’affichage appliquées), hé bien, ici, ça ne marche pas. Même pas un petit peu.

Une démo que vous pouvez refaire chez vous

J’ai inséré un CodePen ci-dessous, mais il est plus facile de tester la version de débug qui n’inclut pas l’environnement CodePen.

See the Pen Table with display:contents by Adrian Roselli (@aardrian) on CodePen.

Exemple avec un lecteur d’écran

Je l’ai parcouru avec NVDA et Firefox pour vous le montrer en action. J’essaie de naviguer par table (T), par liste (L), par bouton (B), et par titre de niveau 2 (2). Aucun n’est reconnu. Il peut être intéressant de noter que chacun de ces éléments comporte un rôle ARIA en doublon de son rôle natif.

Utilisation de NVDA avec Firefox 59.02

Depuis que j’ai fait cette vidéo, j’ai modifié le CodePen pour inclure deux buttons, l’un avec un gestionnaire d’événement onkeypress et tabindex="0" pour montrer qu’il est mort pour un utilisateur au clavier. J’ai ajouté ce code parce que l’autre button est encore cliquable et déclenche bien un événement onclick.

L’arbre d’accessibilité

Pour vous détourner de l’idée que ce serait la faute des lecteurs d’écran, je peux vous assurer qu’aucune information de l’élément (y compris ARIA) n’atteint le lecteur d’écran. Ces saisies d’écran viennent de Chrome 66.

L’arbre d’accessibilité de Chrome montre un h2. L’arbre d’accessibilité de Chrome montre un h2 avec display contents.
L’arbre d’accessibilité de Chrome montre un <h2> dans son état normal, puis une fois le style display:contents appliqué. Il dit que l’élément est ignoré et que Le nœud d’accessibilité n’est pas exposé.
L’arbre d’accessibilité de Chrome montre une table. L’arbre d’accessibilité de Chrome montre une table avec display contents.
L’arbre d’accessibilité de Chrome montre une <table> dans son état normal, puis une fois le style display:contents appliqué. Il dit que l’élément est ignoré et que Le nœud d’accessibilité n’est pas exposé.
L’arbre d’accessibilité de Chrome montre une liste. L’arbre d’accessibilité de Chrome montre une liste avec display contents.
L’arbre d’accessibilité de Chrome montre une <ul> dans son état normal, puis une fois le style display:contents appliqué. Il dit que l’élément est ignoré et que Le nœud d’accessibilité n’est pas exposé.
L’arbre d’accessibilité de Chrome montre un bouton. L’arbre d’accessibilité de Chrome montre un bouton avec display contents.
L’arbre d’accessibilité de Chrome montre un <button> dans son état normal, puis une fois le style display:contents appliqué. Il dit que l’élément est ignoré et que Le nœud d’accessibilité n’est pas exposé.

Des bugs dans la spec et dans les navigateurs

Dix jours avant mes tests, Hidde de Vries avait déjà ouvert des bugs pour les navigateurs sur la base de leur usage de display:contents dans les mises en page en grilles :

  • Firefox bug 1455357: Setting grid item to display:contents resets its accessible role
  • Chromium Issue 835455: Element not exposed to accessibility tree when it is set to display: contents
  • Safari bug 39630240 (que je ne peux pas voir parce que mon Apple ID n’a peut-être pas les bonnes permissions)

Après une discussion sur Twitter ce matin avec Ilya Streltsyn, il a pris l’initiative de faire remonter la question au groupe de travail CSS :

  • CSSWG #2632: [css-display][css-aam][selectors-4] How elements with `display:contents` get focused?

Des tweets

Je ne pense pas être le premier à remarquer display;contents utilisé comme reset CSS, mais mes tweets de ce matin ont l’air d’avoir surpris un certain nombre de personnes, et j’en profite pour glaner de l’information sur ces problèmes.

[Note de traduction : j’ai laissé les messages en anglais comme vous le constaterez. Ils abondent dans le sens de l’auteur]

En résumé

Pour l’instant veuillez n’utiliser display:contents que si vous prévoyez de le tester avec les technologies d’assistance et que vous pouvez confirmer que le résultat fonctionne pour les utilisateurs.

Ceci est une traduction de Display : Contents Is Not a CSS Reset, par Adrian Roselli. Translated with permission.

Commentaires

Qui êtes-vous ?
Votre message

Pour créer des paragraphes, laissez simplement des lignes vides. Tous les liens sortants comporteront un attribut rel="nofollow". Merci de ne pas spammer.