CSS Responsive

Créez des interfaces qui s’adaptent parfaitement à tous les appareils. Mobile, tablette, bureau : une seule base de code, tous les écrans.

Pourquoi le responsive design

Un même site est lu sur un téléphone tenu d’une main et sur un écran de bureau. Les contraintes ne sont pas les mêmes — la feuille de style doit le savoir.

Parts de trafic mondial relevées en juillet 2026 (StatCounter).

Mobile

53 % du trafic web

Contraintes

  • Écran petit (320 px à 480 px)
  • Navigation tactile
  • Bande passante limitée
  • Performance critique
Le contenu prioritaire d’abord, le reste empilé dessous.

Tablette

1,5 % du trafic

Spécificités

  • Écran moyen (768 px à 1024 px)
  • Portrait et paysage
  • Hybride tactile et clavier
  • Expérience intermédiaire
Entre 768 px et 1024 px, la mise en page est hybride.

Bureau

46 % du trafic

Avantages

  • Grand écran (1200 px et plus)
  • Navigation précise
  • Multitâche possible
  • Contenu riche
Au-delà de 1200 px, la mise en page est complète.

Mobile first ou desktop first

Mobile first — recommandé

css

/* Base : Mobile (320px+) */
.container {
    padding: 1rem;
    font-size: 16px;
}

/* Tablette (768px+) */
@media (min-width: 768px) {
    .container {
        padding: 2rem;
        font-size: 18px;
    }
}

/* Desktop (1200px+) */
@media (min-width: 1200px) {
    .container {
        padding: 3rem;
        font-size: 20px;
    }
}

  • On part du plus contraint : rien n’est superflu
  • Les media queries n’ajoutent que ce qui manque
  • Le mobile ne charge pas les règles du bureau

Desktop first

css

/* Base : Desktop (1200px+) */
.container {
    padding: 3rem;
    font-size: 20px;
}

/* Tablette (max 1199px) */
@media (max-width: 1199px) {
    .container {
        padding: 2rem;
        font-size: 18px;
    }
}

/* Mobile (max 767px) */
@media (max-width: 767px) {
    .container {
        padding: 1rem;
        font-size: 16px;
    }
}

  • On part du plus riche, puis on retire
  • Le mobile charge des règles qu’il annule ensuite
  • À réserver aux sites existants qu’on adapte

Les media queries, le cœur du responsive

Une media query pose une condition sur l’appareil. Tant qu’elle est vraie, ses règles s’appliquent ; sinon elles sont ignorées.

Simuler un point de rupture

largeur simulée

☰ Navigation Mobile

Contenu Principal

Optimisé pour mobile

Sidebar (cachée ou en bas)

Header

Navigation complète

Contenu Principal

Article complet avec sidebar visible

Sidebar

Widgets et liens

Largeur simulée : . Media query active : @media (max-width: 768px) @media (min-width: 769px)

/* Mobile */
.layout {
    flex-direction: column;
    gap: 1rem;
}
.sidebar { order: 3; }
/* Desktop */
.layout {
    display: flex;
    gap: 2rem;
}

La largeur n’est qu’un moyen : ce qui se déclenche, c’est la media query affichée sous le cadre.

Les critères d’une media query

La largeur est le critère le plus employé, mais ce n’est pas le seul : une media query peut interroger l’orientation, la finesse de l’écran ou une préférence du lecteur.

Largeur

css

/* Largeur minimale */
@media (min-width: 768px) { }

/* Largeur maximale */
@media (max-width: 767px) { }

/* Plage de largeur */
@media (min-width: 768px) and (max-width: 1023px) { }

Autres critères

css

/* Orientation */
@media (orientation: landscape) { }
@media (orientation: portrait) { }

/* Résolution */
@media (-webkit-min-device-pixel-ratio: 2) { }

/* Préférence utilisateur */
@media (prefers-color-scheme: dark) { }

Bonnes pratiques

  • min-width pour une approche mobile-first
  • Organisez vos points de rupture par taille croissante
  • Évitez d’en multiplier : trois ou quatre suffisent
  • Testez sur de vrais appareils
  • Partez du contenu, pas de la liste des appareils
  • em et rem pour les points de rupture

Les paliers courants

Simuler une navigation responsive

largeur simulée

Largeur simulée : . Media query active : @media (max-width: 768px) @media (min-width: 769px)

/* Navigation mobile */
@media (max-width: 768px) {
    .nav-menu { display: none; }
    .hamburger { display: block; }
}
/* Navigation desktop */
.nav-menu {
    display: flex;
    gap: 2rem;
}

