Développement Web

Tutoriel : Créer un site one-page avec scroll fluide

14 min de lecture

Prérequis

  • Niveau : bases HTML/CSS/JS, manipulation du DOM (cf. manipuler le DOM).
  • Outils : VS Code + Live Server, navigateur moderne (DevTools mobile pour tester le hamburger).
  • Temps estimé : 2 h.

Pourquoi un site one-page ?

Les sites one-page sont parfaits pour les landing pages, lancements de produit et présentations de service. Pas de menu compliqué, l’utilisateur scrolle et tout son parcours est visible. Avec quelques bonnes pratiques (lazy loading, animations légères, navigation fixe), vous obtenez un site rapide et convaincant.

Le site one-page : idéal pour présenter un service

Un site one-page regroupe tout sur une seule page avec des sections. La navigation par scroll fluide (smooth scroll) guide l’utilisateur d’une section à l’autre. Parfait pour les landing pages, portfolios et présentations de services.

Structure HTML

Voici la mise en pratique pour Structure HTML. Le bloc ci-dessous est copiable directement dans votre projet, lisible ligne par ligne. Lisez-le une première fois en survol pour repérer la structure générale, puis adaptez les noms de variables, identifiants et valeurs à votre contexte avant de l’exécuter en local.

<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>MonService - Solution digitale au Sénégal</title>
</head>
<body>

<!-- Navigation fixe -->
<nav id="navbar">
    <div class="logo">MonService</div>
    <ul>
        <li><a href="#accueil">Accueil</a></li>
        <li><a href="#services">Services</a></li>
        <li><a href="#portfolio">Portfolio</a></li>
        <li><a href="#tarifs">Tarifs</a></li>
        <li><a href="#contact">Contact</a></li>
    </ul>
</nav>

<!-- Sections -->
<section id="accueil" class="section hero">
    <h1>Développez votre présence digitale</h1>
    <p>Sites web, applications et marketing digital à Dakar</p>
    <a href="#contact" class="btn">Demander un devis</a>
</section>

<section id="services" class="section">...</section>
<section id="portfolio" class="section">...</section>
<section id="tarifs" class="section">...</section>
<section id="contact" class="section">...</section>

</body>
</html>

CSS : Navigation fixe et sections plein écran

Voici la mise en pratique pour CSS : Navigation fixe et sections plein écran. Le bloc ci-dessous est copiable directement dans votre projet, lisible ligne par ligne. Lisez-le une première fois en survol pour repérer la structure générale, puis adaptez les noms de variables, identifiants et valeurs à votre contexte avant de l’exécuter en local.

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

/* Scroll fluide natif CSS */
html { scroll-behavior: smooth; }

/* Navigation fixe */
#navbar {
    position: fixed;
    top: 0;
    width: 100%;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 15px 40px;
    background: rgba(255, 255, 255, 0.95);
    backdrop-filter: blur(10px);
    box-shadow: 0 2px 20px rgba(0,0,0,0.1);
    z-index: 1000;
    transition: all 0.3s;
}

/* Navbar qui change au scroll */
#navbar.scrolled {
    padding: 10px 40px;
    background: rgba(255, 255, 255, 0.98);
}

.logo { font-size: 24px; font-weight: 700; color: #667eea; }

#navbar ul { list-style: none; display: flex; gap: 30px; }

#navbar a {
    text-decoration: none;
    color: #333;
    font-weight: 500;
    position: relative;
}

/* Soulignement animé au survol */
#navbar a::after {
    content: "";
    position: absolute;
    bottom: -5px;
    left: 0;
    width: 0;
    height: 2px;
    background: #667eea;
    transition: width 0.3s;
}
#navbar a:hover::after,
#navbar a.actif::after { width: 100%; }

/* Sections plein écran */
.section {
    min-height: 100vh;
    padding: 100px 40px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
}

.section:nth-child(even) { background: #f8f9fa; }

/* Hero */
.hero {
    background: linear-gradient(135deg, #667eea, #764ba2);
    color: white;
    text-align: center;
}
.hero h1 { font-size: 48px; margin-bottom: 20px; }
.hero p { font-size: 20px; opacity: 0.9; margin-bottom: 30px; }

.btn {
    display: inline-block;
    padding: 14px 35px;
    background: white;
    color: #667eea;
    border-radius: 30px;
    text-decoration: none;
    font-weight: 600;
    transition: transform 0.3s;
}
.btn:hover { transform: translateY(-3px); }

JavaScript : Navigation active et effets scroll

Voici la mise en pratique pour JavaScript : Navigation active et effets scroll. Le bloc ci-dessous est copiable directement dans votre projet, lisible ligne par ligne. Lisez-le une première fois en survol pour repérer la structure générale, puis adaptez les noms de variables, identifiants et valeurs à votre contexte avant de l’exécuter en local.

// 1. Navbar qui change au scroll
const navbar = document.getElementById('navbar');
window.addEventListener('scroll', () => {
    navbar.classList.toggle('scrolled', window.scrollY > 50);
});

// 2. Lien actif selon la section visible
const sections = document.querySelectorAll('.section');
const navLinks = document.querySelectorAll('#navbar a');

const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            navLinks.forEach(link => {
                link.classList.remove('actif');
                if (link.getAttribute('href') === '#' + entry.target.id) {
                    link.classList.add('actif');
                }
            });
        }
    });
}, { threshold: 0.5 });

