L’accessibilité dans le développement web : 8 erreurs à ne plus commettre

L’accessibilité dans le développement web : 8 erreurs à ne plus commettre (1/3)

Alexandre a divisé cet article sur l’accessibilité web en trois parties. Dans ce premier volet, il présente des erreurs courantes : mauvaise utilisation des balises et hiérarchie des titres. Il aborde aussi la gestion des images et la différence entre liens et éléments cliquables.

La deuxième partie, bientôt publiée, portera sur la navigation au clavier, ARIA, rôles et outils adaptés.

Enfin, la troisième partie proposera un cas pratique sur l’accessibilité d’un menu déroulant (dropdown).

L’accessibilité web permet aux systèmes de s’adapter pour être utilisés par tous. Elle concerne aussi bien les sites que les applications web. Elle joue un rôle clé dès la phase de conception et de développement.

C’est l’un des objectifs majeurs du langage HTML. Ce langage sémantique sert à structurer et à donner du sens au contenu. Le CSS, lui, gère la présentation visuelle. JavaScript ajoute l’interactivité.

Mais aujourd’hui, le web devient de plus en plus interactif. Les frameworks JavaScript modernes et le développement orienté composants se généralisent. Résultat : l’utilisation sémantique du HTML recule. Et cela nuit directement à l’accessibilité web.

Note : Le paradigme du développement en composant consiste à développer sous forme de briques plus ou moins atomiques, isolées et offrant une réutilisation, des tests et une maintenance plus faciles.

On réduit souvent l’accessibilité web à l’usage par des personnes en situation de handicap (par exemple, les personnes malvoyantes). En réalité, elle va bien plus loin.

L’accessibilité concerne tous les utilisateurs. Elle a aussi un impact direct sur l’expérience utilisateur (UX) et le référencement naturel (SEO).

Dans cet article, nous passons en revue 8 erreurs fréquentes qui nuisent à l’accessibilité web. Et nous vous proposons des solutions simples pour y remédier.

Note : Certains exemples ci-dessous utiliserons les frameworks tailwind et Angular

1. Utiliser les bonnes balises

Si nous revenons plus en détail sur HTML, comme dit en introduction, c’est un langage de balisage sémantique. Cela signifie que des balises ordonnent le contenu selon ce qu’elles contiennent.

Cependant, une mauvaise pratique maintenant trop répandue est la surutilisation de la balise `<div>`.

En effet, seulement 2 balises HTML n’ont pas de sens sémantique : `<div>` et `<span>`. Elles servent principalement à regrouper des éléments entre eux afin d’y appliquer du style.

				
					
<div>
  <div>Mes films préférés</div>
  <div>
    <div *ngFor="let movie of movies">
      <div>
        <img [src]="movie.imageUrl" [alt]="movie.imageAcss" />
        <div>{{movie.title}}</div>
      </div>
      <div>
        <div>{{movie.summary}}</div>
      </div>
      <div>Rating : <span>{{movie.rating}}</span></div>
    </div>
  </div>
</div>


				
			

Le code ci-dessus remplit son usage, à savoir afficher la liste des films favoris d’un utilisateur. Cependant, un lecteur d’écran ou un robot d’indexation ne perçoit ni la structure ni la logique des éléments. Il restitue le contenu tel quel, sans en comprendre le sens.


Pourtant, nous pouvons utiliser une multitude de balises, par exemple :

  • `<nav>` : définit une zone contenant des éléments de navigation, utilisée par exemple dans le menu ou le breadcrumb d’une application
  • `<main>` : déclare le contenu principal. Doit être présent une seule fois par page.
  • `<section>` : déclare une zone ayant le même contenu thématique, souvent suivit par un titre.
  • `<article>` : définit un contenu indépendant de la page
  • `<header>` : déclare la partie introductive d’une page ou d’un élément. Par exemple on parle de header d’une application contenant les liens principaux, ou du header d’un article (voir exemples)
  • `<footer>` : contrairement au header, il sert à marquer la fin d’une page ou d’un élément
  •  `<aside>` : définit un contenu sans rapport direct avec le contenu principal (main, section), comme des compléments d’information ou les légendes
  • `<ol>/<ul> <li>` : déclare une liste (ordonnée/désordonnée) et chaque élément de cette liste
  • `<hr>` : déclare un changement de thématique entre des sections ou des paragraphes, souvent représenté par un trait fin de séparation

