# Menus de navigation sur mesure : register_nav_menus et Walker_Nav_Menu

> Déclarer des emplacements de menus, écrire un Walker personnalisé accessible et filtrer nav_menu_css_class pour un menu vraiment sur mesure.

- Auteur : Clément Hadrot
- Publié le : 2020-09-14
- Mis à jour le : 2020-09-14
- Catégorie : Thèmes
- URL : https://wpmoderne.dev.wordpress-developpement.fr/themes/menus-navigation-register-nav-menus-walker/

## L’essentiel

- register_nav_menus pour déclarer les emplacements
- Walker_Nav_Menu personnalisé pour des sous-menus accessibles
- nav_menu_css_class pour des classes BEM propres

Le thème générique livré au Conservatoire Sainte-Cécile affichait un menu correct visuellement, mais totalement inutilisable au clavier : impossible d'atteindre les sous-menus sans souris, et le lecteur d'écran n'annonçait jamais qu'un élément contenait une liste déroulante. Plutôt que de rafistoler la sortie par défaut avec du CSS et du JavaScript défensif, la solution la plus propre consistait à écrire un `Walker_Nav_Menu` personnalisé, en contrôlant précisément chaque balise générée.

Ce tutoriel couvre les trois étapes : déclarer les emplacements de menu, écrire un Walker qui ajoute les attributs ARIA nécessaires, puis affiner les classes CSS générées avec le filtre `nav_menu_css_class` pour obtenir une nomenclature BEM cohérente avec le reste du thème.

## Déclarer les emplacements avec register_nav_menus

Première étape, indispensable avant même de pouvoir assigner un menu depuis l'administration :

```
function agence_register_menus() {
	register_nav_menus( array(
		'primary' => __( 'Menu principal', 'agence' ),
		'footer'  => __( 'Menu du pied de page', 'agence' ),
	) );
}
add_action( 'after_setup_theme', 'agence_register_menus' );
```

Chaque emplacement déclaré apparaît ensuite dans Apparence > Menus, où l'administrateur du site associe un menu créé à la main à l'un de ces emplacements. Sans cette déclaration, `wp_nav_menu()` n'a tout simplement rien à afficher.

## Écrire un Walker personnalisé pour des sous-menus accessibles

> L'essentiel à retenir : register_nav_menus pour déclarer les emplacements ; Walker_Nav_Menu personnalisé pour des sous-menus accessibles ; nav_menu_css_class pour des classes BEM propres

La classe `Walker_Nav_Menu` du cœur de WordPress fournit un rendu correct mais générique. Pour ajouter des attributs ARIA sur les éléments qui contiennent un sous-menu, il faut étendre cette classe et surcharger sa méthode `start_el()` :

```
class Agence_Walker_Nav_Menu extends Walker_Nav_Menu {

	public function start_el( &$output, $item, $depth = 0, $args = null, $id = 0 ) {
		$has_children = in_array( 'menu-item-has-children', $item->classes, true );

		$atts = array();
		if ( $has_children ) {
			$atts['aria-haspopup'] = 'true';
			$atts['aria-expanded'] = 'false';
		}
		$atts['href'] = ! empty( $item->url ) ? $item->url : '';

		$attributes = '';
		foreach ( $atts as $attr => $value ) {
			$attributes .= ' ' . $attr . '="' . esc_attr( $value ) . '"';
		}

		$output .= '<li class="' . esc_attr( implode( ' ', $item->classes ) ) . '">';
		$output .= '<a' . $attributes . '>' . esc_html( $item->title ) . '</a>';
	}
}
```

Le script associé se contente ensuite de basculer `aria-expanded` entre `true` et `false` au clic ou à la prise de focus, sans jamais générer lui-même le sous-menu : le HTML complet est déjà présent dans le DOM dès le chargement de la page, ce qui garantit son fonctionnement même si le JavaScript échoue à s'exécuter.

## Appeler le Walker dans wp_nav_menu()

```
wp_nav_menu( array(
	'theme_location' => 'primary',
	'container'       => false,
	'walker'          => new Agence_Walker_Nav_Menu(),
) );
```

## Filtrer nav_menu_css_class pour des classes BEM

Par défaut, WordPress génère des classes comme `menu-item`, `menu-item-has-children` ou `current-menu-item`, qui ne correspondent pas forcément à la convention de nommage du reste du thème. Le filtre `nav_menu_css_class` permet de les remplacer ou d'en ajouter, sans toucher au Walker :

```
function agence_nav_menu_bem_classes( $classes, $item, $args ) {
	$classes[] = 'c-menu__item';
	if ( in_array( 'current-menu-item', $classes, true ) ) {
		$classes[] = 'c-menu__item--active';
	}
	return array_diff( $classes, array( 'menu-item-has-children' ) );
}
add_filter( 'nav_menu_css_class', 'agence_nav_menu_bem_classes', 10, 3 );
```

Ici, on ajoute la classe `c-menu__item` propre à la nomenclature du projet, on traduit l'état actif en `c-menu__item--active`, et on retire la classe générique `menu-item-has-children` devenue inutile puisque le Walker gère déjà cet état via l'attribut `aria-haspopup`.

### Tester au clavier, pas seulement à la souris

- Tabulation jusqu'au premier élément avec sous-menu : le focus doit être visible.
- Entrée ou Espace sur cet élément : le sous-menu doit s'ouvrir et `aria-expanded` passer à `true`.
- Échap : le sous-menu doit se refermer et rendre le focus à l'élément parent.

> Un menu qui fonctionne parfaitement à la souris et pas du tout au clavier n'est pas un menu accessible à moitié : c'est un menu qui exclut une partie réelle des visiteurs.

## En résumé

Un Walker personnalisé demande un peu plus de code qu'un appel brut à `wp_nav_menu()`, mais c'est le seul moyen de garantir un balisage accessible et des classes CSS cohérentes avec le reste du thème. Le bloc Navigation apparu plus tard dans l'éditeur de site suivra une logique de rendu entièrement différente, qui n'est pas abordée dans ce tutoriel consacré aux thèmes classiques.