sections.forEach(section => observer.observe(section));

// 3. Animation d'apparition au scroll
const animElements = document.querySelectorAll('.anim-scroll');

const animObserver = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            entry.target.classList.add('visible');
        }
    });
}, { threshold: 0.1 });

animElements.forEach(el => animObserver.observe(el));

CSS des animations au scroll

Voici la mise en pratique pour CSS des animations au scroll. Le bloc ci-dessous est copiable directement dans votre projet, lisible ligne par ligne. Lisez-le une première fois en survol pour repérer la structure générale, puis adaptez les noms de variables, identifiants et valeurs à votre contexte avant de l’exécuter en local.

/* Éléments cachés par défaut */
.anim-scroll {
    opacity: 0;
    transform: translateY(30px);
    transition: opacity 0.6s ease, transform 0.6s ease;
}

/* Apparition quand visible */
.anim-scroll.visible {
    opacity: 1;
    transform: translateY(0);
}

/* Décalage pour effet cascade */
.anim-scroll:nth-child(2) { transition-delay: 0.1s; }
.anim-scroll:nth-child(3) { transition-delay: 0.2s; }
.anim-scroll:nth-child(4) { transition-delay: 0.3s; }

Bouton « Retour en haut »

Voici la mise en pratique pour Bouton Retour en haut. Le bloc ci-dessous est copiable directement dans votre projet, lisible ligne par ligne. Lisez-le une première fois en survol pour repérer la structure générale, puis adaptez les noms de variables, identifiants et valeurs à votre contexte avant de l’exécuter en local.

<button id="back-to-top" title="Retour en haut">↑</button>

<style>
#back-to-top {
    position: fixed;
    bottom: 30px;
    right: 30px;
    width: 45px;
    height: 45px;
    border-radius: 50%;
    background: #667eea;
    color: white;
    border: none;
    font-size: 20px;
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.3s, transform 0.3s;
    transform: translateY(20px);
    z-index: 999;
}
#back-to-top.visible {
    opacity: 1;
    transform: translateY(0);
}
</style>

<script>
const btnTop = document.getElementById('back-to-top');
window.addEventListener('scroll', () => {
    btnTop.classList.toggle('visible', window.scrollY > 500);
});
btnTop.addEventListener('click', () => {
    window.scrollTo({ top: 0, behavior: 'smooth' });
});
</script>

Menu hamburger responsive

Voici la mise en pratique pour Menu hamburger responsive. Le bloc ci-dessous est copiable directement dans votre projet, lisible ligne par ligne. Lisez-le une première fois en survol pour repérer la structure générale, puis adaptez les noms de variables, identifiants et valeurs à votre contexte avant de l’exécuter en local.

/* Ajouter dans le HTML du navbar */
<button class="hamburger" id="menu-btn">☰</button>

<style>
.hamburger { display: none; background: none; border: none; font-size: 28px; cursor: pointer; }

@media (max-width: 768px) {
    .hamburger { display: block; }
    #navbar ul {
        position: fixed;
        top: 60px;
        right: -100%;
        width: 250px;
        height: 100vh;
        background: white;
        flex-direction: column;
        padding: 30px;
        transition: right 0.3s;
        box-shadow: -5px 0 15px rgba(0,0,0,0.1);
    }
    #navbar ul.open { right: 0; }
    .hero h1 { font-size: 28px; }
}
</style>

<script>
document.getElementById('menu-btn').addEventListener('click', () => {
    document.querySelector('#navbar ul').classList.toggle('open');
});
// Fermer le menu au clic sur un lien
navLinks.forEach(link => {
    link.addEventListener('click', () => {
        document.querySelector('#navbar ul').classList.remove('open');
    });
});
</script>

Erreurs fréquentes

Le scroll fluide ne fonctionne pas

Cause : oubli de html { scroll-behavior: smooth; }, ou interférence avec une bibliothèque qui désactive le smooth scroll.
Solution : vérifiez la règle CSS, et que votre site n’utilise pas overflow: hidden sur body/html.