Sous 768 px, le menu disparaît et le hamburger prend sa place. Il s’ouvre au clic, comme sur un vrai site.

Les techniques du responsive

Les media queries posent des paliers. Les unités et les fonctions fluides, elles, évitent d’avoir à en poser.

Les unités relatives

css

/* Unités relatives */
.container {
    width: 90%;          /* Pourcentage */
    max-width: 1200px;
    padding: 2rem;       /* Basé sur root */
    margin: 1em;         /* Basé sur parent */
}

/* Unités viewport */
.hero {
    height: 100vh;       /* Hauteur écran */
    width: 100vw;        /* Largeur écran */
    font-size: 4vw;      /* Proportionnel */
}

Chaque bande porte la déclaration qui lui donne sa taille.

Laquelle choisir

  • % : largeurs de conteneurs
  • rem : textes et espacements
  • em : composants qui suivent leur contexte
  • vw et vh : sections en pleine largeur ou pleine hauteur

Les fonctions fluides

Elles calculent une valeur au lieu de la fixer : plus de palier à écrire, la taille suit d’elle-même.

clamp()

clamp(min, préféré, max) — une taille fluide, bornée des deux côtés.

css

/* Typographie fluide */
h1 {
    font-size: clamp(1.5rem, 4vw, 3rem);
}

/* Container adaptatif */
.container {
    width: clamp(300px, 90%, 1200px);
}

min() et max()

La plus petite, ou la plus grande, de deux valeurs.

css

/* Largeur maximum */
.box {
    width: min(90%, 600px);
}

/* Hauteur minimum */
.card {
    height: max(200px, 20vh);
}

/* Taille de texte minimum */
.text {
    font-size: max(16px, 1rem);
}

Container queries

Une condition sur le PARENT, et non sur la fenêtre. Support récent des navigateurs.

css

/* Conteneur queryable */
.sidebar {
    container-type: inline-size;
}

/* Query sur le container */
@container (min-width: 300px) {
    .card {
        display: flex;
    }
}

object-fit

Comment une image occupe le cadre qu’on lui donne.

css

/* Object-fit pour conteneurs */
.image-container img {
    width: 100%;
    height: 200px;
    object-fit: cover;
}

Explorer clamp() sur la taille de texte

font-size

Texte avec clamp()

font-size: ;

Élargissez ou rétrécissez la fenêtre : la taille suit, sans jamais sortir des deux bornes.

Deux mises en page adaptatives

Les mêmes techniques, appliquées à ce qu’on rencontre sur de vrais sites.

Grille de produits

auto-fill crée autant de colonnes que la place le permet, minmax garantit une largeur minimale, clamp fait respirer les espacements. Aucune media query.

css

/* Grille adaptative moderne */
.products-grid {
    display: grid;
    grid-template-columns:
        repeat(auto-fill, minmax(280px, 1fr));
    gap: clamp(1rem, 3vw, 2rem);
    padding: clamp(1rem, 4vw, 3rem);
}

.product-card {
    background: white;
    border-radius: 12px;
    padding: 1.5rem;
    box-shadow: 0 4px 6px rgba(0,0,0,0.1);
    transition: transform 0.2s ease;
}

.product-card:hover {
    transform: translateY(-4px);
}

Quatre cartes qui se réorganisent selon la largeur du cadre.

Ce qui fait le travail

  • auto-fill : autant de colonnes que la place le permet
  • minmax(280px, 1fr) : une largeur minimale garantie
  • clamp() : des espacements fluides
  • Aucune media query nécessaire

Formulaire adaptatif

Une colonne sur mobile, deux à partir de 768 px. Et un détail qui compte : font-size: max(16px, 1rem) empêche iOS de zoomer au moment de la saisie.

css

/* Layout de formulaire adaptatif */
.form-container {
    max-width: 600px;
    margin: 0 auto;
    padding: clamp(1rem, 4vw, 2rem);
}

.form-row {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1rem;
    margin-bottom: 1.5rem;
}

@media (min-width: 768px) {
    .form-row.two-cols {
        grid-template-columns: 1fr 1fr;
    }
}

.form-input {
    width: 100%;
    font-size: max(16px, 1rem);
}

Au-delà de 768 px, les champs courts se placent côte à côte.

Bonnes pratiques

  • font-size: max(16px, 1rem) évite le zoom automatique sur iOS
  • Une grille adaptative pour organiser les champs
  • clamp() pour les espacements
  • Des libellés et des indications clairs

