Retour au blog

CSS Moderne en 2025 : Container Queries, Cascade Layers et Nouveaux Sélecteurs

Salut HaWkers, le CSS a connu une révolution ces dernières années. Des fonctionnalités qui nécessitaient JavaScript ou étaient tout simplement impossibles sont maintenant natives. Si vous êtes encore bloqué sur le CSS de 2020, vous passez à côté d'outils incroyables.

Saviez-vous qu'il est désormais possible de styliser un élément en fonction de la taille de son conteneur parent, et pas seulement de la viewport ? Explorons les fonctionnalités les plus puissantes du CSS moderne.

L'État du CSS en 2025

Le CSS a plus évolué ces 3 dernières années que durant la décennie précédente. Les navigateurs modernes supportent désormais des fonctionnalités avancées avec une excellente compatibilité.

Support des Navigateurs en 2025

Fonctionnalités avec support total (95%+ des navigateurs) :

  • Container Queries
  • Cascade Layers
  • CSS Nesting
  • Sélecteur :has()
  • Subgrid
  • Fonctions couleur (oklch, color-mix)
  • Animations pilotées par le scroll
  • View transitions

Encore expérimentales :

  • CSS Anchor Positioning
  • Scroll state container queries

Container Queries

Les Container Queries sont probablement l'ajout le plus impactant au CSS depuis Flexbox et Grid. Elles permettent de styliser des éléments en fonction de la taille du conteneur parent.

Setup Basique

/* Définir le container */
.card-container {
  container-type: inline-size;
  container-name: card;
}

/* Ou en raccourci */
.card-container {
  container: card / inline-size;
}

/* Appliquer des styles basés sur le container */
@container card (min-width: 400px) {
  .card {
    display: grid;
    grid-template-columns: 150px 1fr;
    gap: 1rem;
  }

  .card-image {
    width: 150px;
    aspect-ratio: 1;
  }
}

@container card (min-width: 600px) {
  .card {
    grid-template-columns: 200px 1fr;
  }

  .card-title {
    font-size: 1.5rem;
  }
}

Composant Card Responsif

/* Container queries pour des cards vraiment responsives */
.card-wrapper {
  container: card / inline-size;
}

.card {
  display: flex;
  flex-direction: column;
  background: white;
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.card-image {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
}

.card-content {
  padding: 1rem;
}

.card-title {
  font-size: 1rem;
  font-weight: 600;
  margin-bottom: 0.5rem;
}

.card-description {
  font-size: 0.875rem;
  color: #666;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.card-meta {
  display: none;
}

/* Container moyen : layout horizontal */
@container card (min-width: 350px) {
  .card {
    flex-direction: row;
  }

  .card-image {
    width: 120px;
    aspect-ratio: 1;
  }

  .card-content {
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
}

/* Grand container : plus de détails */
@container card (min-width: 500px) {
  .card-image {
    width: 180px;
  }

  .card-title {
    font-size: 1.25rem;
  }

  .card-description {
    -webkit-line-clamp: 3;
  }

  .card-meta {
    display: flex;
    gap: 1rem;
    margin-top: 0.75rem;
    font-size: 0.75rem;
    color: #888;
  }
}

Cascade Layers

Les Cascade Layers résolvent l'un des plus grands problèmes du CSS : gérer la spécificité. Elles permettent de définir des couches de style avec une précédence contrôlée.

Définir les Layers

/* Définir l'ordre des layers (la première a la plus faible précédence) */
@layer reset, base, components, utilities;

/* Styles de reset */
@layer reset {
  *,
  *::before,
  *::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
  }

  html {
    font-size: 100%;
    -webkit-text-size-adjust: 100%;
  }
}

/* Styles de base */
@layer base {
  body {
    font-family: system-ui, -apple-system, sans-serif;
    line-height: 1.6;
    color: #333;
  }

  a {
    color: #0066cc;
    text-decoration: none;
  }

  a:hover {
    text-decoration: underline;
  }

  h1, h2, h3, h4, h5, h6 {
    line-height: 1.2;
    font-weight: 600;
  }
}

/* Composants */
@layer components {
  .button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0.75rem 1.5rem;
    font-size: 1rem;
    font-weight: 500;
    border-radius: 8px;
    border: none;
    cursor: pointer;
    transition: all 0.2s ease;
  }

  .button-primary {
    background: #0066cc;
    color: white;
  }

  .button-primary:hover {
    background: #0055aa;
  }

  .card {
    background: white;
    border-radius: 12px;
    padding: 1.5rem;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  }
}

/* Utilitaires (plus haute précédence) */
@layer utilities {
  .text-center { text-align: center; }
  .text-left { text-align: left; }
  .text-right { text-align: right; }

  .flex { display: flex; }
  .grid { display: grid; }
  .hidden { display: none; }

  .mt-1 { margin-top: 0.25rem; }
  .mt-2 { margin-top: 0.5rem; }
  .mt-4 { margin-top: 1rem; }
  .mt-8 { margin-top: 2rem; }

  .gap-1 { gap: 0.25rem; }
  .gap-2 { gap: 0.5rem; }
  .gap-4 { gap: 1rem; }
}