Le lien actif ne se met pas à jour au scroll

Cause : seuil threshold trop élevé sur l’IntersectionObserver, ou plusieurs sections détectées simultanément.
Solution : ajustez threshold à 0.4-0.6, et utilisez rootMargin pour décaler la zone de détection.

Animations qui se déclenchent trop tôt

Cause : threshold: 0.1 sur des grands éléments → l’animation démarre dès qu’un pixel est visible.
Solution : augmentez threshold ou utilisez rootMargin: '0px 0px -100px 0px' pour attendre que l’élément soit bien dans la vue.

Performance dégradée par scroll + backdrop-filter

Cause : backdrop-filter est gourmand sur certains GPU (notamment Safari).
Solution : mettez plutôt un fond opaque rgba(255,255,255,0.95) sans flou, ou désactivez le filtre sous prefers-reduced-motion.

Exercice pratique

🎯 Défi : Créez votre landing page one-page

  1. 5 sections : Hero, Services (3 cartes), Portfolio (grille d’images), Tarifs (3 plans), Contact (formulaire)
  2. Navigation fixe avec lien actif au scroll
  3. Animations d’apparition au scroll
  4. Bouton retour en haut
  5. Menu hamburger responsive
  6. Bonus : ajoutez un compteur animé dans la section Hero

scroll-snap CSS sans JavaScript

Voici la mise en pratique pour scroll-snap CSS sans JavaScript. Le bloc ci-dessous est copiable directement dans votre projet, lisible ligne par ligne. Lisez-le une première fois en survol pour repérer la structure générale, puis adaptez les noms de variables, identifiants et valeurs à votre contexte avant de l’exécuter en local.

html {
  scroll-snap-type: y mandatory;
  scroll-behavior: smooth;
}
section {
  scroll-snap-align: start;
  min-height: 100vh;
}

Le navigateur va automatiquement aligner le haut de chaque section avec le haut de la fenêtre. Support complet Chrome/Firefox/Safari/Edge depuis 2020.

Animer l’apparition avec Intersection Observer

Voici la mise en pratique pour Animer l’apparition avec Intersection Observer. Le bloc ci-dessous est copiable directement dans votre projet, lisible ligne par ligne. Lisez-le une première fois en survol pour repérer la structure générale, puis adaptez les noms de variables, identifiants et valeurs à votre contexte avant de l’exécuter en local.

const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      entry.target.classList.add('visible');
      observer.unobserve(entry.target);
    }
  });
}, { threshold: 0.2 });
document.querySelectorAll('.fade-in').forEach((el) => observer.observe(el));
.fade-in { opacity: 0; transform: translateY(24px); transition: opacity .6s, transform .6s; }
.fade-in.visible { opacity: 1; transform: none; }

Respecter prefers-reduced-motion

Voici la mise en pratique pour Respecter prefers-reduced-motion. Le bloc ci-dessous est copiable directement dans votre projet, lisible ligne par ligne. Lisez-le une première fois en survol pour repérer la structure générale, puis adaptez les noms de variables, identifiants et valeurs à votre contexte avant de l’exécuter en local.

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .fade-in { opacity: 1; transform: none; transition: none; }
}

WCAG 2.1 critère 2.3.3 le formalise. Testez en activant l’option : Windows Paramètres > Accessibilité > Effets visuels > Animations OFF.

Navigation par ancres avec scroll-margin-top

Voici la mise en pratique pour Navigation par ancres avec scroll-margin-top. Le bloc ci-dessous est copiable directement dans votre projet, lisible ligne par ligne. Lisez-le une première fois en survol pour repérer la structure générale, puis adaptez les noms de variables, identifiants et valeurs à votre contexte avant de l’exécuter en local.

section { scroll-margin-top: 80px; }

Si votre menu fixe fait 80 px, cette propriété réserve 80 px au-dessus de la cible quand on saute via une ancre #contact. Marquez aussi le lien actif au scroll via Intersection Observer.

Performance : un one-page n’est pas une excuse pour 8 Mo

  • Une seule famille de police, deux graisses max, hébergée localement en woff2.
  • Vidéo Hero : MP4 H.264, < 2 Mo, avec poster image qui s’affiche tant que la vidéo n’est pas chargée.
  • Lazy-load les images du bas via loading='lazy'.
  • Pas de framework JS pour un one-page statique : du HTML, du CSS, 30 lignes de JS suffisent.

SEO : un seul URL, plusieurs intentions

