Une timeline (frise chronologique) est parfaite pour raconter un parcours, l'histoire d'une entreprise ou les étapes d'un projet. Tu vas en construire une en HTML et CSS, qui alterne à gauche et à droite sur grand écran, puis ajouter en JavaScript une apparition en douceur au défilement, sans bibliothèque. Durée : environ 40 minutes.
Ce que tu vas construire
- une frise verticale avec une ligne et un point pour chaque évènement ;
- une mise en page sur une colonne sur mobile, et en alternance gauche-droite sur ordinateur ;
- des évènements qui apparaissent quand ils entrent à l'écran ;
- une animation désactivée pour les personnes qui l'ont demandé dans leur système.
Crée un dossier avec index.html, style.css et script.js.
1. Le HTML : une liste ordonnée
Une timeline est une suite d'évènements dans un ordre précis : c'est exactement ce que décrit une liste ordonnée <ol>. La balise <time> et son attribut datetime donnent aux moteurs de recherche et aux outils une date lisible par une machine.
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mon parcours</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main>
<h1>Mon parcours de développeur</h1>
<ol class="timeline">
<li class="event">
<time datetime="2023-09">Septembre 2023</time>
<h2>Premières lignes de HTML</h2>
<p>Une page perso, beaucoup de balises et une première mise en ligne.</p>
</li>
<li class="event">
<time datetime="2024-01">Janvier 2024</time>
<h2>Découverte de JavaScript</h2>
<p>Une calculatrice, une todo list, et enfin des pages qui réagissent.</p>
</li>
<li class="event">
<time datetime="2024-06">Juin 2024</time>
<h2>Premier projet en PHP</h2>
<p>Un blog avec une base de données MySQL et un espace d'administration.</p>
</li>
<li class="event">
<time datetime="2025-02">Février 2025</time>
<h2>Premier client</h2>
<p>Un site vitrine pour un artisan, livré en trois semaines.</p>
</li>
</ol>
</main>
<script src="script.js"></script>
</body>
</html>
2. Le CSS : la ligne, les points et l'alternance
main {
max-width: 48rem;
margin: 0 auto;
padding: 2rem 1rem;
font-family: system-ui, sans-serif;
}
.timeline {
position: relative;
margin: 0;
padding: 0;
list-style: none;
}
/* La ligne verticale. */
.timeline::before {
content: "";
position: absolute;
top: 0;
bottom: 0;
left: 0.5rem;
width: 2px;
background: #25737d;
}
.event {
position: relative;
margin-bottom: 2rem;
padding-left: 2.5rem;
}
/* Le point sur la ligne. */
.event::before {
content: "";
position: absolute;
top: 0.35rem;
left: 0;
width: 1rem;
height: 1rem;
border: 3px solid #25737d;
border-radius: 50%;
background: #fff;
box-sizing: border-box;
}
.event time {
font-size: 0.875rem;
font-weight: 600;
color: #25737d;
}
.event h2 {
margin: 0.25rem 0;
font-size: 1.25rem;
}
/* Sur grand écran : les évènements alternent de part et d'autre de la ligne. */
@media (min-width: 48rem) {
.timeline::before {
left: 50%;
}
.event {
width: 50%;
padding-left: 0;
padding-right: 2.5rem;
text-align: right;
}
.event::before {
left: auto;
right: -0.5rem;
}
.event:nth-child(even) {
margin-left: 50%;
padding-right: 0;
padding-left: 2.5rem;
text-align: left;
}
.event:nth-child(even)::before {
right: auto;
left: -0.5rem;
}
}
/* Apparition au défilement, seulement si le JavaScript a pu s'exécuter. */
.js .event {
opacity: 0;
transform: translateY(1.5rem);
transition: opacity 0.6s, transform 0.6s;
}
.js .event.visible {
opacity: 1;
transform: none;
}
/* Respecte le réglage « réduire les animations » du système. */
@media (prefers-reduced-motion: reduce) {
.js .event {
opacity: 1;
transform: none;
transition: none;
}
}
Toute la partie graphique vient des pseudo-éléments ::before, sans une seule image :
.timeline::beforedessine la ligne verticale, positionnée en absolu sur toute la hauteur de la liste ;.event::beforedessine un rond (border-radius: 50%) posé sur la ligne ;- à partir de 48rem de large, la ligne passe au centre, chaque évènement prend la moitié de la largeur, et
:nth-child(even)décale un évènement sur deux à droite.
Les règles .js .event cachent les évènements avant leur apparition, mais seulement si la page porte la classe js. Si le script ne se charge pas, tout reste visible.
3. Le JavaScript : apparition au défilement
// Le CSS ne cache les évènements que si cette classe est présente :
// sans JavaScript, la timeline reste lisible.
document.documentElement.classList.add('js');
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
observer.unobserve(entry.target); // L'animation ne se joue qu'une fois.
}
});
}, { threshold: 0.2 });
document.querySelectorAll('.event').forEach((event) => observer.observe(event));
IntersectionObserver prévient ton code quand un élément entre dans la zone visible. C'est bien plus efficace que d'écouter l'évènement scroll et de calculer des positions à chaque pixel. Avec threshold: 0.2, l'évènement apparaît dès que 20 % de sa hauteur est visible. unobserve() arrête ensuite de le surveiller : l'animation ne se joue qu'une fois.
4. Tester
- Fais défiler la page : les évènements apparaissent un à un.
- Réduis la fenêtre : la frise passe sur une seule colonne, la ligne à gauche.
- Dans les outils de développement, émule « prefers-reduced-motion: reduce » : tout s'affiche sans animation.
Pour aller plus loin
- Générer la timeline depuis des données : un tableau d'objets
{ date, titre, texte }et une boucle qui crée les éléments. Tu pourras ensuite les charger depuis un fichier JSON. - Des évènements dépliables : réutilise la technique du tutoriel sur la FAQ en accordéon pour afficher des détails au clic.
- Une timeline horizontale :
display: flex, un défilement horizontal etscroll-snap-typepour s'arrêter sur chaque évènement.
Pourquoi ne pas raconter ton propre parcours de développeur avec, puis le partager dans la vitrine des lecteurs ?