Niveau : Débutant

Créer des menus simples en CSS

Dans ce tutoriel, nous allons apprendre à réaliser des menus "simples" (sans arborescence: tous les liens sont au même niveau) à l'aide des feuilles de style.

Tutoriel par (Développeur web, Verviers (Belgique))
Créé le , mis à jour le (605181 lectures)
Tags : css, menu, rollover, menu css, menu graphique, sprite, porte, coulissant, hover

menu

Sommaire

Le code HTML

Comme toujours, le choix des balises html doit être guidé par la nature du contenu qu'on souhaite y placer. Dans le cas d'un menu, la question est un peu délicate, dans la mesure où il n'existe pas de balise spécifique pour désigner un ensemble de liens.

Un point de vue largement répandu consiste à considérer le menu de navigation comme une liste de liens, et donc à utiliser la balise correspondant aux listes non ordonnées, <ul>.

Par exemple, pour un menu comportant 5 items:

<ul id="navigation">
  <li><a href="#" title="aller à la section 1">item1</a></li>
  <li><a href="#" title="aller à la section 2">item2</a></li>
  <li><a href="#" title="aller à la section 3">item3</a></li>
  <li><a href="#" title="aller à la section 4">item4</a></li>
  <li><a href="#" title="aller à la section 5">item5</a></li>
</ul>

Remarquez l'identifiant placé sur la balise <ul>. C'est tout ce dont on a besoin accéder aux différents éléments du menu et pour les modifier dans la feuille de style.

S'il s'agit d'une liste de liens de navigation en HTML5, pensez à ajouter les balises <nav> et </nav> autour de la liste <ul>.

Capture d'écran du résultat:

Affichage de la liste dans un navigateur, sans styles

On va limiter la largeur du menu à une valeur donnée (200px), supprimer les marges et padding par défaut ainsi que la puce des éléments:

#navigation {
  width: 200px;
  list-style: none;
  margin: 0;
  padding: 0;
}

Capture d'écran du résultat:

Styles appliqués à la liste <ul>

Il est nécessaire de supprimer les marges et les padding pour obtenir le même rendu dans tous les navigateurs. En effet, les valeurs par défaut de ces propriétés ne sont pas les mêmes dans Firefox, Opera ou IE.

Par défaut, les éléments de liste ont un comportement de type bloc: ils s'empilent les uns sur les autres et occupent toute la largeur disponible. La disposition des éléments est donc déjà correcte.

On va donner une bordure, une couleur de fond et une couleur de texte aux éléments <li>:

#navigation li {
  background: #c00 ;
  color: #fff ;
  border: 1px solid #600 ;
  margin-bottom: 1px ;
}

Capture d'écran du résultat:

Styles appliqués aux éléments de liste

N'oubliez pas de choisir des couleurs de fond et de texte compatibles, et ce même si vous avez prévu d'employer une image de fond !

Pour que les éléments du menu réagissent comme des boutons au passage de la souris, il faut faire en sorte que les liens occupent tout l'espace à l'intérieur des <li>:

#navigation li a {
  display: block ;
  background: #c00 ;
  color: #fff ;
  font: 1em "Trebuchet MS",Arial,sans-serif ;
  line-height: 1em ;
  text-align: center ;
  text-decoration: none ;
  padding: 4px 0 ;
}

Au passage, on a modifié la police de caractères, l'alignement du texte, on a supprimé le soulignement des liens et on a ajouté des padding en haut et en bas pour "aérer" le menu, tout en positionnant le texte au milieu.

Il ne reste plus qu'à faire en sorte que les liens réagissent au passage de la souris ou lorsqu'ils reçoivent le focus (pour ceux qui naviguent au clavier):

#navigation li a:hover, #navigation li a:focus, #navigation li a:active {
  background: #900 ;
  text-decoration: underline ;
}

Capture d'écran du résultat:

Styles appliqués aux liens hypertextes

On peut éventuellement appliquer un effet de survol aux éléments de liste <li> pour obtenir le même résultat. Gardez à l'esprit que le vieux navigateur Internet Explorer 6 ne gère la pseudo-classe :hover que lorsqu'elle est appliquée à la balise <a>.

Avec les anciennes versions d'Internet Explorer, les <li> sont parfois séparés par des espaces importants, sans raison apparente. Les causes de ce désagrément, ainsi que les solutions, sont décrites dans cet article.

Voir le résultat

