Coder un sélecteur de plage de dates en CSS et JavaScript est une compétence essentielle pour moderniser vos interfaces web. Dans ce tutoriel, vous allez apprendre à concevoir un calendrier interactif, élégant et responsive, capable de gérer une sélection de dates avec fluidité, sans utiliser de librairie externe. Un guide simple et complet pour maîtriser pas à pas la création d’un date range picker performant.
- Coder un sélecteur de plage de dates moderne et fonctionnel sans dépendre de librairies externes
- Améliorer l’expérience utilisateur avec des interactions fluides, des animations et une interface soignée
- Acquérir des bases solides en JavaScript pour manipuler des dates à travers un cas concret réutilisable
Vous souhaitez permettre à vos utilisateurs de sélectionner une plage de dates sur votre site web, comme sur Booking ou Airbnb ? On va créer votre propre date range picker en HTML, CSS et JavaScript, sans aucune librairie externe. Dans ce tutoriel frontend, vous allez apprendre à construire ce fameux sélecteur de plage de dates élégant, moderne et interactif, en partant d’un exemple complet que vous pourrez réutiliser dans vos projets.
- Comprendre le projet global
- Structure HTML : la base du calendrier
- Le design CSS : modernité et simplicité
- Les interactions visuelles
- JavaScript : le cerveau du calendrier
- Bloquer les dates passées
- Sélection de la plage de dates
- Mise en évidence de la plage
- Affichage du résultat
- Navigation entre les mois
- Responsive design
- Ce que vous avez appris : Le résumé
- Le code complet HTML + CSS + JS
Comprendre le projet global
Avant de plonger dans le code, prenons 30 secondes pour comprendre ce que vous allez créer.
Votre composant permet de :
- Naviguer entre les mois
- Sélectionner une date de début
- Sélectionner une date de fin
- Visualiser la plage sélectionnée
- Bloquer les dates passées
- Offrir une interface fluide avec animations