Importer du CSS Externe dans des Layers

/* Importer des bibliothèques externes dans des layers spécifiques */
@import url('https://unpkg.com/normalize.css') layer(reset);
@import url('./vendor/library.css') layer(vendor);

/* Définir l'ordre incluant vendor */
@layer reset, vendor, base, components, utilities;

/* Vos styles dans components vont écraser vendor */
@layer components {
  /* Personnalisations qui écrasent la bibliothèque */
  .library-component {
    border-radius: 8px; /* Écrase l'original */
  }
}

Sélecteur :has()

Le sélecteur :has() est souvent appelé "sélecteur parent" - quelque chose que les développeurs demandaient depuis des décennies.

Cas d'Usage Pratiques

/* Styliser un élément parent basé sur l'enfant */

/* Formulaire avec erreur */
.form-group:has(input:invalid) {
  border-color: #dc3545;
}

.form-group:has(input:invalid) .form-label {
  color: #dc3545;
}

/* Card avec image */
.card:has(img) {
  padding: 0;
}

.card:has(img) .card-content {
  padding: 1.5rem;
}

/* Card sans image */
.card:not(:has(img)) {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
}

/* Navigation avec sous-menu ouvert */
.nav-item:has(.submenu:hover),
.nav-item:has(.submenu:focus-within) {
  background: #f5f5f5;
}

.nav-item:has(.submenu:hover) .submenu,
.nav-item:has(.submenu:focus-within) .submenu {
  display: block;
}

/* Figure avec caption */
figure:has(figcaption) img {
  border-radius: 8px 8px 0 0;
}

figure:has(figcaption) figcaption {
  background: #f5f5f5;
  padding: 0.75rem;
  border-radius: 0 0 8px 8px;
}

/* Checkbox personnalisée qui affecte le label voisin */
input[type="checkbox"]:checked + label {
  text-decoration: line-through;
  color: #999;
}

/* Grid qui s'adapte au contenu */
.grid-container:has(> :nth-child(4)) {
  grid-template-columns: repeat(2, 1fr);
}

.grid-container:has(> :nth-child(7)) {
  grid-template-columns: repeat(3, 1fr);
}

Formulaires Intelligents

/* Afficher/cacher des champs basés sur les sélections */
.form:has(#option-business:checked) .business-fields {
  display: block;
}

.form:has(#option-personal:checked) .business-fields {
  display: none;
}

/* Bouton submit activé seulement quand le form est valide */
.form:has(:invalid) .submit-button {
  opacity: 0.5;
  pointer-events: none;
}

/* Indicateur de progression du formulaire */
.form-progress::before {
  content: '';
  width: 0%;
  height: 4px;
  background: #0066cc;
  transition: width 0.3s ease;
}

.form:has(input:nth-of-type(1):valid) .form-progress::before {
  width: 25%;
}

.form:has(input:nth-of-type(2):valid) .form-progress::before {
  width: 50%;
}

.form:has(input:nth-of-type(3):valid) .form-progress::before {
  width: 75%;
}

.form:has(input:nth-of-type(4):valid) .form-progress::before {
  width: 100%;
  background: #28a745;
}

CSS Nesting Natif

Nous avons enfin le nesting natif en CSS, similaire à Sass/Less.

Syntaxe de Nesting

/* Nesting natif - sans préprocesseur */
.card {
  background: white;
  border-radius: 12px;
  padding: 1.5rem;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);

  /* Éléments enfants */
  & .card-header {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-bottom: 1rem;
  }

  & .card-title {
    font-size: 1.25rem;
    font-weight: 600;
    color: #333;
  }

  & .card-body {
    color: #666;
    line-height: 1.6;
  }

  & .card-footer {
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid #eee;
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
  }

  /* Pseudo-classes */
  &:hover {
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
    transform: translateY(-2px);
  }

  &:focus-within {
    outline: 2px solid #0066cc;
    outline-offset: 2px;
  }

  /* Variantes */
  &.card-featured {
    border: 2px solid #0066cc;

    & .card-title {
      color: #0066cc;
    }
  }

  &.card-danger {
    border-left: 4px solid #dc3545;

    & .card-title::before {
      content: '⚠️ ';
    }
  }

  /* Media queries imbriquées */
  @media (min-width: 768px) {
    padding: 2rem;

    & .card-title {
      font-size: 1.5rem;
    }
  }

  /* Container queries imbriquées */
  @container card (min-width: 400px) {
    flex-direction: row;

    & .card-image {
      width: 150px;
    }
  }
}

Subgrid

Subgrid permet aux éléments enfants de participer à la grille de l'élément parent.

Alignement Parfait des Cards

/* Grille principale */
.cards-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 2rem;
}

