métro[zen]dodo est en chantier ! Suivez la progression des travaux.

Une histoire à tiroirs

J’aimerais pouvoir dire que la simplicité du menu de MacGeneration représente notre approche du journalisme technologique, et que l’absence totale de navigation sur le Club iGen est un triomphe de minimalisme. En vérité, je n’ai jamais été foutu de construire un menu de navigation vaguement présentable. Notre incapacité à embrasser la complexité de notre système d’information et organiser la taxonomie de nos contenus n’aide certainement pas, mais elle n’explique pas mes difficultés à concevoir le menu d’un « simple » blog.

Je crois que cela tient à mon approche architectonique de la structure d’une page web, en partant depuis l’enpied, comme une lourde fondation sur laquelle repose le reste de l’édifice. Le temps d’arriver au toit, je suis impatient d’en finir, alors je jette mes dernières forces avec deux coups de truelle. Sauf que ce qui pourrait passer inaperçu depuis le pied d’un immeuble de quarante étages est immanquable dans les quarante premiers pixels d’un écran tenu à dix centimètres du nez.

Alors cette fois, je commence par l’entête, que je conçois comme une charpente, pour filer la métaphore. On n’a jamais bâti une charpente avec trois cure-dents, mais on a fabriqué quelques cathédrales avec des forêts impénétrables. Qu’il prenne toute la hauteur des écrans verticaux, ou toute la largeur des écrans horizontaux, le menu de navigation doit déborder de liens, comme autant de poutres et de poteaux. La structure de l’entête est fort traditionnelle :

<div class="site-header">
  <div class="content">
    <h1 class="title"></h1>
    
    <input data-menu hidden id="menu-opener" type="checkbox" />
    <label aria-controls="menu" aria-haspopup="true" aria-label="Ouvrir le menu" class="menu-button" for="menu-opener" id="menu-label" role="button"></label>
  </div>
	
  <div class="menu"></div>
</div>

Le menu de navigation s’ouvre comme un tiroir, une interaction qui ne demande pas la moindre ligne de JavaScript, mais peut être entièrement réalisée avec une animation CSS. L’entête est ancré en bas de la fenêtre avec position: fixed;, une position atypique que je veux réutiliser depuis l’abandon des premières maquettes du Club iGen. Le menu est lui-même attaché à l’entête, mais caché sous le tapis avec transform: translateY(125%);1 :

.site-header .content {
  background: purple;
  display: flex;
    align-items: center;
    justify-content: space-between;
  height: 4em;
  padding: 1em 1em 1em 1em;
  position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
  z-index: 3;
}

.site-header .menu .navigation {
  position: fixed;
    bottom: 4em;
    left: 0;
    right: 0;
  transform: translateY(125%);
  z-index: 2;
}

Le sélecteur :has() simplifie considérablement la rédaction de la mécanique d’ouverture, déclenchée en cochant la case qui porte l’attribut data-menu. Le menu pointe son nez hors du tapis (transform: translateY(0);) à l’aide d’une jolie animation :

.site-header:has([data-menu]:checked) .menu .navigation {
  transform: translateY(0);
  transition: transform 0.5s cubic-bezier(0.5, 0.0, 0.25, 1);
}

Ces trois lignes de code sont plus importantes qu’il n’y parait : avant l’apparition du sélecteur :has(), il n’était pas possible de changer l’apparence d’un enfant (.menu .navigation) en fonction du comportement de son parent (.site-header). Ajoutons le sélecteur de voisin général pour appliquer un flou sur le contenu principal (l’un des voisins de l’entête au sein du <body>) lorsque la case est cochée dans l’entête (:has([data-menu]:checked)) :

.site-header:has([data-menu]:checked) ~ .site-main {
  filter: blur(5px);
  transition: filter 0.5s cubic-bezier(0.5, 0.0, 0.25, 1);
}

Cette déclaration parsemée de caractères cryptiques devrait suffire à convaincre les développeurs les plus paresseux qu’ils devraient vraiment étudier les dernières évolutions de mon langage de programmation favori. Ou, disons, apprendre à centrer un élément sur les deux axes sans importer un framework de quelques dizaines de milliers de lignes de code. This Ain’t Your Father’s CSS.

L’entête et le menu sur un petit écran vertical.

Seuls les navigateurs les plus récents prennent en charge le sélecteur :has(), mais les navigateurs les plus anciens conservent le bénéfice de l’élément translucide qui assombrit légèrement l’arrière-plan :

.site-header:has([data-menu]:checked) .menu .overlay {
  background: hsla(285, 10%, 5%, 75%);
  position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    top: 0;
  z-index: 1;
}

Sur les écrans horizontaux, l’entête passe à la verticale, une disposition qui constitue maintenant une signature personnelle. Le menu s’ouvre vers la droite, une disposition qui permet d’apprécier toute l’astuce de la paire de boutons de fermeture :

<div class="menu">
  <label class="overlay" for="menu-opener"></label>
  
  <div class="navigation">
    <label class="close" for="menu-opener">Fermer</label>
    
    <div class="sections"></div>
  </div>
</div>

Le premier bouton, directement attaché au volet de navigation, offre une affordance familière, celle du simple lien de fermeture. Le deuxième est en fait cet élément translucide qui assombrit légèrement l’arrière-plan : en croyant cliquer en dehors du menu, l’utilisateur clique sur un gros bouton de fermeture à l’intérieur même du menu, la mécanique est invisible, mais l’interaction est naturelle.

L’arrière-plan reste interactif lorsque le menu est ouvert, et peut encore défiler alors même qu’il est assombri et estompé. Je doute que la propriété overscroll-behavior suffise à résoudre le problème, et je devrai probablement écrire quelques lignes de JavaScript. La question du comportement réglée, je peux maintenant me concentrer sur le contenu. L’arrangement horizontal offre un petit aperçu de mes ambitions.

Cela suffirait…
…mais j’ai toujours été gourmand.

  1. Une valeur de 100 % devrait suffire, mais l’effet de « rebondissement » à la fin du défilement dans Safari permet de voir quelques dizaines de pixels au-delà de la page web… et donc la poussière sous le tapis. Une valeur de 125 % est suffisante pour mettre le menu hors de portée, une valeur supérieure augmenterait la durée de l’« ouverture » du tiroir en lui faisant parcourir une plus grande distance. ↩︎