Bart Simpson qui lit une lettre "Chère Edna, je veux voir davantage de ta personne. Que penses-tu d'un autre rendez-vous secret? Vôtre, Principal Skinner."

Depuis l’avènement de React, il est facile de prendre des raccourcis lorsque l’on code. L’accès à cet univers de nouvelles fonctionnalités n’est pas sans risque, et on a tendance à vite oublier nos solutions natives HTML, CSS, pourtant accessibles, présentes, et moins nocives.

La nocivité de React

Utiliser du JavaScript pour chaque composant un peu complexe implique devoir implémenter soi-même toutes les notions d’accessibilité. Pour un bouton, simple d’apparence, beaucoup d’éléments rentrent en compte: il doit être cliquable bien sûr, mais également pouvoir se désactiver, expliquer ce qu’il se passe lorsque l’on clique, être navigable au clavier, …

De la même manière, un simple élément « Voir davantage », qui affiche des détails lorsque l’on clique dessus, doit respecter ses propres critères. Chose que, bien sûr, vous implémentez-vous même à chaque fois… Non?

Le cœur du problème

Un problème…

Attaquons le problème. Voici une implémentation assez classique d’un élément permettant d’afficher davantage d’informations sur un élément.

import { useState } from "react";

export default function App() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <div>
      <button onClick={() => setIsOpen((v) => !v)}>
        {isOpen ? "v" : ">"} Qu'est-ce qui est jaune et qui attend ?
      </button>
      {isOpen && "Jonathan."}
    </div>
  );
}

… Voire désavantage ?

Fonctionnel et navigable au clavier, cet élément a pourtant bien des soucis. Il fonctionnera parfaitement pour une personne totalement apte, mais ne possède aucun avantage d’accessibilité.

Un élément intégré manuellement a pour désavantages:

  • Un lecteur d’écran ne sait pas ce que fait ce bouton. Aucune indication de rôle, de ce qu’il permet de faire.
  • Puisqu’on ne sait pas ce que fait ce bouton, on n’a aucun indicateur de si l’état est « ouvert » ou « fermé », ce qui le rend difficile d’utilisation pour certains utilisateurs.
  • De plus, cet élément intègre conditionnellement certains nœuds de la page dans l’HTML, ce qui est déconcertant pour un lecteur d’écran. Un meilleur élément aurait en permanence le texte dans l’HTML, bien que caché visuellement.
  • Cette solution ne profite pas des différentes mises à jour et intégrations par chaque navigateur qui, bien que rares, peuvent survenir.

Mais, si tous ces soucis sont là, comment les régler? En utilisant des éléments natifs.

Le cœur de la solution

De la bonne intégration d’un élément natif

La réponse dans ce cas, ça sera d’utiliser ce qui existe déjà. L’élément de divulgation des détails. Il s’agit d’un élément HTML natif, qui implémente déjà l’entièreté de notre solution, de son accessibilité, de sa navigation clavier, et qui répond aux différents critères pour rendre le site navigable par tous.

export default function App() {
  return (
    <details>
      <summary>Qu'est-ce qui est jaune et qui attend ?</summary>
      Jonathan.
    </details>
  );
}

Voir davantage d’avantages

Cet élément présente tous les avantages auxquels on peut s’attendre en utilisant un élément déjà fait par des gens déjà qualifiés, dans un contexte qu’ils maîtrisent.

Utiliser l’élément details, summary a pour avantages:

  • Un lecteur d’écran connaît cet élément, et peut travailler avec ;
  • Puisque le lecteur d’écran connaît l’élément, il sait qu’il existe un état « ouvert » et « fermé » ;
  • L’élément est navigable au clavier ;
  • Le contenu de cet élément n’est pas conditionnellement intégré à l’HTML ;
  • Si des mise à jour sont faites par les navigateurs, elles seront directement ajoutées à cet élément ;
  • Des fonctionnalités CSS natives sont présentes.

Voir davantage

Avec un peu de CSS, on se rend compte qu’on n’a pas du tout besoin de Javascript pour cet élément, grâce aux sélecteurs d’attributs « open » et « close ». En effet, puisque le navigateur sait que details peut être ouvert ou fermé, on a désormais accès à tout ce qui en découle.

Ainsi, une solution entièrement native serait:

<details>
  <summary>Qu'est-ce qui est jaune et qui attend ?</summary>
  <p>Jonathan.</p>
</details>

En utilisant intelligemment le sélecteur d’attribut [open], on styliser différemment l’état ouvert, par rapport à l’état fermé.

details {
  margin: 10px 0;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 4px;
  background-color: #f9f9f9;
}

summary {
  cursor: pointer;
  font-weight: bold;
  padding: 5px;
  outline: none;
}

summary:hover {
  color: #007bff;
}

details[open] summary {
  color: #ff5733;
}

details p {
  margin: 10px 0 0;
  padding: 5px;
  font-size: 14px;
}

Mais je n’aime que Tailwind!

En bref, avec quelques petits changements dans nos habitudes de programmation, on peut créer des éléments bien plus qualitatifs. Navigation au clavier, accessibilité, style CSS, vous ne verrez plus React du même œil.

Par ailleurs, tous ces sélecteurs sont disponibles directement en TailwindCSS, si vous êtes aficionado de cette manière de faire.

L’exact code précédent peut se faire ainsi:

<details class="border border-gray-300 rounded-md bg-gray-100 p-4 [&>summary]:open:text-orange-500">
  <summary class="cursor-pointer font-bold p-2 outline-none hover:text-blue-500">
    Click me to expand
  </summary>
  <p class="mt-2 p-2 text-sm">
    Details content goes here.
  </p>
</details>

Sources