/* Card utilisant subgrid pour aligner le contenu */
.card {
  display: grid;
  grid-template-rows: auto 1fr auto;
  /* Ou avec subgrid pour hériter les rows du parent */
  /* grid-template-rows: subgrid; */
  gap: 1rem;
  background: white;
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.card-image {
  aspect-ratio: 16 / 9;
  object-fit: cover;
}

.card-content {
  padding: 0 1.5rem;
  /* Flexible pour occuper l'espace disponible */
}

.card-footer {
  padding: 1rem 1.5rem;
  background: #f8f9fa;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

/* Exemple avec subgrid réel */
.features-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: auto auto 1fr auto;
  gap: 2rem;
}

.feature-card {
  display: grid;
  grid-row: span 4;
  grid-template-rows: subgrid;
  background: white;
  border-radius: 12px;
  padding: 1.5rem;
  gap: 0;
}

.feature-icon {
  /* Ligne 1 : icône */
  font-size: 2.5rem;
  margin-bottom: 1rem;
}

.feature-title {
  /* Ligne 2 : titre */
  font-size: 1.25rem;
  font-weight: 600;
}

.feature-description {
  /* Ligne 3 : description (flexible) */
  color: #666;
  line-height: 1.6;
}

.feature-link {
  /* Ligne 4 : lien (aligné dans toutes les cards) */
  margin-top: 1rem;
  color: #0066cc;
  font-weight: 500;
}

Nouvelles Fonctions de Couleur

Le CSS moderne offre des fonctions de couleur bien plus puissantes.

OKLCH et Color-Mix

:root {
  /* OKLCH : meilleur pour créer des palettes accessibles */
  --primary: oklch(55% 0.25 250);
  --primary-light: oklch(70% 0.2 250);
  --primary-dark: oklch(40% 0.25 250);

  /* Générer des variantes automatiquement */
  --primary-hover: oklch(from var(--primary) calc(l - 10%) c h);
  --primary-active: oklch(from var(--primary) calc(l - 20%) c h);

  /* Color-mix pour les transparences */
  --primary-10: color-mix(in srgb, var(--primary) 10%, transparent);
  --primary-50: color-mix(in srgb, var(--primary) 50%, transparent);

  /* Couleurs sémantiques dérivées */
  --success: oklch(65% 0.2 145);
  --warning: oklch(75% 0.15 85);
  --danger: oklch(55% 0.25 25);
}

.button-primary {
  background: var(--primary);
  color: white;

  &:hover {
    background: var(--primary-hover);
  }

  &:active {
    background: var(--primary-active);
  }
}

/* Thème sombre automatique avec OKLCH */
@media (prefers-color-scheme: dark) {
  :root {
    --primary: oklch(70% 0.2 250);
    --primary-light: oklch(80% 0.15 250);
    --primary-dark: oklch(60% 0.2 250);
  }
}

Scroll-Driven Animations

Animations basées sur le scroll, sans JavaScript.

/* Définir une timeline de scroll */
@keyframes fade-in {
  from {
    opacity: 0;
    transform: translateY(50px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Appliquer une animation basée sur le scroll */
.animate-on-scroll {
  animation: fade-in linear;
  animation-timeline: view();
  animation-range: entry 0% cover 40%;
}

/* Parallax avec scroll */
@keyframes parallax {
  from {
    transform: translateY(0);
  }
  to {
    transform: translateY(-100px);
  }
}

.parallax-background {
  animation: parallax linear;
  animation-timeline: scroll();
}

/* Barre de progression basée sur le scroll */
.reading-progress {
  position: fixed;
  top: 0;
  left: 0;
  height: 4px;
  background: #0066cc;
  transform-origin: left;
  animation: grow-progress linear;
  animation-timeline: scroll();
}

@keyframes grow-progress {
  from {
    transform: scaleX(0);
  }
  to {
    transform: scaleX(1);
  }
}

Conclusion

Le CSS de 2025 est un langage complètement différent du CSS d'il y a quelques années. Container Queries, Cascade Layers, :has() et les autres fonctionnalités présentées résolvent des problèmes qui nécessitaient auparavant du JavaScript ou étaient impossibles.

Pour commencer dès aujourd'hui :

  1. Expérimentez les Container Queries sur un composant
  2. Organisez vos styles avec Cascade Layers
  3. Remplacez du JavaScript par :has() où possible
  4. Adoptez le CSS Nesting pour un code plus propre

Le web évolue rapidement, et le CSS suit. Les développeurs qui maîtrisent ces outils créent des interfaces plus élégantes avec moins de code.

Si vous voulez approfondir vos connaissances en front-end, je vous recommande de jeter un œil à un autre article : React, Vue et Angular en 2025 où vous découvrirez comment ces frameworks s'intègrent avec le CSS moderne.

C'est parti ! 🦅

📚 Vous Voulez Approfondir Vos Connaissances en JavaScript ?

Cet article a couvert le CSS moderne, mais JavaScript reste essentiel pour tout développeur front-end.

Les développeurs qui investissent dans une connaissance solide et structurée tendent à avoir plus d'opportunités sur le marché.

Matériel d'Étude Complet

Si vous voulez maîtriser JavaScript du basique à l'avancé, j'ai préparé un guide complet :

Options d'investissement :

  • €9,90 (paiement unique)

👉 Découvrir le Guide JavaScript

💡 Matériel mis à jour avec les meilleures pratiques du marché

Commentaires (0)

Cet article n'a pas encore de commentaires. Soyez le premier!

Ajouter des commentaires