Tableau de bord multi-écrans

Les zones nommées se redessinent d’un palier à l’autre : empilées sur mobile, réparties en trois colonnes sur bureau. Le balisage, lui, ne bouge pas.

css

/* Dashboard avec Grid Areas */
.dashboard {
    display: grid;
    gap: 1rem;
    padding: 1rem;
    min-height: 100vh;
}

/* Mobile : Stack vertical */
.dashboard {
    grid-template-areas:
        "header"
        "stats"
        "chart"
        "sidebar";
}

/* Desktop : Layout complexe */
@media (min-width: 1024px) {
    .dashboard {
        grid-template-columns: 250px 1fr 300px;
        grid-template-rows: 60px 1fr;
        grid-template-areas:
            "sidebar header stats"
            "sidebar chart stats";
    }
}

Mobile : les quatre zones empilées, dans l’ordre du code.
Bureau : la barre latérale s’étend sur deux lignes, les statistiques passent à droite.

Exercice : site e-commerce responsive

La mission

Créez un site e-commerce complet qui s’adapte parfaitement à tous les appareils avec une expérience utilisateur optimisée.

Fonctionnalités requises

  • Header responsive : logo, navigation et panier
  • Bannière hero avec appel à l’action adaptatif
  • Grille produits : 4 → 3 → 2 → 1 colonnes
  • Filtres : barre latérale sur bureau, fenêtre modale sur mobile
  • Cartes produits avec images adaptatives
  • Formulaire de contact optimisé pour le mobile
  • Footer avec colonnes adaptatives

Contraintes techniques

  • Approche mobile-first obligatoire
  • Utiliser clamp() pour la typographie fluide
  • Grid et Flexbox combinés
  • Images avec srcset et chargement différé
  • Performance : moins de 3 s sur mobile

La même page à deux paliers

Mobile (320 px – 767 px) : menu replié, produits empilés.
Bureau (1024 px et plus) : navigation dépliée, filtres à gauche, quatre produits de front.

Code de démarrage

html

<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ShopResponsive - E-commerce Adaptatif</title>
    <style>
        /* Reset et base */
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            font-family: system-ui, -apple-system, sans-serif;
            line-height: 1.6;
            color: #333;
        }
        
        /* Container responsive */
        .container {
            width: min(90%, 1200px);
            margin: 0 auto;
            padding: 0 clamp(1rem, 4vw, 2rem);
        }
        
        /* Header responsive */
        .header {
            background: white;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            position: sticky;
            top: 0;
            z-index: 100;
        }
        
        .nav {
            /* TODO: Flex layout pour header responsive */
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 1rem 0;
        }
        
        .nav-menu {
            /* TODO: Navigation qui se cache sur mobile */
            display: none;
        }
        
        .hamburger {
            /* TODO: Menu hamburger pour mobile */
            display: block;
            background: none;
            border: none;
            font-size: 1.5rem;
            cursor: pointer;
        }
        
        /* Hero section */
        .hero {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            text-align: center;
            padding: clamp(3rem, 8vw, 6rem) 0;
        }
        
        .hero h1 {
            /* TODO: Typographie fluide avec clamp */
            font-size: clamp(2rem, 5vw, 3.5rem);
            margin-bottom: 1rem;
        }
        
        .hero p {
            font-size: clamp(1rem, 2.5vw, 1.25rem);
            margin-bottom: 2rem;
            opacity: 0.9;
        }
        
        .cta-button {
            /* TODO: Bouton responsive */
            display: inline-block;
            background: white;
            color: #667eea;
            padding: clamp(0.75rem, 2vw, 1rem) clamp(1.5rem, 4vw, 2rem);
            text-decoration: none;
            border-radius: 50px;
            font-weight: bold;
            transition: transform 0.2s ease;
        }
        
        /* Grille produits */
        .products-section {
            padding: clamp(3rem, 6vw, 5rem) 0;
        }
        
        .products-grid {
            /* TODO: Grille adaptive sans media queries */
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
            gap: clamp(1rem, 3vw, 2rem);
            margin-top: 2rem;
        }
        
        .product-card {
            background: white;
            border-radius: 12px;
            overflow: hidden;
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
            transition: transform 0.2s ease;
        }
        
        .product-card:hover {
            transform: translateY(-4px);
        }
        
        .product-image {
            width: 100%;
            height: 200px;
            object-fit: cover;
        }
        
        .product-info {
            padding: 1.5rem;
        }
        
        /* Media Queries */
        @media (min-width: 768px) {
            .nav-menu {
                display: flex;
                gap: 2rem;
            }
            
            .hamburger {
                display: none;
            }
            
            .hero {
                text-align: left;
            }
            
            /* TODO: Layout desktop avec sidebar filtres */
        }
        
        @media (min-width: 1024px) {
            .main-layout {
                display: grid;
                grid-template-columns: 250px 1fr;
                gap: 2rem;
            }
            
            .filters-sidebar {
                background: #f8f9fa;
                padding: 2rem;
                border-radius: 12px;
                height: fit-content;
                position: sticky;
                top: 100px;
            }
        }
        
        /* Formulaire responsive */
        .contact-form {
            max-width: 600px;
            margin: 0 auto;
        }
        
        .form-group {
            display: grid;
            gap: 1rem;
            margin-bottom: 1.5rem;
        }
        
        .form-group.two-cols {
            grid-template-columns: 1fr;
        }
        
        @media (min-width: 768px) {
            .form-group.two-cols {
                grid-template-columns: 1fr 1fr;
            }
        }
        
        .form-input {
            width: 100%;
            padding: 0.75rem;
            font-size: max(16px, 1rem); /* Évite le zoom iOS */
            border: 2px solid #e2e8f0;
            border-radius: 8px;
            transition: border-color 0.2s ease;
        }
        
        .form-input:focus {
            outline: none;
            border-color: #667eea;
        }
        
        /* Footer responsive */
        .footer {
            background: #1a1a1a;
            color: white;
            padding: clamp(2rem, 4vw, 3rem) 0;
        }
        
        .footer-content {
            display: grid;
            gap: 2rem;
            grid-template-columns: 1fr;
        }
        
        @media (min-width: 768px) {
            .footer-content {
                grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            }
        }
    </style>
