Écrit par Victor D.
Bonjour à tous, l’article d’aujourd’hui , dans la lignée de celui concernant la barre de chargement en CSS portera sur la création d’un élément bien connu des intégrateurs, en particulier sur les applications métier : Le menu accordéon.
L’idée de cet article sera de se passer de Javascript, nous n’aurons besoin que de quelques astuces à base de balises HTML et de sélecteurs CSS. Voici un exemple du menu que nous pourrions obtenir à la fin de cet article.
Le markup HTML
Tout d’abord, créons un menu en HTML standard, à base de liste imbriquées :
<ul> <li> Menu #1 <ul> <li>Sous-menu #1</li> <li>Sous-menu #2</li> </ul> </li> <li> Menu #2 <ul> <li>Sous-menu #1</li> <li>Sous-menu #2</li> </ul> </li> <li> Menu #3 <ul> <li>Sous-menu #1</li> <li>Sous-menu #2</li> </ul> </li> </ul>
Ajoutons quelques classes afin de clarifier le markup et les CSS qui suivront, nous aurions pu n’utiliser que les éléments HTML mais le CSS aurait été plus abstrait. Notre menu global sera donc un .accordion , contenant lui-même des éléments avec un autre menu imbriqué que nous définirons avec .has-sub. Chaque sous-menu sera appelé .sub.
<ul class="accordion"> <li class="has-sub"> Menu #1 <ul class="sub"> <li></li> </ul> </li> [...] </ul>
Afin de savoir si un .has-sub est sélectionné, pour afficher son menu enfant, nous utiliserons un input, de type checkbox ou radio. Les 2 sont utilisables, mais n’auront pas exactement le même résultat en termes d’expérience utilisateur, nous verrons par la suite quelle est la différence.
Pour modifier la valeur de ces inputs, nous utiliserons des labels avec un attribut for désignant l’input concerné.
Chaque input se verra assigné un attribut name qui nous permettra de définir des groupes d’input. Il est possible en règle générale de placer l’input à l’intérieur du label afin de ne pas avoir à définir d’attribut for mais nous aurons besoin d’avoir l’input et le .sub au même niveau pour jouer avec les CSS.
<ul class="accordion"> <li class="has-sub"> <label for="menu1">Menu #1</label> <input id="menu1" type="checkbox" name="menu" /> <ul class="sub"> <li></li> <li></li> </ul> </li> <li class="has-sub"> <label for="menu2">Menu #2</label> <input id="menu2" type="checkbox" name="menu" /> <ul class="sub"> <li></li> <li></li> </ul> </li> </ul>
Vous devriez obtenir un rendu dans ce style-là :
Les CSS pour l’interaction
Tout d’abord, pour avoir un accordéon, nous devons faire en sorte de n’afficher que les éléments parents qui afficheront leurs enfants. Nous aurions pu utiliser un simple display: none; sur chaque .sub mais cela nous empêcherait de gérer une transition entre les 2 états. Nous allons donc commencer par appliquer une hauteur nulle à chaque élément de nos sous-menus avec un overflow: hidden; pour être sûr de ne pas afficher leur contenu, quelqu’il soit.
.sub> li { height: 0; overflow:hidden; }
Ensuite, nous dissimulons les inputs qui nous permettront de connaître le(s) sous-menu(s) affiché(s)
input[type="radio"], input[type="checkbox"] { display:none }
Et enfin, nous affichons les sous-menus lorsque l’input qui les précède est checké.
input:checked +.sub> li { height: 2em; }
Checkbox ou Radio
Comme je vous l’expliquais plus tôt, nous avons la possibilité d’utiliser des inputs de type checkbox ou radio afin de modifier le comportement du menu.
Checkbox
L’utilisation de checkbox nous permet d’ouvrir en parallèle plusieurs sous-menus, pratique pour les menus avec peu d’entrées et permet d’avoir une vision globale de tous les sous-menus. Cette solution permet aussi de refermer chacun des menus, pour afficher un élément sous ce dernier sans scroller par exemple.
Radio
En utilisant des inputs de type radio, nous pourrons faire en sorte de n’afficher qu’un seul sous-menu à la fois, ce qui peut être utile lorsque tous les sous-menus comportent beaucoup d’entrées et qu’il est inutile d’en afficher plusieurs à la fois. Cette solution ne permet pas, par contre, de refermer tous les menus.
Après l’interaction, ajoutons un peu de styles
Si on a tout suivi à la lettre, nous devrions nous retrouver avec une liste très moche contenant des labels permettant d’afficher ou non une sous-liste.
Comme je disais plus haut, ce genre d’interaction aurait très bien pu être effectué avec un display: none; sur chaque .sub. Mais la plupart du temps, nous aimerions bien avoir un vrai effet d’accordéon, où chaque sous-liste se déploie élégamment. Dans un premier temps, nous allons donc mettre en place une transition.
.sub> li { transition: all 0.25s ease-in-out // n'hésitez pas à jouer avec ces valeurs }
Nous remarquons maintenant que chaque sous-liste se déploie avec une transition, cela permet de renvoyer à l’utilisateur un indice visuel sur sa dernière action. Nous pouvons affiner la transition en ajoutant un changement d’opacité, et pourquoi pas de padding afin de modifier légèrement la taille et la position des entrées de notre sous-menu.
.sub> li { opacity: 0; height: 0; padding: 0; transition: all 0.25s ease-in-out; } input:checked +.sub> li { padding: 0.7em 1em height: 1.4em opacity: 1 }
Je vous laisse tester le résultat, bien plus fluide n’est-ce pas ? Évidemment l’ensemble reste visuellement proche d’une liste en HTML pure, nous pourrions modifier les couleurs de l’ensemble, ajuster des marges ou peaufiner encore les transitions.
N’hésitez pas à forker le codepen ou à refaire l’exercice et à nous partager vos propres expérimentations, je suis curieux de voir ce que ça pourrait donner ! J’espère que cet exercice vous a plu, si tel est le cas n’hésitez pas à le partager autour de vous.
À bientôt.