Petit topo sur les menus déroulants en CSS

Point de départ : menu pas déroulant

Il s'agit d'un menu dont seuls les items secondaires sont des liens (pas les titres de catégorie). J'ai choisi d'utiliser des sections contenant un titre et une liste, plutôt que de faire une liste de listes comme on le voit souvent.

	<nav class="menu">
		<section class="categorie">
			<h3>Rubans</h3>
			<ul>
				<li><a href="">Tagliatelle</a></li>
				<li><a href="">Fettuccine</a></li>
				<li><a href="">Lasagne</a></li>
			</ul>
		</section>
		<section class="categorie">
			<h3>Ficelles</h3>
			<ul>
				<li><a href="">Spaghetti</a></li>
				<li><a href="">Spaghettoni</a></li>
				<li><a href="">Vermicelli</a></li>
				<li><a href="">Capellini</a></li>
			</ul>
		</section>
		<section class="categorie">
			<h3>Tubes</h3>
			<ul>
				<li><a href="">Macaroni</a></li>
				<li><a href="">Cannelloni</a></li>
				<li><a href="">Penne</a></li>
				<li><a href="">Cavattapi</a></li>
			</ul>
		</section>
		<section class="categorie">
			<h3>Autres</h3>
			<ul>
				<li><a href="">Farfalle</a></li>
				<li><a href="">Fusilli</a></li>
				<li><a href="">Ravioli</a></li>
				<li><a href="">Gnocchi</a></li>
				<li><a href="">Conchigliette</a></li>
			</ul>
		</section>
	</nav>

Pour le CSS, j'utilise du inline-block pour simplifier l'exemple.

.menu {
	border: 1px solid black;
}

.menu .categorie {
	border: 1px solid red;
	display: inline-block;
	vertical-align: top;
}

.menu .categorie ul {
	background-color: lightgreen;
}

NB : j'ai mis une classe categorie aux catégories, que j'utilise dans le CSS au lieu du sélecteur d'élément section. Cela rend le CSS plus clair et moins dépendant du HTML.

Menu déroulant de base

On met tout simplement le sous-menu en display:none par défaut, et en display:block quand son parent (la section de classe categorie) est survolé.

.menu .categorie ul {
	display: none;
}

.menu .categorie:hover ul {
	display: block;
}

Cependant, ça ne suffit pas, car l'apparition des sous-menus décale le reste de la page.

Menu déroulant propre

.menu .categorie ul {
	position: absolute;
}

Pour éviter le problème de la section précédente, j'ai mis les sous-menus en position:absolute. Comme je n'ai pas utilisé de décalage (avec les propriétés top, left, etc.), ils ne changent pas de position ; cependant, ils sont sortis du « flux » normal de la page, leur apparition ne fait donc rien bouger.

On peut cependant remarquer que leur largeur ne dépend plus de celle de leur parent. On va voir deux solutions dans la suite.


Habillage du menu

Le CSS ci-dessous reprend les quelques règles permettant d'obtenir un menu déroulant, et ajoute un peu d'habillage au menu. En particulier, on prend soin de présenter de la même façon les éléments h3 (titres des catégories) et les liens eux-mêmes (sauf en ce qui concerne le survol).

On utilise un width fixé (ici à 10em) pour que les sous-menus soient de même taille que les titres de catégorie.

.menu {
	text-align: center;
}

.menu .categorie {
	display: inline-block;
	vertical-align: top;
	background: lightblue;
}

.menu .categorie ul {
	display: none;
	position: absolute;
	padding: 0;
	margin: 0;
	list-style: none;
}

.menu .categorie:hover ul {
	display: block;
}

.menu h3,
.menu a {
	display: block;
	margin: 0;
	padding: .5em 1.5em;
	font-size: inherit;
	color: black;
	background-color: lightblue;
	text-decoration: none;
	box-sizing: border-box;
	width: 10em;
}

.menu .categorie:hover h3 {
	color: white;
	background-color: steelblue;
}

.menu a:hover {
	color: white;
	background-color: skyblue;
}

Avec grid et des animations

Utiliser inline-block est pratique, mais n'est pas très propre pour une mise en page précise, car les espaces dans le HTML apparaissent dans le rendu entre les items.

Le CSS ci-dessous utilise une grille CSS pour créer un menu déroulant dont chaque item fait exactement un quart de la largeur disponible, et ajoute une animation au déroulement du menu.

.menu {
	text-align: center;
	display: grid;
	grid-template-columns: 1fr 1fr 1fr 1fr;
}

.menu .categorie {
	background: lightblue;
	position: relative;  /* pour être la «racine» du sous-menu en absolute */
}

.menu .categorie ul {
	max-height: 0;
	overflow: hidden;
	transition: 0;
	position: absolute;
	padding: 0;
	margin: 0;
	list-style: none;
	width: 100%;  /* pour avoir la même largeur que la «racine» */
}

.menu .categorie:hover ul {
	max-height: 50em;
	transition: 1s;
}

.menu h3,
.menu a {
	display: block;
	margin: 0;
	padding: .5em 1.5em;
	font-size: inherit;
	color: black;
	background-color: lightblue;
	text-decoration: none;
	box-sizing: border-box;
}

.menu .categorie:hover h3 {
	color: white;
	background-color: steelblue;
}

.menu a:hover {
	color: white;
	background-color: skyblue;
}

Plusieurs points à noter :