Cette fois, il faut modifier le comportement par défaut des <li> : ils ne doivent plus s'empiler, mais se placer les uns à côté des autres. Il y a deux approches possibles pour y parvenir.

Mode de rendu "en-ligne"

On peut modifier le mode d'affichage pour que les <li> s'affichent comme des éléments de type "inline":

#navigation li {
  display: inline;
}

Les éléments du menu se comportent maintenant comme du texte "normal".

Capture d'écran du résultat:

Disposition des <li> en mode inline

Cette méthode permet de réaliser facilement un menu de navigation aligné à droite ou centré, et évite de sortir les <li> du flux de la page (on verra plus loin quelques ennuis liés à la sortie de ces éléments du flux). De plus, la largeur des éléments du menu s'adapte automatiquement à la taille du texte.

Malheureusement, en procédant de cette façon, on ne peut plus contrôler les hauteurs et largeurs des <li>, on ne peut agir que sur les padding & sur l'interlignage.

Un exemple de menu centré:

#navigation {
  margin: 0 ;
  padding: 0 ;
  list-style: none ;
  text-align: center ;
}

On supprime les marges, padding & puces de la même manière que pour le menu vertical. La propriété text-align: center aura pour effet de centrer toutes les balises de type inline à l'intérieur de la liste.

#navigation li {
  display: inline ;
  margin-right: 1px ;
  color: #fff ;
  background: #c00 ;
}

On modifie le rendu des éléments de liste, et on ajoute couleur de fond, couleur du texte et bordures.

#navigation li a {
  padding: 4px 20px ;
  background: #c00 ;
  color: #fff ;
  border: 1px solid #600 ;
  font: 1em "Trebuchet MS",Arial,sans-serif ;
  line-height: 1em ;
  text-align: center ;
  text-decoration: none ;
}

Attention, il ne faut pas utiliser display: block sur les liens, sinon ils vont étirer les éléments de liste sur toute la largeur disponible, les obligeant à se superposer! On peut les agrandir en utilisant les valeurs de padding.

#navigation li a:hover, #navigation li a:focus, #navigation li a:active {
  background: #900 ;
  text-decoration: underline ;
}

Le comportement réactif des liens est obtenu de la même façon que pour le menu vertical.

Voir le résultat

Items flottants

Une autre possibilité est de faire "flotter" les éléments avec la propriété CSS float :

#navigation li {
  float: left ;
}

Dans ce cas, les <li> restent de type bloc, et on peut encore les dimensionner. Par contre, ils sont sortis du flux de la page, et la propriété text-align n'aura plus d'effet sur eux. Cette méthode ne sera donc utile que lorsqu'on désire obtenir des éléments de menu de mêmes dimensions.

Toujours à partir du même code XHTML, on peut construire un menu de ce type:

#navigation {
  margin: 0 ;
  padding: 0 ;
  list-style: 0 ;
}

Suppression des marges, padding et puces. La routine ;)

#navigation li {
  float: left ;
  width: 150px ;
  border: 1px solid #600 ;
  margin-right: 1px ;
  color: #fff ;
  background: #c00 ;
}

On fait "flotter" les <li> et on leur donne une largeur. Attention à laisser suffisament d'espace pour que le texte du lien puisse être agrandi confortablement par l'utilisateur!

Le rendu des liens se modifie exactement comme pour le menu vertical:

#navigation li a {
  display: block ;
  background: #c00 ;
  color: #fff ;
  font: 1em "Trebuchet MS",Arial,sans-serif ;
  line-height: 1em ;
  padding: 4px 0 ;
  text-align: center ;
}
#navigation li a:hover, #navigation li a:focus, #navigation li a:active {
  background: #900 ;
}

Si on insère du texte dans la page juste en dessous du menu et que celui-ci n'occupe pas toute la largeur, on observe que le texte vient se placer à côté du menu, au lieu de rester sagement en-dessous !

C'est à cause des flottants: en utilisant la propriété float: left, on a sorti les <li> du flux de la page. Ils n'interviennent dès lors plus dans le calcul de la hauteur de la liste <ul>. Comme celle-ci ne contient rien d'autre, sa hauteur est donc nulle. Oups.

Il y a (au moins) 3 solutions:

  1. appliquer clear: left à l'élément qui suit directement le menu dans le code html.
  2. forcer la liste à occuper une hauteur suffisante :
    #navigation {
      height: 1.8em ;
      ...
    }
    mais il n'est pas toujours facile de déterminer la hauteur requise, et des problèmes vont apparaître si le visiteur agrandit suffisament la police de caractères pour obliger le texte des items du menu à passer sur 2 lignes.
  3. obliger la liste à englober les flottants dans le calcul de sa hauteur en créant un nouveau contexte de formatage.

