Go to Hackademy website

Création d'un menu en accordéon avec CSS

Victor Darras

Posté par dans la catégorie design

  • Tags de l'article
  • css
  • fr

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 selecteurs 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 selectionné, pour afficher son menu enfant, nous utiliserons un input, de type checkbox ou radio. Les 2 sont utilisables mais n’aurons pas exactement le même résultat en terme d’expérience utilisateur, nous verrons par la suite quelle est la différence. Afin de modifier la valeur de ces input, nous utiliserons des label 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#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#menu2 type="checkbox" name="menu" />
    <ul class="sub">
      <li></li>
      <li></li>
    </ul>
  </li>
</ul>

Vous devriez obtenir un rendu dans ce style là :

Accordéon sans HTML

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 permettrons de connaitre 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-menu, pratique pour les menu 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 input de type radio nous pourrons faire en sorte de n’afficher qu’un seul sous-menu à la fois, ce qui peut-être utilie lorsque tous les sous-menu 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’intéraction aurait très bien pu être effectuée 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-listes 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 ? Evidemment 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. Ce que je me suis empréssé de faire, je vous invite à découvrir mon exemple sur codepen :

See the Pen Pure CSS accordions by Victor Darras (@victordarras) on CodePen.


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. À bientot.

L’équipe Synbioz.

Libres d’être ensemble.

Commentaires (12) Flux RSS des commentaires

  • 04/06/2015 à 12:30

    Mouhamadou DER

    C'est joli, la manière dont les items s'affiche. C'est comme le fadein/fadeout de jquery

  • 05/06/2015 à 09:24

    Cyrille Jesmo Drazik

    Salut,

    J'ai refais l'exercice sur un codepen que voici : http://codepen.io/JesmoDrazik/pen/MwpNqq

    J'ai essayé de "BEMifier" le tout. C'est ma première fois avec cette méthodologie, ça doit pouvoir être plus propre ;)

    La technique est très simple et super efficace, bien trouvé :) Il faudra cependant rappeler que :checked n'est disponible qu'à partir d'IE 9 (http://caniuse.com/#feat=css-sel3). Je ne sais pas si il existe un moyen de faire la même chose sous IE 8 sans avoir recours à du JS.

  • 05/06/2015 à 09:37

    Victor

    Bonjour Cyrille,
    effectivement ça ne fonctionne qu'à partir de IE9,
    d'autant plus avec les transitions ou on passe à du IE10+...
    Sinon belle proposition, la propriété scale rend bien aussi dans ce genre de situation et le BEM c'est quand même bien sympa, il serait temps que je m'y mette sérieusement.
    (Sympa ton blog en passant)

  • 05/06/2015 à 10:03

    Cyrille Jesmo Drazik

    Oui après les transitions n'empêche pas le menu de fonctionner, c'est du plus visuel.

    Merci pour mon blog ;)

  • 09/02/2017 à 08:38

    Pierre Magnier

    Bonjour,
    J'ai un souci: mon code est exactement comme les sources et:
    quand je clique sur le Menu #1 j'ai les sous-menus,
    quand je clique sur Menu #3 j'ai les sous-menus.
    Mais quand je clique sur menu #2 rien ne se passe.
    C'est un problème de position car j'ai tout essayé et c'est en position 2 des menus que rien ne se passe.
    J'ai affiché tous les caractères sur Notepad++ et aucun caractère (contrôle ou autre)ne traîne dans mon fichier?
    Avez-vous une idée de ce qui se passe?
    Merci,
    Pierre Magnier

  • 09/02/2017 à 08:38

    Pierre Magnier

    Bonjour,
    J'ai un souci: mon code est exactement comme les sources et:
    quand je clique sur le Menu #1 j'ai les sous-menus,
    quand je clique sur Menu #3 j'ai les sous-menus.
    Mais quand je clique sur menu #2 rien ne se passe.
    C'est un problème de position car j'ai tout essayé et c'est en position 2 des menus que rien ne se passe.
    J'ai affiché tous les caractères sur Notepad++ et aucun caractère (contrôle ou autre)ne traîne dans mon fichier?
    Avez-vous une idée de ce qui se passe?
    Merci,
    Pierre Magnier

  • 09/02/2017 à 17:46

    Cyrille Jesmo Drazik

    Bonjour Pierre,

    Peux-tu mettre ton code sur http://codepen.io afin qu'on puisse y jeter un œil ?

  • 10/02/2017 à 09:23

    Pierre Magnier

    Bonjour Cyrille,
    Merci pour ton aide mais j'ai trouvé.... c'est effectivement une scorie (caractère de contrôle inopiné) qui bloquait tout.
    Bonne journée et encore merci pour ta proposition.
    Pierre

  • 10/02/2017 à 17:27

    Cyrille Jesmo Drazik

    @Pierre,

    Cool que tu aies trouvé. Au plaisir !

  • 02/11/2017 à 10:40

    patrick wagneur

    bonjour à tous,
    j'aimerais savoir s'il y a moyen de "préouvrir" un point de sous-menu à l'ouverture de la page ?
    d'avance merci.

  • 02/11/2017 à 12:14

    Cyrille Jesmo Drazik

    Il te suffit d'ajouter l'attribut `checked` à l'input de l'élément que tu veux voir ouvert au chargement de la page.

  • 02/11/2017 à 12:14

    Cyrille Jesmo Drazik

    Il te suffit d'ajouter l'attribut `checked` à l'input de l'élément que tu veux voir ouvert au chargement de la page.

Ajouter un commentaire