Une barre de navigation fixe reste en haut de l'écran pendant que le visiteur fait défiler la page : le menu est toujours à portée de clic. Tu vas la construire en HTML et CSS avec position: sticky, puis ajouter en JavaScript deux finitions : une ombre qui apparaît au défilement, et le lien de la section en cours mis en évidence. Durée : environ 30 minutes.
Ce que tu vas construire
- une barre qui reste collée en haut de l'écran, sur ordinateur comme sur mobile ;
- des liens vers les sections de la page, avec un défilement fluide ;
- des titres qui ne se cachent pas sous la barre quand on arrive sur une section ;
- une ombre sous la barre dès que la page défile ;
- le lien de la section affichée à l'écran mis en évidence.
Crée un dossier avec index.html, style.css et script.js.
1. Le HTML : un en-tête et des sections
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Barre de navigation fixe</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header class="site-header">
<nav class="nav" aria-label="Navigation principale">
<a href="#accueil" class="nav-logo">Mon portfolio</a>
<ul class="nav-links">
<li><a href="#accueil">Accueil</a></li>
<li><a href="#services">Services</a></li>
<li><a href="#projets">Projets</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</nav>
</header>
<main>
<section id="accueil">
<h1>Bienvenue</h1>
<p>Fais défiler la page : la barre reste en haut de l'écran.</p>
</section>
<section id="services">
<h2>Services</h2>
<p>Sites vitrines, applications web, maintenance.</p>
</section>
<section id="projets">
<h2>Projets</h2>
<p>Une sélection de mes derniers travaux.</p>
</section>
<section id="contact">
<h2>Contact</h2>
<p>Écris-moi pour parler de ton projet.</p>
</section>
</main>
<script src="script.js"></script>
</body>
</html>
La barre est un <header> qui contient un <nav>. L'attribut aria-label nomme la navigation pour les lecteurs d'écran, ce qui est utile quand une page en compte plusieurs. Chaque lien pointe vers l'id d'une section : href="#projets" mène à <section id="projets">.
2. Le CSS : sticky plutôt que fixed
html {
scroll-behavior: smooth;
}
body {
margin: 0;
font-family: system-ui, sans-serif;
line-height: 1.5;
}
/* La barre reste collée en haut de l'écran pendant le défilement. */
.site-header {
position: sticky;
top: 0;
z-index: 10;
background: #fff;
transition: box-shadow 0.2s;
}
/* Ajoutée par le JavaScript dès que la page défile. */
.site-header.scrolled {
box-shadow: 0 2px 12px rgb(0 0 0 / 0.12);
}
.nav {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 0.5rem 1.5rem;
max-width: 60rem;
margin: 0 auto;
padding: 0.75rem 1rem;
}
.nav-logo {
font-weight: 700;
color: #25737d;
text-decoration: none;
}
.nav-links {
display: flex;
flex-wrap: wrap;
gap: 0.25rem 1rem;
margin: 0;
padding: 0;
list-style: none;
}
.nav-links a {
padding: 0.25rem 0;
border-bottom: 2px solid transparent;
color: #333;
text-decoration: none;
}
.nav-links a:hover,
.nav-links a:focus-visible,
.nav-links a[aria-current="true"] {
border-bottom-color: #25737d;
color: #25737d;
}
section {
min-height: 80vh;
max-width: 60rem;
margin: 0 auto;
padding: 2rem 1rem;
/* Laisse la place de la barre quand on arrive sur une ancre. */
scroll-margin-top: 4rem;
}
@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
}
Les points importants :
position: stickyettop: 0: la barre défile normalement, puis se colle en haut de l'écran quand elle l'atteint. Elle garde sa place dans la page : rien ne passe dessous au chargement.z-index: 10et un fond blanc : le contenu passe sous la barre, pas au travers.scroll-margin-top: 4remsur les sections : quand on clique sur un lien, le navigateur s'arrête un peu avant la section. Sans lui, son titre finirait caché sous la barre.scroll-behavior: smooth: le défilement vers une section est fluide, sauf pour les personnes qui ont demandé moins d'animations dans leur système (prefers-reduced-motion).flex-wrap: wrap: sur un petit écran, les liens passent sous le logo au lieu de déborder.
Et position: fixed ?
Avec position: fixed, la barre sort du flux de la page : le début du contenu passe dessous, et il faut ajouter à la main un padding-top de la hauteur de la barre, à ajuster dès qu'elle change. sticky évite ce bricolage et fonctionne dans tous les navigateurs actuels. Seule condition : aucun parent de la barre ne doit avoir overflow: hidden, sinon elle ne colle plus.
3. Le JavaScript : l'ombre et le lien actif
const header = document.querySelector('.site-header');
const links = document.querySelectorAll('.nav-links a');
// 1. Une ombre sous la barre dès que la page a défilé.
function updateShadow() {
header.classList.toggle('scrolled', window.scrollY > 0);
}
window.addEventListener('scroll', updateShadow, { passive: true });
updateShadow();
// 2. Le lien de la section en cours de lecture est mis en évidence.
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (!entry.isIntersecting) {
return;
}
links.forEach((link) => {
if (link.getAttribute('href') === '#' + entry.target.id) {
link.setAttribute('aria-current', 'true');
} else {
link.removeAttribute('aria-current');
}
});
});
}, { rootMargin: '-50% 0px -50% 0px' });
document.querySelectorAll('main section').forEach((section) => observer.observe(section));
L'ombre. À chaque défilement, classList.toggle('scrolled', window.scrollY > 0) ajoute la classe si la page a défilé et l'enlève sinon. L'option { passive: true } promet au navigateur que le code n'empêchera pas le défilement : il reste fluide.
Le lien actif. Plutôt que de calculer la position de chaque section à chaque pixel, on laisse IntersectionObserver prévenir le code. Avec rootMargin: '-50% 0px -50% 0px', la zone observée se réduit à une ligne au milieu de l'écran : la section qui croise cette ligne est celle qu'on lit. Son lien reçoit aria-current="true", qui sert à la fois au style et aux lecteurs d'écran.
4. Tester
- Fais défiler : la barre reste en haut et une ombre apparaît.
- Clique sur « Projets » : la page défile en douceur, le titre de la section reste visible sous la barre et le lien est souligné.
- Réduis la fenêtre à la largeur d'un téléphone : les liens passent à la ligne, sans défilement horizontal.
Pour aller plus loin
- Un menu burger sur mobile : quand les liens deviennent trop nombreux, cache-les derrière un bouton. C'est l'objet du tutoriel menu de navigation responsive.
- Cacher la barre en descendant : compare
window.scrollYà sa valeur précédente, et fais glisser la barre vers le haut (transform: translateY(-100%)) quand on descend, pour la faire revenir dès qu'on remonte. - Un lien d'évitement : un lien « Aller au contenu » en tout début de page, visible au focus, permet aux personnes qui naviguent au clavier de sauter le menu.
Tu as construit un portfolio avec cette barre ? Partage-le dans la vitrine des lecteurs.