Voir le résultat

On a utilisé pour les 2 menus précédents la pseudo-classe hover, qui permet de modifier le style d'un élément lorsqu'il est survolé par la souris. Il est possible de modifier l'image d'arrière-plan des liens de cette façon, et donc de créer un effet de rollover en se servant uniquement de la feuille de style.

Par exemple, si on modifie le menu horizontal:

#navigation li a {
  display: block ;
  background: #c00 url(lienNormal.png) left top no-repeat ;
  color: #fff ;
  font: 1em "Trebuchet MS",Arial,sans-serif ;
  line-height: 1em ;
  padding: 4px 0 ;
  text-align: center ;
}
#navigation li a:hover, #navigation li a:focus, #navigation li a:active {
  background: #900 url(lienHover.png) left top no-repeat ;
}

Voir le résultat

Les images utilisées ont des dimensions de 150x50 px. La hauteur de 50px est supérieure à la hauteur strictement nécessaire, afin de permettre le redimensionnement du texte sans "abîmer" le menu. D'autre part, on a fait en sorte que le bas de l'image comporte un dégradé vers la couleur de fond. Ainsi, même dans le cas d'un agrandissement important de la police, la transition ne sera pas marquée entre image & couleur de fond.

C'est tout, l'effet de rollover est fonctionnel.

La méthode des portes coulissantes

Il reste cependant un détail gênant: le délai d'affichage de l'image au moment du survol. En effet, l'image "lienHover.png" n'est pas chargée par le navigateur avant que le visiteur ne passe sa souris au-dessus du menu. Il doit donc aller la chercher sur le serveur, ce qui demande un peu de temps (même si l'image est très légère!).

Dans ce cas précis, ce n'est pas trop pénalisant, car tous les items du menu utilisent les mêmes images d'arrière-plan. Le délai ne se fera sentir qu'une seule fois. Mais rien n'empêche de réaliser des menus plus complexes, où les différents items du menu ont des images d'arrière-plan différentes. Le manque de réactivité du menu devient alors très ennuyeux!

Heureusement, il existe une astuce qui permet de supprimer complètement cette inertie: la méthode des "portes coulissantes".

Au lieu d'utiliser 2 images distinctes pour l'état normal et l'état survolé, on va les regrouper dans une seule et même image. Ici, par exemple, on crée une image de 300x50 px:

construction de l'image de fond utilisée dans la méthode des portes coulissantes

On va donc utiliser cette nouvelle image pour l'état normal et l'état survolé, et on réalisera l'effet de rollover en modifiant la position de l'image d'arrière-plan:

#navigation li a {
  display: block ;
  background: #900 url(lienCoulissant.png) left top no-repeat ;
  color: #fff ;
  font: 1em "Trebuchet MS",Arial,sans-serif ;
  line-height: 1em ;
  padding: 4px 0 ;
  text-align: center ;
}	
#navigation li a:hover, #navigation li a:focus, #navigation li a:active {
  background: #033 url(lienCoulissant.png) right top no-repeat ;
}

Voir le résultat

Puisqu'il n'y a plus qu'une seule image, elle est enregistrée par le navigateur dans le cache au moment du chargement de la page, et le changement d'image au survol est maintenant quasi-instantané.

Le "tout image"

Pour de nombreuses raisons, il est courant d'utiliser une image par item de menu, qui comporte à la fois l'arrière-plan et le texte du lien. La tentation est grande d'utiliser la méthode des rollovers CSS avec ce type d'image et de les insérer comme des images d'arrière-plan dans la feuille de style. On se retrouve alors avec une liste de liens vides en guise de menu.

Attention : En faisant cela, on retire une information importante du document HTML, et on rend le menu inutilisable pour tous les visiteurs qui n'ont pas accès aux images d'arrière-plan: navigateurs textes, lecteurs d'écrans, utilisateurs ayant désactivé le chargement des images, robots d'indexation des moteurs de recherche, etc.

Il faut donc considérer d'autres méthodes pour obtenir un effet de rollover sur de telles images. Le plus simple consiste à placer les images en dur dans le code html et à créer le rollover avec Javascript, quite à sacrifier l'effet de survol pour les utilisateurs qui ne disposent pas de JS.