Nous pouvons transformer l’exemple précédent pour le rendre plus logique et accessible sur le web :

				
					
<section>
  <h2>Mes films préférés</h2>
  <ul>
    <li *ngFor="let movie of movies">
      <article>
        <header>
          <img [src]="movie.src" [alt]="movie.alt" />
          <h3>{{movie.title}}</h3>
        </header>
        <p>{{movie.summary}}</p>
        <footer>Rating : <span>{{movie.rating}}</span></footer>
      </article>
    </li>
  </ul>
</section>


				
			

Nous avons donc une section de la page contenant un titre de niveau 2. Cette section contient une liste désordonnée d’articles composés d’un header (contenant une image et un titre de niveau 3), d’un paragraphe et d’un footer.

Ainsi, sans parler du contenu en tant que tel (title, summary, …), nous pouvons comprendre la structure et le sens de chaque élément.

2. Les niveaux des titres doivent être ordonnés

Abordons maintenant un sujet qui paraît évident une fois énoncé, mais qui pose davantage de difficultés en pratique, notamment avec le paradigme du développement en composant (voir Introduction).

En HTML, les titres (h1, h2, …, h6) représentent les niveaux de titre. Donc un titre de niveau 3 (h3) devra toujours se trouver à la suite d’un titre de niveau 2 (h2). Si l’on compare avec une présentation orale point par point, il n’y aurait pas de sens à aborder le III) après le I), et de l’entamer par un C). C’est le même principe ici.

Ainsi, si nous reprenons l’exemple précédent (films préférés), c’est bien le cas avec notre `<h3>` étant un enfant (même indirect) d’un `<h2>`.

Cependant, si l’on extrait le bloc de présentation d’un film (`<article>`) pour en faire un composant réutilisable, un problème se pose. Le `<h3>` à l’intérieur ne correspondra pas toujours au bon niveau de titre, car il dépendra de la structure dans laquelle le composant est intégré.

Ce manque de flexibilité nuit à l’accessibilité web, mais aussi à l’expérience du développeur.

Pour éviter cela, il faut permettre au développeur d’indiquer manuellement le niveau de titre. Cela peut se faire via un paramètre ou une zone de contenu libre (ex : ng-content).

3. Image de contenu vs image décorative

Les images sont aujourd’hui essentielles dans toute application web. Nous pouvons les diviser en deux catégories opposées : images de contenu et images décoratives. Les images de contenu transmettent une information importante aux utilisateurs. Les images décoratives sont plutôt liées au design et au marketing. Ainsi, l’une est donc obligatoire, l’autre plutôt facultative.

Cette question doit se poser pour chacune lorsque l’on parle d’accessibilité web, notamment pour les lecteurs d’écran mais aussi pour le SEO.

Pour les images dites de contenu, il faut alors utiliser l’attribut `alt` de la balise `<img>` en fournissant une description pertinente de l’image. Par exemple, “Image du film” reste un contenu peu informatif, que “Affiche du film Joker” remplace de manière plus précise et utile. Le personnage du Joker se tient debout dans un escalier entre 2 immeubles”.

Pour les images décoratives, il faut enlever ou laisser à vide l’attribut `alt` et utiliser l’attribut `aria-hidden=”true”`. Il est aussi possible d’afficher l’image avec la propriété CSS `background-image`, qui ne convient pas aux images de contenu, puisque l’image ne fait alors plus partie du contenu HTML, mais du style CSS.

Note : Lorsqu’une image ne peut être chargée (connexion lente, mauvaise URL…), le texte défini dans l’attribut alt est alors affiché à sa place par le navigateur.

4. Un lien n'est pas un bouton

Note : L’ouverture automatique d’un lien dans un nouvel onglet est également une mauvaise pratique d’accessibilité web et d’UX, mais expose également à des failles de sécurité.

Les liens sont définis par la balise `<a>` et embarquent plusieurs fonctionnalités natives d’un navigateur (ouverture dans un nouvel onglet par exemple).

