Développement Web

Tutoriel : Créer un menu de navigation responsive

12 دقائق للقراءة

Prérequis

  • Niveau : bases HTML, CSS et JavaScript (cf. nos tutoriels Flexbox et manipulation du DOM).
  • Outils : VS Code + Live Server, navigateur moderne avec DevTools mobile.
  • Temps estimé : 1 h.

Pourquoi un menu responsive ?

Le menu est la première chose qu’un visiteur voit. S’il est cassé sur mobile (60 % du trafic), tout le reste devient inutile. Cet article construit le menu standard du web moderne : barre horizontale sur desktop, hamburger animé sur mobile, fermeture intelligente.

Le menu responsive : la première chose que voit votre visiteur

Un menu de navigation qui s’adapte à tous les écrans est essentiel. Sur desktop, c’est une barre horizontale. Sur mobile, c’est un menu hamburger. Voici comment créer les deux en HTML, CSS et JavaScript pur.

HTML du menu

<header>
    <nav class="navbar">
        <a href="#" class="logo">ITSkills</a>
        
        <ul class="nav-links" id="navLinks">
            <li><a href="#" class="actif">Accueil</a></li>
            <li><a href="#">Formations</a></li>
            <li><a href="#">Blog</a></li>
            <li><a href="#">À propos</a></li>
            <li><a href="#" class="btn-nav">Contact</a></li>
        </ul>
        
        <!-- Bouton hamburger (mobile) -->
        <button class="hamburger" id="hamburger" aria-label="Menu">
            <span></span>
            <span></span>
            <span></span>
        </button>
    </nav>
</header>

CSS complet

/* Reset */
* { margin: 0; padding: 0; box-sizing: border-box; }

/* Navbar */
.navbar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 15px 40px;
    background: white;
    box-shadow: 0 2px 15px rgba(0,0,0,0.08);
    position: fixed;
    top: 0;
    width: 100%;
    z-index: 1000;
}

/* Logo */
.logo {
    font-size: 24px;
    font-weight: 700;
    color: #667eea;
    text-decoration: none;
}

/* Liens de navigation */
.nav-links {
    list-style: none;
    display: flex;
    align-items: center;
    gap: 30px;
}

.nav-links a {
    text-decoration: none;
    color: #555;
    font-weight: 500;
    position: relative;
    transition: color 0.3s;
}

