Amélioration progressive, orthogonalité et accessibilité (1/2)

Amélioration progressive, orthogonalité et accessibilité (1/2) (le 16/05/2015)

J’ai eu l’immense plaisir de réaliser un site pour le Haut-Commissariat des Droits de l’Homme, ce site s’intitule « People with albinism, not ghosts but human beings ».

People with Albinism, amélioration progressive et accessibilité

Je vous propose deux billets montrant quelques astuces sympathiques mêlant accessibilité, amélioration progressive, orthogonalité et intégration CSS. En voici le premier.

Un burger-icon avec de l’ARIA et des transitions CSS

Le site comporte un « burger-icon » qui apparait à partir de la version tablette. Grosso modo (je vous passe tous les détails), c’est un lien qui pointe vers une ancre (avant que JavaScript ne s’en mêle :) ). Un détail cependant : il doit faire apparaitre le menu de navigation avec une transition CSS. Donc, en quelque sorte, cela devient un bouton avec JavaScript.

En se basant sur ARIA comme descripteur d’interface, c’est extrêmement simple, naturel et intuitif. Via jQuery, cela nous donne à l’initialisation :

$('#displaymenu').attr({
'aria-controls' : 'navigation-container',
'aria-expanded' : 'false',
'role' : 'button'
});
// ce qui donne
<a id="displaymenu" href="#navigation" aria-controls="navigation-container" aria-expanded="false" role="button">

aria-controls indique qu’il contrôle le conteneur ayant l’id navigation-container, et aria-expanded indiquera si le menu est ouvert ou non.

Ensuite, il faudra mettre aria-hidden sur #navigation-container, qui indiquera que le menu est fermé. À partir de ces états, on a tout ce qu’il faut pour poser les styles nécessaires à la transition CSS.

Au début, mon idée était de faire une transition sur height : c’est somme tout logique, la hauteur varie de height: 0 (état fermé) à height: auto. Sauf que… cela n’est pas possible, on ne peut pas animer ainsi, il faut indiquer une valeur numérique (dura lex sed lex).

La solution est alors d’animer la propriété max-height, elle variera de max-height: 0 (état fermé) à max-height: <une valeur suffisante mais pas trop> pour que l’animation soit complète mais pas trop longue.

.navigation-container {
/* ici les préfixes ! */
transition: max-height 1s ease;
}
.navigation-container[aria-hidden="true"] {
max-height: 0; /* caché */
}
.navigation-container[aria-hidden="false"] {
max-height: 25em; /* ouvert */
}

Comme vous pouvez le voir, les styles pour effectuer la transition sont d’une simplicité à l’épreuve des balles, la transition et les deux états (ouvert/fermé).

Maintenant, il ne reste plus qu’à créer le code pour transformer le lien vers une ancre en un bouton. Si l’on reprend le comportement du bouton, il doit s’activer :

  • quand on clique dessus ;
  • quand on tape entrée dessus ;
  • et quand on tape espace dessus.

Avec jQuery, l’événement click est déclenché quand on clique dessus, et comme la balise est un lien, quand on tape entrée. Il faudra donc que j’écoute quand la touche espace sera pressée sur l’élément #displaymenu.

Autre point de détail, les liens du menu restent focusables même s’ils sont pris dans un conteneur avec max-height: 0 (il faudrait que le conteneur soit en display: none pour qu’ils ne soient plus focusables). Autrement dit, les liens peuvent être atteints au clavier quand le menu est fermé, ce qui n’est pas très logique.

Deux solutions :

  • ajouter un style comme display: none une fois la transition terminée (ce que je fais sur mon carrousel accessible) ;
  • ou rendre les liens non focusables via tabindex="-1".

La seconde solution étant très simple à faire et particulièrement adaptée à mon cas, c’est celle que j’ai retenue. Ce qui nous donne :

// si l’on frappe une touche clavier 
// ou si l’on clique sur le #displaymenu
$('#displaymenu').on( "click keydown", function( event ) {
var $this = $(this),
$navigation_container = $('.navigation-container'), // le conteneur de la navigation
$navigation_links = $('.navigation__link'); // les liens de navigation

// soit un click = clic ou entrée
// soit une touche = keycode 32 => espace
if ( event.type==='click' || ( event.type==='keydown' && event.keyCode == 32 ) ) {

// si le menu est fermé
if ($this.attr('aria-expanded') === 'false') {
// on l’ouvre
$this.attr('aria-expanded', 'true');
// navigation ouverte
$navigation_container.attr('aria-hidden', 'false');
// on vire les tabindex pour les liens
$navigation_links.removeAttr('tabindex');
}
else {
// on ferme
$this.attr('aria-expanded', 'false');
$navigation_container.attr('aria-hidden', 'true');
// liens non focusables
$navigation_links.attr('tabindex', '-1');
}

// on évite la propagation dans ces cas uniquement
event.preventDefault();
}

});

Et voilà comment un lien se transforme en bouton. Bien entendu, ce n’est pas parfait, c’est sûrement améliorable. J’aurais pu utiliser la balise button qui m’aurait évité de faire tout cela : cette balise inclut par défaut les événements que j’ai dû recoder en jQuery. Autrement dit avec un button, pour chopper le clic, la touche entrée ou la touche espace, cela se fait juste ainsi :

$('button').on( "click", function( event ) {

Et pas besoin d’ajouter role="button". Là, vous venez sûrement de comprendre pourquoi j’ai utilisé cette balise dans mon script hide/show (collapsible regions). Avec la sémantique, beaucoup moins d’efforts !

Voilà, nous sommes à la fin de ce premier billet. Le second est disponible : Amélioration progressive, capacités et CSS (2/2).

Permalien :

Flux RSS des commentaires de ce billet : https://www.nicolas-hoffmann.net/rss/commentaires.php?id_news=1664

1 commentaire

Posté par Marc le magnifique le 17/05/2015 à 9:44:53
J'ai aussi été confronté à l'impossibilité de faire la transition de height de 0 vers auto et je trouve que ta solution d'utiliser max-height est très ingénieuse. (sourire)

Ajouter un commentaire









L'option « Se souvenir de mes informations » utilise un cookie, elle ne sera pas effective si vous les avez désactivés.

Les balises HTML ne seront pas interprétées, il est donc inutile d'en mettre. Par contre, les sauts de lignes de votre commentaire seront pris en compte, ne mettez donc pas de <br />, le site s'en chargera. Bien sûr, un commentaire vide ne sera pas ajouté !

L'auteur (autrement dit moi) n'est pas responsable des éventuelles fautes d'orthographe dans les commentaires.
Tout propos raciste et/ou insultant sera supprimé sans préavis. Les commentaires hors de propos destinés à faire de la pub pour des sites seront également supprimés sans ménagement.

Je vous prie de me pardonner, j'ai énormément de mal à lire le "langage" SMS, il n'est donc pas du tout interdit de s'abstenir de l'utiliser. Qui plus est, vous avez sûrement un clavier digne de ce nom et pas celui d'un téléphone portable. Ne vous gênez pas pour utiliser l'option "Prévisualiser" si vous voulez vous relire avant de poster, je vous en remercie d'avance !

Cet article a été écrit par Nicolas Hoffmann.

Ce site est la propriété de Nicolas Hoffmann.
Tous droits réservés, les textes du blog sont publiés sous licence CC BY-NC-SA.