Créer un menu off-canvas entièrement en css

Ce que j’aime par dessus tout avec CSS3, c’est cette capacité qu’il offre de nous affranchir de javascript pour tout ce qui touche aux animations. Après m’être penché sur une solution de menus accordéons en full css (dont je parlerai bientôt), je vous propose une version revisitée du menu « off canvas » via css3, sans javascript.

https://embed.cacher.io/815631d40534af44a8ac16c6022f4cf12d58ad40.js?a=0ce1d2c84cb4970f9a5b4b1d7640394e

Cette solution m’a été inspirée par csswizardry et ses filtres de contenu en full css grâce au sélecteur « :target ». Je pense que les possibilités offertes par ce sélecteur sont nombreuses, et je me suis dit que j’en testerais bien une.

Pour commencer, que propose ce sélecteur au juste ? D’après Mozilla Developer Network :

La pseudo-classe:target permet de cibler l’unique élément (s’il existe) dont l’attribut id correspond au fragment d’identifiant de l’URI du document.

En clair, cela signifie que je peux cibler un élément identifié comme c’est le cas avec une ancre. Je clique sur un lien qui pointe vers une ancre (exemple href= »#mon-id »), je serai à même d’appliquer à l’élément ciblé par cette ancre un style… ou un comportement dans notre cas 🙂

Je tiens à préciser que j’utilise massivement sass dans cet article. Je met le code complet à la fin de l’article, avec la feuille de style css compilée 😉

Démonstration

Considérons le code qui suit pour la partie html :

Pure off canvas css menu

J’ai créé un menu portant l’id « menu » ainsi qu’un double bouton permettant de l’ouvrir et le fermer.

Remarquez au passage ce qui suit : Dans la balise portant la classe « button-group » nous avons en réalité 2 liens. Cet artifice permettra par la suite d’avoir un bouton pour fermer le menu. Je vous donne plus de détails dans la partie consacrée à css.
autre chose, le menu porte un id, car pour designer son comportement, nous allons faire appel au pseudo élément `:target`, ce qui en simplifiant, revient à créer une ancre.

A présent que notre structure html est prête, passons à la partie amusante : la css 🙂

Voici comment on va procéder : lorsque le bouton sera cliqué, l’élément ciblé via son attribut `href` sera sélectionné. A ce moment, le menu s’ouvrira, et par la même occasion, le second bouton passera en premier plan de manière à nous permettre de le rendre cliquable et de refermer le menu.

Commençons par opérer un « reset » sur notre page :

* {
  box-sizing: border-box;
}

html, body {
  margin: 0;
}

Ainsi nous aurons bien un menu bord à bord avec notre fenêtre. Ajoutons à cela le design de notre menu :

#menu {
  position: absolute;
  height: 100vh;
  width: 300px;
  margin: 0;
  background-color: #000;
  transform: translateX(-100%); /* Rend le menu invisible */
  a {
    color:#fff;
    font-family: Cutive Mono;
    text-decoration: none;
  }
}

Rien d’exotique ici, nous avons seulement placé le tout en position absolue, stipulé une translation négative de 100% de la largeur de l’élément afin de masquer le menu.

Passons aux boutons :

.button-group{
  position: absolute;
  width: 65px;
  height: 65px;
  top: 0;
  left: 0;
  margin: 8px; /* histoire de ne pas avoir le bouton collé */
}

.menu-button {
  width: 100%;
  height: 100%;
  position: absolute;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: #000;
  text-align: center;
  color: #fff; 
  border-radius: 50%;
  left: 0;
  top: 0;
  span {
    display: block;
    width: 100%;
    height: 20px;
    position: absolute;

    .fa-times {
      display: none;
    }
  }
  
  &.off {
    z-index: -1;
  }
}

Ce qui est important ici est le positionnement absolu de notre groupe de boutons, mais aussi et surtout ce dont je parlais plus tôt : le second bouton est placé derrière le premier grâce à un `z-index` négatif. Ainsi, lorsque nous cliquerons la première fois, nous ne risquerons pas de cibler le mauvais bouton.

Désormais, tout est près pour accueillir notre comportement. Il suffit d’utiliser le sélecteur css `:target` sur le menu pour jouer notre astuce.

#menu:target {
  transform: translateX(0);
  +.button-group {
    left: 300px;
    
    .menu-button:first-child {
      span {
        .fa-times {
          display: block;
        }
        .fa-bars {
          display: none;
        }
      }
    }
    
    .off {
      z-index: 0;
      background-color: transparent;
    }
  }
}

On a donc ajouté notre sélecteur qui dit que s’il est ciblé, notre menu opérera une translation vers la droite. On en profite au passage pour faire passer notre second bouton – celui qui ferme – au premier plan, en redéfinissant sont `z-index` à 0. Pour le cibler, rien de plus simple : notre groupe de boutons se trouve exactement après le menu dans le DOM. Du coup, on peut agir sur ses enfants simplement en utilisant le sélecteur `+`. On re-style aussi ce dernier pour se positionner à 300px du bord gauche du document.

Pas encore satisfait ? Allons un peu plus loin en tirant parti des facultés de css en termes d’animation et rendons tout cela un peu plus touchy’ 🙂

Et puisque nous faisons du scss, créons une petite `@mixin` qui se chargera de créer nos transitions :

* {
  box-sizing: border-box;
}

html, body {
  margin: 0;
}

@mixin animation() {  
  transition-duration: 0.5s;
  transition-timing-function: cubic-bezier(0.65, 0.05, 0.36, 1);
}

.button-group{
  position: absolute;
  width: 65px;
  height: 65px;
  top: 0;
  left: 0;
  margin: 8px; /* just to give some space*/
  @include animation();
}

.menu-button {
  width: 100%;
  height: 100%;
  position: absolute;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: #000;
  text-align: center;
  color: #fff; 
  border-radius: 50%;
  left: 0;
  top: 0;
  span {
    display: block;
    width: 100%;
    height: 20px;
    position: absolute;

    .fa-times {
      display: none;
    }
  }
  
  &.off {
    z-index: -1;
  }
}

#menu {
  position: absolute;
  height: 100vh;
  width: 300px;
  margin: 0;
  background-color: #000;
  transform: translateX(-100%);
  a {
    color:#fff;
    font-family: Cutive Mono;
    text-decoration: none;
  }
  @include animation();
  
  &:target {
    transform: translateX(0);
    +.button-group {
      left: 300px;
      
      .menu-button:first-child {
        span {
          .fa-times {
            display: block;
          }
          .fa-bars {
            display: none;
          }
        }
      }
      
      .off {
        z-index: 0;
        background-color: transparent;
      }
    }
  }
}

Je trouve cette expérimentation – à défaut d’être respectueuse des règles d’accessibilité – très intéressante dans la mesure où elle remet en question l’usage de javascript pour des usages d’ordre fonctionnel ou esthétique. Ceci tend à rendre la frontière entre css et javascript un peu plus floue en rendant css auto suffisant lors de traitements fonctionnels un tantinet complexes.

En revanche, attention ! rechargez votre page et l’état du menu ne sera pas réinitialisé. En effet étant donné que nous utilisons une ancre, celle ci reste dans la barre d’adresse. De plus, l’état de votre menu est de fait enregistré dans votre historique.

Télécharger les sources

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s