Autrement dit, vous recréez un mini calendrier intelligent.
Voici le résultat final :
Structure HTML : la base du calendrier
Commençons par la structure HTML. Rien de compliqué, mais chaque bloc a un rôle précis.
<div class="datepicker">
<div class="header">
<button id="prev">◀</button>
<h3 id="monthYear"></h3>
<button id="next">▶</button>
</div>
<div class="days">
<div>L</div><div>M</div><div>M</div><div>J</div><div>V</div><div>S</div><div>D</div>
</div>
<div class="dates" id="dates"></div>
<div class="footer" id="output">
Sélectionnez une plage de dates
</div>
</div>
Explication simple du code HTML :
.header: navigation entre les mois.days: affichage des jours de la semaine#dates: les jours du mois générés en JS#output: affichage du résultat sélectionné
Vous avez donc une structure vide… et c’est JavaScript qui va tout animer.
Le design CSS : modernité et simplicité
Ici, vous avez utilisé une approche très propre avec des variables CSS :
:root {
--primary: #4f46e5;
--bg: #f9fafb;
--text: #1f2937;
--muted: #9ca3af;
--range: #e0e7ff;
}
Pourquoi c’est utile ?
Parce que vous pouvez changer tout le design en quelques secondes. Par exemple :
- changer la couleur principale
- adapter au dark mode
- harmoniser avec votre charte graphique
Le conteneur principal
.datepicker {
background: var(--white);
padding: 20px;
border-radius: 16px;
box-shadow: 0 10px 30px rgba(0,0,0,0.05);
}
Résultat : un effet carte moderne type UI SaaS.
Les interactions visuelles
C’est là que ça devient intéressant.
Effet au survol
.date:hover {
background: var(--range);
transform: scale(1.05);
}
Vous donnez une sensation de vie à votre interface. Sans ça, votre calendrier serait… triste.
Animation d’apparition
@keyframes fadeSlide {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
Chaque changement de mois est fluide. C’est un détail… mais c’est ce qui fait la différence.
JavaScript : le cerveau du calendrier
Maintenant, passons au cœur du système.
Variables principales
let currentDate = new Date();
let startDate = null;
let endDate = null;
currentDate: mois affichéstartDate: début sélectionendDate: fin sélection
Génération du calendrier
La fonction clé :
function renderCalendar(date) {
Ce qu’elle fait
- Vide le calendrier
- Calcule :
- le premier jour du mois
- le nombre de jours
- Génère chaque case jour
- Applique les styles (sélection, range, désactivé)
Calcul du nombre de jours
const daysInMonth = new Date(year, month + 1, 0).getDate();
Astuce JavaScript :
- jour 0 du mois suivant = dernier jour du mois actuel
Oui, c’est un hack… mais un hack officiel 😄
Des formations informatique pour tous !
Débutant ou curieux ? Apprenez le développement web, le référencement, le webmarketing, la bureautique, à maîtriser vos appareils Apple et bien plus encore...
Formateur indépendant, professionnel du web depuis 2006, je vous accompagne pas à pas et en cours particulier, que vous soyez débutant ou que vous souhaitiez progresser. En visio, à votre rythme, et toujours avec pédagogie.
Découvrez mes formations Qui suis-je ?Bloquer les dates passées
if (fullDate < today) {
div.classList.add("disabled");
}
Pourquoi c’est important ?
- Empêcher des réservations invalides
- Éviter les erreurs utilisateur
- Améliorer l’expérience
Sélection de la plage de dates
Voici la logique :
function selectDate(date) {
Cas 1 : aucune date sélectionnée
- On définit la date de début
Cas 2 : une date déjà choisie
- On définit la fin
Cas 3 : l’utilisateur clique avant la date de début
- On inverse automatiquement
Résultat : Une UX ultra fluide !
Mise en évidence de la plage
if (startDate && endDate && fullDate > startDate && fullDate < endDate) {
div.classList.add("in-range");
}
Effet visuel
- Début : couleur principale
- Fin : couleur principale
- Entre les deux : couleur douce
C’est exactement le comportement attendu par les utilisateurs.
Affichage du résultat
output.textContent = `${formatDate(startDate)} → ${formatDate(endDate)}`;
Format français
date.toLocaleDateString("fr-FR");
Simple, efficace, localisé.
Navigation entre les mois
currentDate.setMonth(currentDate.getMonth() + 1);
JavaScript gère automatiquement :
- changement d’année
- mois négatifs
- transitions
Responsive design
@media (max-width: 400px)
Votre calendrier reste utilisable sur mobile grâce aux media query en CSS.
Et ça, croyez-moi, c’est non négociable aujourd’hui.
Ce que vous avez appris : Le résumé
En construisant ce projet, vous avez manipulé :
- Le DOM en JavaScript
- Les dates en JS (pas simple !)
- Les événements utilisateur
- Les animations CSS
- L’UX design
- La logique conditionnelle
Autrement dit… vous avez fait un vrai projet pro.
Le code complet HTML + CSS + JS
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Date Range Picker</title>
<style>
:root {
--primary: #4f46e5;
--bg: #f9fafb;
--text: #1f2937;
--muted: #9ca3af;
--range: #e0e7ff;
--white: #ffffff;
}
body {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
background: var(--bg);
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
/* Container */
.datepicker {
background: var(--white);
padding: 20px;
border-radius: 16px;
box-shadow: 0 10px 30px rgba(0,0,0,0.05);
width: 320px;
max-width: 95%;
}
/* Header */
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.header button {
background: none;
border: none;
font-size: 18px;
cursor: pointer;
color: var(--text);
transition: transform 0.2s ease;
}
.header button:hover {
transform: scale(1.2);
}
.header h3 {
margin: 0;
font-size: 16px;
}
/* Days */
.days, .dates {
display: grid;
grid-template-columns: repeat(7, 1fr);
text-align: center;
}
.days div {
font-size: 12px;
color: var(--muted);
margin-bottom: 5px;
}
/* Animation container */
.dates {
animation: fadeSlide 0.3s ease;
}
@keyframes fadeSlide {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Date cells */
.date {
padding: 10px;
margin: 2px;
border-radius: 8px;
cursor: pointer;
transition: all 0.2s ease;
}
.date:hover {
background: var(--range);
transform: scale(1.05);
}
.selected {
background: var(--primary);
color: white;
}
.in-range {
background: var(--range);
}
/* Disabled past dates */
.disabled {
color: #d1d5db;
pointer-events: none;
background: transparent;
}
/* Footer */
.footer {
margin-top: 15px;
font-size: 14px;
text-align: center;
color: var(--text);
}
/* Responsive */
@media (max-width: 400px) {
.datepicker {
padding: 15px;
}
.date {
padding: 8px;
}
}
</style>
</head>
<body>
<div class="datepicker">
<div class="header">
<button id="prev">◀</button>
<h3 id="monthYear"></h3>
<button id="next">▶</button>
</div>
<div class="days">
<div>L</div><div>M</div><div>M</div><div>J</div><div>V</div><div>S</div><div>D</div>
</div>
<div class="dates" id="dates"></div>
<div class="footer" id="output">
Sélectionnez une plage de dates
</div>
</div>
<script>
const datesContainer = document.getElementById("dates");
const monthYear = document.getElementById("monthYear");
const output = document.getElementById("output");
let currentDate = new Date();
let startDate = null;
let endDate = null;
// Aujourd'hui sans heure
const today = new Date();
today.setHours(0,0,0,0);
function renderCalendar(date) {
datesContainer.innerHTML = "";
const year = date.getFullYear();
const month = date.getMonth();
const firstDay = new Date(year, month, 1).getDay();
const daysInMonth = new Date(year, month + 1, 0).getDate();
const monthNames = ["Janvier","Février","Mars","Avril","Mai","Juin",
"Juillet","Août","Septembre","Octobre","Novembre","Décembre"];
monthYear.textContent = `${monthNames[month]} ${year}`;
let startOffset = (firstDay === 0 ? 6 : firstDay - 1);
for (let i = 0; i < startOffset; i++) {
datesContainer.innerHTML += `<div></div>`;
}
for (let day = 1; day <= daysInMonth; day++) {
const fullDate = new Date(year, month, day);
fullDate.setHours(0,0,0,0);
const div = document.createElement("div");
div.classList.add("date");
div.textContent = day;
// Bloquer dates passées
if (fullDate < today) {
div.classList.add("disabled");
} else {
div.addEventListener("click", () => selectDate(fullDate));
}
if (startDate && sameDate(fullDate, startDate)) {
div.classList.add("selected");
}
if (endDate && sameDate(fullDate, endDate)) {
div.classList.add("selected");
}
if (startDate && endDate && fullDate > startDate && fullDate < endDate) {
div.classList.add("in-range");
}
datesContainer.appendChild(div);
}
}
function selectDate(date) {
if (!startDate || (startDate && endDate)) {
startDate = date;
endDate = null;
} else {
if (date < startDate) {
endDate = startDate;
startDate = date;
} else {
endDate = date;
}
}
updateOutput();
renderCalendar(currentDate);
}
function sameDate(d1, d2) {
return d1.toDateString() === d2.toDateString();
}
function updateOutput() {
if (startDate && endDate) {
output.textContent = `${formatDate(startDate)} → ${formatDate(endDate)}`;
} else if (startDate) {
output.textContent = `Début : ${formatDate(startDate)}`;
}
}
function formatDate(date) {
return date.toLocaleDateString("fr-FR");
}
document.getElementById("prev").onclick = () => {
currentDate.setMonth(currentDate.getMonth() - 1);
renderCalendar(currentDate);
};
document.getElementById("next").onclick = () => {
currentDate.setMonth(currentDate.getMonth() + 1);
renderCalendar(currentDate);
};
renderCalendar(currentDate);
</script>
</body>
</html>
Aller plus loin : idées d'amélioration
Si vous voulez pousser ce composant encore plus loin :
- Ajouter un bouton "Réinitialiser"
- Bloquer certaines plages (week-ends, jours fériés)
- Ajouter une heure (datetime picker)
- Synchroniser avec une API (réservation)
- Ajouter un mode sombre
- Etc...
Vous venez de créer un sélecteur de plage de dates moderne en CSS et JavaScript, entièrement personnalisé, sans dépendance externe. Et surtout, vous avez compris comment il fonctionne.
Ce type de composant est un excellent exercice : il mélange logique, design et interaction utilisateur. C’est exactement ce qu’on vous demandera dans des projets réels.
La prochaine étape ? L’intégrer dans un vrai projet !
Et entre nous… quand vous commencez à coder ce genre de composant sans copier-coller, vous n’êtes plus débutant.

Fondateur de l’agence Créa-troyes, affiliée France Num
Intervenant en Freelance.
Contactez-moi