Choisissez l’intention la plus large (« agence web Dakar », « freelance design Abidjan ») et structurez en H2 (Services, Réalisations, À propos, Contact). Ajoutez un schema LocalBusiness dans un bloc JSON-LD avec adresse, ville, téléphone, horaires : Google peut alors afficher votre site dans le panneau de connaissances local.

Adaptation au contexte ouest-africain : performance, équipe, marché

Pour un développeur ou une PME basée à Dakar, Abidjan, Bamako, Cotonou, Lomé, Ouagadougou, Niamey ou Conakry qui livre des sites web ou applications custom, trois adaptations pèsent sur le succès des projets. Premièrement, la connectivité 4G inégale impose de réduire le poids des pages au strict nécessaire. Deuxièmement, le profil typique des développeurs disponibles localement est majoritairement formé sur du JavaScript moderne et du PHP. Troisièmement, le coût en FCFA des services cloud doit être anticipé : Hetzner CX22 à 4 500 FCFA/mois reste imbattable pour démarrer, Cloudflare Pages gratuit pour les sites statiques, Backblaze B2 à 6 USD/TB/mois pour les sauvegardes.

Tester sur appareils réels

Plus important que tous les outils synthétiques, tester son site sur un Android d’entrée de gamme (Galaxy A03 à 200 EUR neuf, Tecno Spark, Itel A60) avec une connexion 4G locale dégradée donne le seul verdict qui compte. Pour étoffer le tableau, voir le guide media queries responsive.

Pratiques avancées et outils complémentaires

Au-delà des patterns présentés, plusieurs outils complètent une maîtrise sérieuse. Premier axe : automatiser la qualité via une pipeline CI (GitHub Actions, GitLab CI) qui exécute tests, linting et audit avant chaque déploiement. Deuxième axe : monitorer en production avec Sentry pour les erreurs JavaScript ou New Relic pour les performances applicatives. Troisième axe : documenter les décisions importantes dans un dossier docs/adr/ avec format simple (contexte, décision, conséquences).

Ressources francophones pour approfondir

Plusieurs ressources gratuites en français permettent de monter en compétence rapidement. MDN Web Docs reste la documentation de référence. FreeCodeCamp propose des parcours de 300+ heures. Grafikart.fr offre des centaines de tutoriels vidéos en français. Pour la pratique, contribuer à un projet open source via GitHub est l’investissement le plus payant à moyen terme.

Pratiques avancées et outils complémentaires

Au-delà des patterns présentés, plusieurs outils complètent une maîtrise sérieuse. Premier axe : automatiser la qualité via une pipeline CI (GitHub Actions, GitLab CI) qui exécute tests, linting et audit avant chaque déploiement. Deuxième axe : monitorer en production avec Sentry pour les erreurs JavaScript ou New Relic pour les performances applicatives. Troisième axe : documenter les décisions importantes dans un dossier docs/adr/ avec format simple (contexte, décision, conséquences).

Ressources francophones pour approfondir

Plusieurs ressources gratuites en français permettent de monter en compétence rapidement. MDN Web Docs reste la documentation de référence. FreeCodeCamp propose des parcours de 300+ heures. Grafikart.fr offre des centaines de tutoriels vidéos en français. Pour la pratique, contribuer à un projet open source via GitHub est l’investissement le plus payant à moyen terme.

Erreurs courantes à éviter en production

Trois patterns reviennent dans les projets web mal exécutés et coûtent cher à corriger plus tard. Premier pattern : copier-coller de code Stack Overflow sans comprendre le contexte d’origine. Deuxième pattern : ignorer les warnings de la console — chaque warning est un signal qui mérite d’être lu. Troisième pattern : ne pas tester sur de vrais appareils. DevTools simule mais ne remplace pas un test physique sur un Android d’entrée de gamme avec une 4G dégradée. Documenter chaque décision technique majeure dans un fichier ADR prend dix minutes et fait gagner des heures lors d’un incident ou d’un audit.

Erreurs courantes à éviter en production

Trois patterns reviennent dans les projets web mal exécutés et coûtent cher à corriger plus tard. Premier pattern : copier-coller de code Stack Overflow sans comprendre le contexte d’origine. Deuxième pattern : ignorer les warnings de la console — chaque warning est un signal qui mérite d’être lu. Troisième pattern : ne pas tester sur de vrais appareils. DevTools simule mais ne remplace pas un test physique sur un Android d’entrée de gamme avec une 4G dégradée. Documenter chaque décision technique majeure dans un fichier ADR prend dix minutes et fait gagner des heures lors d’un incident ou d’un audit.

Pour les équipes qui veulent industrialiser leur production de sites professionnels, formaliser un design system partagé entre projets accélère la livraison et garantit une cohérence visuelle de marque qui se voit immédiatement.

Pour creuser ce sujet

Partager