J’étais très fier de mon histoire de tiroir… jusqu’à ce que la tendinite qui annonce le bouclage me force à lâcher mon trackball. L’astuce généralement employée pour concevoir un menu « hamburger » en code CSS consiste à utiliser une case à cocher. Son libellé — en l’occurrence ☰
— est visible, et peut donc être manipulé avec le pointeur, mais la case elle-même est masquée, et ne peut donc pas être manipulée avec le clavier :
<input hidden id="menu-opener" type="checkbox" />
<label for="menu-opener" role="button">☰</label>
L’approche plus traditionnelle est plus immédiatement accessible, puisqu’elle utilise un simple bouton, mais demande de régler les interactions avec quelques lignes de code JavaScript. Le script « attend » le clic sur le bouton pour déclencher la fonction menu_toggle()
, qui relève la valeur de l’attribut menu-state
pour savoir si le menu est ouvert ou fermé, et donc s’il doit être fermé ou ouvert :
var menu = document.getElementById("menu");
var menu_button = document.getElementById("menu-button");
function menu_open() {
menu.setAttribute("menu-state", "opened");
}
function menu_close() {
menu.setAttribute("menu-state", "closed");
}
function menu_toggle() {
var menu_state = menu.getAttribute("menu-state");
if (menu_state == "closed") {
menu_open();
} else {
menu_close();
}
}
menu_button.addEventListener("click", function () {
menu_toggle();
}, false);
Ces changements favorisent l’amélioration progressive du code CSS en fonction des capacités du navigateur. Sur tous les navigateurs de la dernière décennie, la valeur de l’attribut menu-state
détermine l’apparition du menu :
.menu {
/* Par défaut, le menu est fermé et donc caché. */
position: absolute;
transform: translateY(125%);
visibility: hidden;
}
.menu[menu-state="opened"] {
/* Le menu est ouvert et donc affiché. */
position: fixed;
bottom: 4em;
left: 0;
right: 0;
top: 0;
transform: translateY(0);
visibility: inherit;
}
Sur les navigateurs plus récents, le sélecteur :has()
et le sélecteur de voisin général ~
permettent de voiler et flouter l’arrière-plan lorsque le menu est ouvert :
.site-header .overlay {
/* Par défaut, le voile est invisible. */
opacity: 0;
}
.site-header:has(.menu[menu-state="opened"]) .overlay {
/* Lorsque le menu est ouvert,
le voile est visible. */
background: var(--bg-overlay);
position: fixed;
bottom: 0;
left: 0;
right: 0;
top: 0;
opacity: 1;
z-index: 1;
}
.site-header:has(.menu[menu-state="opened"]) ~ .site-main {
/* Lorsque le menu est ouvert,
les éléments voisins sont floutés. */
filter: blur(5px);
}
L’API Pop Up, qui vient tout juste d’être incorporée à Google Chrome, promet d’incorporer ces mécanismes au navigateur. Le progrès a (parfois) du bon. Quitte à devoir écrire du code JavaScript, j’ai ajouté quelques lignes supplémentaires pour encore simplifier la navigation au clavier. Une pression sur la touche M
permet d’ouvrir le menu, et une pression sur la touche ⎋
de le fermer, voilà pourquoi j’avais prévu des fonctions menu_open()
et menu_close()
plutôt qu’une seule fonction menu_toggle()
:
document.addEventListener('keydown', function(event) {
if (event.code === "KeyM") {
menu_open();
}
if (event.key === "Escape") {
menu_close();
}
}, false);
J’ai profité de l’occasion pour personnaliser l’apparence de la mise en évidence :focus
, en reprenant l’astuce de Patrick Lauke, qui permet de distinguer les navigateurs prenant uniquement en charge :focus
(qui peut apparaitre lors des manipulations avec le pointeur) des navigateurs prenant aussi en charge :focus-visible
(qui apparait uniquement lors des manipulations avec le clavier) :
*:focus {
/* La mise en évidence par défaut… */
outline: 2px solid var(--bg-primary);
outline-offset: 2px;
}
*:focus:not(:focus-visible) {
/* …est annulée dans les navigateurs
prenant en charge :focus-visible… */
outline: none;
outline-offset: 0;
}
*:focus-visible {
/* …et réservée aux manipulations
avec le clavier. */
outline: 2px solid var(--bg-primary);
outline-offset: 2px;
}
Le contour est parfois coupé quand les dimensions des éléments ne sont pas explicites. La solution n’est pas très satisfaisante, mais est parfaitement fonctionnelle :
*:focus-visible {
position: relative;
}
Le menu de navigation n’est plus très loin de ce que j’avais en tête, je vais maintenant pouvoir me concentrer sur la mise en page des articles.