Ils permettent ainsi d’être interprétés par un lecteur d’écran pour décréter que la prochaine action est relative à la navigation.

Cependant, de plus en plus de liens utilisent d’autres balises HTML via JavaScript. Cela impacte négativement tous les utilisateurs.

Le clic sur un `<button>` entrainant une navigation via du code javascript est donc une mauvaise pratique au niveau de l’expérience utilisateur (UX). En effet, il n’a pas la possibilité d’ouvrir ce lien dans un nouvel onglet par exemple. Si cette mise en place vise à effectuer divers contrôles, ces contrôles doivent alors se faire en amont.

Attention, on ne peut pas désactiver un lien d’un point de vue sémantique.

Le seul cas où la redirection via javascript peut être “tolérée” est à la suite d’un traitement asynchrone (échange de données avec une API, lors de la connection par exemple), si cela était explicite pour l’utilisateur.

5. Les éléments cliquables ne sont pas toujours des boutons

L’évènement javascript `onclick` permet de réagir à l’action de click d’un utilisateur sur un élément cliquable (le plus souvent un bouton, hors cas spécifiques).

Cependant, la balise `<button>` est décrite par les standards HTML comme devant contenir uniquement des éléments dits “inline”, donc un contenu principalement textuel (texte, `<span>`, `<i>`, `<b>`, …).

Note : cette règle ne s’applique pas aux liens (`<a>`) qui peuvent contenir de larges et divers contenus.

Cependant, dans des applications web au design plus moderne, le besoin de zones cliquables ayant un contenu plus structuré est apparu. On voit alors soit apparaitre des boutons ayant de large contenu, mais surtout des évènements de click sur des balises non cliquables (`div`, `article`, …).

Cela a un impact sur l’accessibilité et l’expérience utilisateur car ces éléments ne sont alors pas interprétés par les navigateurs (et les lecteurs d’écran) comme cliquable. On peut seulement interagir avec eux grâce à un click de la souris (voir l’article suivant).

Pour y remédier, un élément structurant que l’on souhaite rendre cliquable doit posséder les attributs `role=”button”` ainsi que `tabindex=0`, permettant respectivement qu’il soit interprété en tant que bouton et pouvant recevoir le **focus** via l’utilisation de la navigation au sein de la page en utilisant la tabulation. Enfin, une gestion de l’évènement `keydown`, `keyup` ou `keypress` est nécessaire afin de reproduire les comportements natifs d’un bouton, comme par exemple son activation en utilisant la touche *Entrer*.

Note : utiliser le `role=”button”` ne rend pas l’élément désactivable (disabled). Il faut donc gérer ce cas manuellement en retirant les attributs.

Cette utilisation répétée des attributs et évènements décrits peut être mutualisé et pourrait par exemple être faite par une directive Angular :

				
					@Directive({
  selector: '[myClickableElement]',
  standalone: true,
})
export class MyClickableElementDirective {
  @HostBinding('attr.tabindex') tabIndex = 0;
  @HostBinding('attr.role') role = 'button';

  constructor(private element: ElementRef<HTMLElement>) {}

  @HostListener('keypress', ['$event']) manageKeyPress(event: KeyboardEvent): void {
    if (event.key === 'Enter' && !event.ctrlKey) {
      this.element.nativeElement.click();
    }

    // Default space event is scroll, except on buttons
    if (event.code === 'Space') {
      event.preventDefault();
      event.stopImmediatePropagation();
    }
  }

  @HostListener('keyup', ['$event']) manageKeyUp(event: KeyboardEvent): void {
    if (event.code === 'Space') {
      this.element.nativeElement.click();
    }
  }
}


				
			

Attention : si vous utilisez un linter d’accessibilité web, l’utilisation d’une directive sera tout de même interprétée comme une erreur puisque le parser ne comprendra pas l’application dynamique du `role`, `tabindex` et `keydown`

Fin de cette première partie, la suite sera disponible très prochainement.

Image de Alexandre PICARD

Alexandre PICARD

Practice Leader Product Development à Toulouse
& Tech Lead Angular

Partager l'article

Partager l'article

Sommaire

À lire aussi

Management participatif-2
Lire l'article
ia-min
Lire l'article
Notions d'écologie numérique
Lire l'article