</head>
<body>
    <header class="header">
        <div class="container">
            <nav class="nav">
                <div class="logo">
                    <h1>ShopResponsive</h1>
                </div>
                <div class="nav-menu">
                    <a href="#">Accueil</a>
                    <a href="#products">Produits</a>
                    <a href="#contact">Contact</a>
                </div>
                <div class="nav-actions">
                    <button class="hamburger">☰</button>
                    <a href="#" class="cart">🛒 Panier (0)</a>
                </div>
            </nav>
        </div>
    </header>

    <section class="hero">
        <div class="container">
            <h1>Découvrez nos Produits</h1>
            <p>Une sélection unique de produits de qualité</p>
            <a href="#products" class="cta-button">Voir les Produits</a>
        </div>
    </section>

    <section id="products" class="products-section">
        <div class="container">
            <h2>Nos Produits</h2>
            <div class="main-layout">
                <aside class="filters-sidebar">
                    <h3>Filtres</h3>
                    <!-- TODO: Ajouter filtres -->
                </aside>
                <div class="products-grid">
                    <div class="product-card">
                        <img src="https://via.placeholder.com/300x200" alt="Produit" class="product-image" loading="lazy">
                        <div class="product-info">
                            <h3>Produit Exemple</h3>
                            <p>Description du produit...</p>
                            <div class="price">29,99 €</div>
                            <button>Ajouter au Panier</button>
                        </div>
                    </div>
                    <!-- TODO: Répéter pour plus de produits -->
                </div>
            </div>
        </div>
    </section>

    <footer class="footer">
        <div class="container">
            <div class="footer-content">
                <div>
                    <h4>ShopResponsive</h4>
                    <p>Votre boutique en ligne de confiance</p>
                </div>
                <div>
                    <h4>Liens Utiles</h4>
                    <ul>
                        <li><a href="#">À propos</a></li>
                        <li><a href="#">Contact</a></li>
                    </ul>
                </div>
            </div>
        </div>
    </footer>

    <script>
        // TODO: JavaScript pour menu mobile
        document.querySelector('.hamburger').addEventListener('click', function() {
            // Toggle menu mobile
        });
    </script>
</body>
</html>

Les commentaires TODO marquent ce qui reste à écrire. 294 lignes reprises telles quelles.

Le responsive est acquis

Vous savez poser des paliers, choisir des unités qui suivent l’écran et laisser les fonctions fluides faire le reste. Une seule base de code, tous les appareils.

  • 📱 Mobile first

    Commencez toujours par le mobile, puis enrichissez

  • 🔧 Fonctions CSS

    clamp(), min(), max() pour des tailles fluides

  • 🎯 Paliers

    Trois suffisent presque toujours : 768, 1024, 1200

Les autres pages CSS