.nav-links a:hover,
.nav-links a.actif { color: #667eea; }

/* Soulignement animé */
.nav-links a::after {
    content: "";
    position: absolute;
    bottom: -5px;
    left: 0;
    width: 0;
    height: 2px;
    background: #667eea;
    transition: width 0.3s;
}

.nav-links a:hover::after,
.nav-links a.actif::after { width: 100%; }

/* Bouton CTA dans le menu */
.btn-nav {
    background: #667eea !important;
    color: white !important;
    padding: 10px 24px;
    border-radius: 25px;
}

.btn-nav::after { display: none !important; }

/* Hamburger (caché sur desktop) */
.hamburger {
    display: none;
    background: none;
    border: none;
    cursor: pointer;
    padding: 5px;
}

.hamburger span {
    display: block;
    width: 28px;
    height: 3px;
    background: #333;
    margin: 5px 0;
    border-radius: 3px;
    transition: all 0.3s;
}

/* Animation hamburger → X */
.hamburger.actif span:nth-child(1) {
    transform: rotate(45deg) translate(6px, 5px);
}
.hamburger.actif span:nth-child(2) {
    opacity: 0;
}
.hamburger.actif span:nth-child(3) {
    transform: rotate(-45deg) translate(6px, -5px);
}

/* ========== RESPONSIVE ========== */
@media (max-width: 768px) {
    .hamburger { display: block; }
    
    .nav-links {
        position: fixed;
        top: 60px;
        right: -100%;
        width: 280px;
        height: calc(100vh - 60px);
        background: white;
        flex-direction: column;
        padding: 40px 30px;
        gap: 20px;
        transition: right 0.3s ease;
        box-shadow: -5px 0 20px rgba(0,0,0,0.1);
    }
    
    .nav-links.open { right: 0; }
    
    .nav-links a { font-size: 18px; }
    
    .btn-nav {
        text-align: center;
        display: block;
        width: 100%;
    }
}

JavaScript

const hamburger = document.getElementById('hamburger');
const navLinks = document.getElementById('navLinks');

// Toggle menu
hamburger.addEventListener('click', () => {
    hamburger.classList.toggle('actif');
    navLinks.classList.toggle('open');
});

// Fermer au clic sur un lien
document.querySelectorAll('.nav-links a').forEach(link => {
    link.addEventListener('click', () => {
        hamburger.classList.remove('actif');
        navLinks.classList.remove('open');
    });
});

// Fermer si clic à l'extérieur
document.addEventListener('click', (e) => {
    if (!e.target.closest('.navbar')) {
        hamburger.classList.remove('actif');
        navLinks.classList.remove('open');
    }
});

// Navbar transparente → opaque au scroll
window.addEventListener('scroll', () => {
    const navbar = document.querySelector('.navbar');
    navbar.style.background = window.scrollY > 50 
        ? 'rgba(255,255,255,0.98)' 
        : 'white';
});

Variante : Menu dropdown (sous-menu)

<li class="dropdown">
    <a href="#">Formations ▾</a>
    <ul class="dropdown-menu">
        <li><a href="#">Développement Web</a></li>
        <li><a href="#">Marketing Digital</a></li>
        <li><a href="#">Cybersécurité</a></li>
    </ul>
</li>

<style>
.dropdown { position: relative; }
.dropdown-menu {
    position: absolute;
    top: 100%;
    left: 0;
    background: white;
    min-width: 220px;
    box-shadow: 0 8px 25px rgba(0,0,0,0.1);
    border-radius: 8px;
    padding: 10px 0;
    opacity: 0;
    visibility: hidden;
    transform: translateY(10px);
    transition: all 0.3s;
    list-style: none;
}
.dropdown:hover .dropdown-menu {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}
.dropdown-menu a {
    display: block;
    padding: 10px 20px;
}
.dropdown-menu a:hover { background: #f5f5f5; }
</style>

Erreurs fréquentes

Le menu hamburger ne s’ouvre pas

Cause : les IDs hamburger et navLinks ne correspondent pas, ou le script est chargé avant le DOM.
Solution : ajoutez defer au script, vérifiez les IDs avec les DevTools.

Menu mobile qui se fige sous la navbar

Cause : oubli du position: fixed sur .nav-links mobile, ou top incorrect.
Solution : dans la media query, déclarez position: fixed; top: 60px (= hauteur navbar).

Pas accessible au clavier

Cause : le bouton hamburger n’a pas d’aria-label, ou aria-expanded n’est pas mis à jour.
Solution : ajoutez aria-label="Ouvrir le menu" et synchronisez aria-expanded dans le toggle JS.

Le menu reste ouvert après changement de page

Cause : on oublie de fermer le menu au clic sur un lien interne (SPA).
Solution : bouclez sur les liens et retirez la classe d’ouverture au clic (déjà fait dans cet article).

Exercice pratique

🎯 Défi : Navigation complète

  1. Créez un menu responsive avec logo, 5 liens et bouton CTA
  2. Ajoutez un sous-menu dropdown sur « Formations »
  3. Implémentez le hamburger animé (→ X) sur mobile
  4. Le menu mobile doit se fermer au clic sur un lien
  5. Testez sur 3 tailles : mobile (375px), tablette (768px), desktop (1200px)

Accessibilité du menu de navigation : règles WCAG 2.2

Un menu de navigation est l’élément le plus utilisé d’un site — il doit être impeccable côté accessibilité. Cinq règles obligatoires en 2026. Première règle : structure sémantique avec <nav> et aria-label descriptif (par exemple aria-label="Navigation principale") pour distinguer plusieurs <nav> sur la même page. Deuxième règle : focus visible sur chaque lien, avec un outline contrasté (minimum 3:1 par rapport au fond) pour les utilisateurs au clavier.

Troisième règle : menu hamburger accessible. Le bouton qui ouvre/ferme le menu doit avoir un aria-expanded qui reflète son état (true/false) et un aria-controls qui pointe vers l’ID du menu. Quatrième règle : fermeture au clavier. Touche Escape ferme le menu mobile, et le focus revient sur le bouton hamburger. Cinquième règle : tap targets ≥ 44 × 44 px sur mobile, exigence Apple HIG et Android pour la touchabilité confortable.

Patterns avancés : méga-menus et sous-menus

Pour les sites complexes (e-commerce, médias, organisations), un menu plat à 5-7 entrées ne suffit plus. Trois patterns dominent en 2026. Premier pattern : méga-menu qui s’ouvre en pleine largeur sous une entrée parent, contenant plusieurs colonnes de liens groupés thématiquement avec parfois des images promotionnelles. Adapté aux sites e-commerce avec dizaines de catégories produits. Deuxième pattern : menu déroulant classique avec sous-menus à plusieurs niveaux, simple à implémenter mais plus difficile à utiliser sur mobile.

Troisième pattern : off-canvas drawer sur mobile, qui glisse depuis le côté de l’écran en couvrant partiellement la page. Pattern dominant sur les apps natives, transposable au web via transform: translateX(). Pour chaque pattern, prévoir une version desktop (hover ou click) et une version mobile (tap + drawer) qui peuvent diverger considérablement dans leur logique d’interaction.

Performance : éviter le layout shift au chargement

Un menu mal codé peut faire sauter le contenu de la page pendant le chargement, dégradant le score Cumulative Layout Shift (CLS) qui pénalise le SEO. Trois patterns évitent ce problème. Premier : réserver l’espace du menu en CSS dès le premier paint avec min-height sur le header. Deuxième : éviter le FOUC (Flash Of Unstyled Content) en chargeant le CSS critique inline dans le <head> avec uniquement les styles du header et du menu.

Troisième : si le menu hamburger ouvre/ferme avec une transition CSS, utiliser transform et opacity plutôt que display: none/block qui force un reflow. transform est animé sur le compositor GPU, ce qui donne 60 fps même sur Android d’entrée de gamme.

Adaptation au contexte ouest-africain

Pour un développeur basé à Dakar, Abidjan, Bamako ou Cotonou, le menu de navigation est l’élément où la majorité des PME perdent leurs visiteurs. Sur un Galaxy A03 avec 4G dégradée, un menu mal optimisé prend 2-3 secondes à devenir interactif — beaucoup de visiteurs cliquent dans le vide pendant ce temps et finissent par quitter. Trois conseils pratiques. Premièrement, réduire le menu mobile à 5-7 entrées maximum. Au-delà, l’utilisateur scroll et abandonne. Deuxièmement, prioriser les actions principales (Accueil, Produits, Contact) en haut du menu drawer. Troisièmement, tester systématiquement le focus clavier — les utilisateurs sénégalais ou ivoiriens utilisateurs de lecteurs d’écran existent et méritent un site utilisable.

Pour les sites WordPress, le menu se construit dans Apparence → Menus et le thème se charge du responsive. Pour les thèmes peu performants par défaut, le guide Bootstrap 5 responsive donne les patterns de référence pour un navbar responsive performant.

Menu sticky vs menu absolute : choisir la bonne ergonomie

Trois positionnements de menu coexistent en 2026, chacun avec ses cas d’usage. Premier positionnement : sticky (le menu reste visible en haut quand on scroll). Pratique pour les sites longs où l’utilisateur veut accéder à la navigation depuis n’importe quel point. Réalisé avec position: sticky; top: 0; z-index: 100. Attention au CLS si le menu apparaît brutalement — préférer une animation douce avec transition.

Deuxième positionnement : fixed (le menu reste collé au viewport quoi qu’il arrive). Comportement similaire au sticky mais le menu n’occupe plus d’espace dans le flux du document, ce qui peut créer des chevauchements avec le contenu si pas géré. Réservé aux sites où le menu est toujours présent (apps web, dashboards). Troisième positionnement : statique (le menu défile avec la page). Pattern minimaliste pour les sites vitrines courts où la navigation reste accessible en haut sans avoir besoin de la suivre.

Méga-menus : pattern et pièges

Un méga-menu réussi tient à trois facteurs. Premier facteur : structuration claire en colonnes thématiques. Une colonne par catégorie, 5-8 sous-entrées maximum par colonne. Au-delà, l’utilisateur ne scanne plus, il décroche. Deuxième facteur : fermeture intuitive. Le méga-menu doit se fermer quand le curseur sort de la zone, après un délai de 200-300 ms pour éviter les fermetures accidentelles. Troisième facteur : équivalent mobile dégradé. Sur un écran de 375 px, un méga-menu en colonnes ne tient pas — il bascule en accordéon vertical avec catégories repliables.

Les pièges classiques : méga-menu qui s’ouvre au survol involontaire, qui couvre le contenu de la page, qui charge des images promotionnelles lourdes ralentissant le rendu. Pour les sites e-commerce sérieux comme Amazon ou Jumia, le méga-menu est un investissement UX qui demande plusieurs itérations avant d’être vraiment efficace.

Indicateur de page active : visible et accessible

L’utilisateur doit toujours savoir où il se trouve dans la navigation. Trois techniques cumulables. Première technique : classe CSS active sur le lien correspondant à la page courante (par exemple .is-active) avec un style distinctif (couleur, soulignement, fond). Deuxième technique : attribut aria-current="page" qui annonce aux lecteurs d’écran que l’élément correspond à la page active. Troisième technique : indicateur visuel constant (barre, point, icône) pour les utilisateurs qui ne perçoivent pas bien la différence de couleur.

Un piège courant est de styler uniquement le link actif sans aria-current. Les utilisateurs voyants comprennent, mais ceux au lecteur d’écran ne savent pas qu’ils sont sur la page courante — ils tabbent dans un menu qu’ils croient nouveau alors qu’ils sont déjà sur la page.

Tester son menu sur appareils réels

Tester un menu sur Chrome DevTools en mode mobile reste utile mais ne reproduit pas tout. Trois aspects ne se voient qu’en testant sur un vrai téléphone. Premier aspect : la latence du tap sur la zone active. Une zone trop petite oblige à viser, ce qui frustre. Deuxième aspect : la friction du scroll horizontal accidentel pendant un swipe vertical. Troisième aspect : le comportement de la barre d’URL Safari iOS qui apparaît/disparaît selon le scroll, modifiant la hauteur du viewport et faisant sauter un menu fixed.

Pour valider, tester systématiquement sur deux appareils minimum : un Android d’entrée de gamme (Galaxy A03 ou équivalent à 200 EUR) et un iPhone récent. C’est le contexte réel d’usage de la majorité des visiteurs ouest-africains. Pour les patterns de validation et tests, voir aussi le tutoriel de validation de formulaire JS.

À lire ensuite sur la mesure UX, des outils comme Microsoft Clarity (gratuit) ou Hotjar fournissent des heatmaps des clics réels sur votre menu — vous voyez quels items sont vraiment utilisés et lesquels n’ont jamais été cliqués depuis 6 mois.

Lectures complémentaires

مشاركة