Design & UX

CSS modernes Baseline 2026 — container queries, subgrid, :has, scroll-driven animations

8 min de lecture

Cet article décrit les fonctionnalités CSS désormais disponibles dans tous les navigateurs courants (Baseline) en 2026 : container queries, subgrid, sélecteur :has(), nesting natif, scroll-driven animations, anchor positioning, et nouvelles unités cqi/cqb. Toutes les syntaxes ci-dessous sont vérifiées sur la documentation MDN et sur les tableaux de support caniuse.com à mai 2026.

1 — Container queries (Baseline 2023)

Container queries permettent à un composant de s’adapter à la taille de son conteneur, indépendamment de la largeur de la fenêtre. Devenu Baseline Newly Available en février 2023, Baseline Widely Available depuis août 2025.

Déclarer un conteneur de requête :

.card-container {
    container-type: inline-size;
    container-name: card;
}

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

Trois valeurs pour container-type :

  • inline-size — observe la largeur seulement (le plus courant)
  • size — observe largeur et hauteur (impose un layout strict)
  • normal — n’agit pas comme conteneur de requête, sert pour style queries

Unités relatives au conteneur :

  • cqw — 1 % de la largeur du conteneur
  • cqh — 1 % de la hauteur
  • cqi — 1 % de la dimension inline (= cqw en mode horizontal)
  • cqb — 1 % de la dimension block (= cqh en mode horizontal)
  • cqmin, cqmax — la plus petite/grande des deux
.card h2 {
    font-size: clamp(1rem, 4cqi, 1.75rem);
}

Cette règle fait grossir le titre en proportion de la largeur du conteneur, avec plancher 1rem et plafond 1,75rem.

2 — Subgrid (Baseline 2023)

Subgrid permet à un élément grid imbriqué d’hériter des lignes de son parent, garantissant l’alignement de plusieurs cartes côte à côte. Baseline Newly Available depuis septembre 2023, Widely Available depuis mars 2026.

.cards {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1rem;
}

.card {
    display: grid;
    grid-template-rows: subgrid;
    grid-row: span 3;
    gap: 0.5rem;
}

.card-image, .card-title, .card-body {
    /* automatiquement aligné sur la rangée correspondante */
}

Sans subgrid, un titre court dans une carte et un titre long dans la carte voisine produisent un décalage du contenu en dessous. Avec grid-template-rows: subgrid, le bord inférieur du titre s’aligne sur les autres cartes — pratique pour les listes produits, les stats, les bios.

3 — Sélecteur :has() (Baseline 2023)

Le sélecteur de parent :has() sélectionne un élément en fonction de ses descendants ou de ses voisins. Baseline depuis décembre 2023, support universel sur Chrome, Edge, Firefox (depuis 121, décembre 2023), Safari.

/* Une carte qui contient une vidéo prend une mise en page différente */
.card:has(video) {
    display: grid;
    grid-template-columns: 2fr 1fr;
}

/* Un label suivi d'un input invalide passe en rouge */
label:has(+ input:invalid) {
    color: #c00;
}

/* Body sans h1 dans le main */
body:has(main:not(:has(h1))) {
    background: #fef;
}

Limitation : :has() ne peut pas être imbriqué dans certains pseudo-classes (:has(:has(...)) est interdit). Performance : Chrome a optimisé le sélecteur depuis la version 105, le coût est négligeable sur des sélecteurs simples.

4 — Nesting natif (Baseline 2023)

Le nesting CSS natif évite Sass/PostCSS pour la majorité des cas. Baseline Newly Available depuis août 2023.

.card {
    padding: 1rem;
    border-radius: 8px;

    & h2 {
        font-size: 1.25rem;
    }

    &:hover {
        transform: translateY(-2px);
    }

    @media (max-width: 600px) {
        padding: 0.5rem;
    }

    & .meta {
        color: #666;

        & a {
            color: inherit;
            text-decoration: underline;
        }
    }
}

Différences avec Sass : & est obligatoire pour cibler le parent (en CSS natif, contrairement à Sass où il est optionnel). Les règles imbriquées sans & ciblent un descendant.

5 — Scroll-driven animations

Animations déclenchées par le défilement, sans JavaScript. Disponible nativement sur Chromium 115+ (juillet 2023). Firefox derrière (en cours d’implémentation, flag activable). Safari : pas encore supporté en mai 2026 — prévoir un fallback.

@keyframes apparait {
    from { opacity: 0; transform: translateY(20px); }
    to   { opacity: 1; transform: translateY(0); }
}

.section {
    animation: apparait linear;
    animation-timeline: view();
    animation-range: entry 0% entry 100%;
}

L’élément .section joue l’animation au moment où il entre dans le viewport. animation-timeline: view() lie la timeline à la position relative à la fenêtre. animation-range: entry couvre la phase d’entrée (0 % = le haut de l’élément touche le bas du viewport ; 100 % = le bas de l’élément quitte le haut du viewport).

Pour une barre de progression de lecture en haut de page :

.progress-bar {
    position: fixed;
    top: 0; left: 0;
    height: 3px;
    width: 100%;
    background: #06c;
    transform-origin: left;
    animation: scaleProgress linear;
    animation-timeline: scroll(root);
}

@keyframes scaleProgress {
    from { transform: scaleX(0); }
    to   { transform: scaleX(1); }
}

Détecter le support et fournir une alternative JavaScript :

@supports not (animation-timeline: scroll()) {
    .progress-bar { display: none; }
    /* JS prend le relais via IntersectionObserver */
}

6 — Anchor positioning

Anchor positioning permet de positionner un élément flottant (tooltip, popover, menu) par rapport à un autre, sans calcul JavaScript. Disponible sur Chromium 125+ (avril 2024) — Baseline Newly Available depuis fin 2025. Safari TP supporte (Safari stable en cours). Firefox : flag layout.css.anchor-positioning.enabled.

.bouton {
    anchor-name: --bouton-aide;
}

.tooltip {
    position: absolute;
    position-anchor: --bouton-aide;
    top: anchor(bottom);
    left: anchor(center);
    translate: -50% 4px;
}

L’expression anchor(bottom) renvoie la position du bas de l’élément ancre. Pour un menu qui suit son trigger :

.menu {
    position: fixed;
    position-anchor: --trigger;
    inset-block-start: anchor(end);
    inset-inline-start: anchor(start);
    position-try-fallbacks: flip-block, flip-inline;
}

position-try-fallbacks teste plusieurs positions alternatives si l’élément déborde de la fenêtre — équivalent natif de Floating UI.

7 — Color functions modernes

OKLCH est un espace de couleur perceptuellement uniforme, supporté Baseline depuis mai 2023.

.btn {
    background: oklch(60% 0.15 240);  /* lightness chroma hue */
    --btn-light: oklch(from var(--btn-color) calc(l + 0.1) c h);
}

/* Mélange de couleurs */
.btn-secondary {
    background: color-mix(in oklch, blue 50%, white);
}

OKLCH évite les transitions ternes du HSL traditionnel sur les variations de luminosité. color-mix() est Baseline depuis mai 2023.

8 — Architecture CSS recommandée en 2026

Une feuille CSS moderne tire parti de ces fonctionnalités pour réduire la dépendance aux frameworks :

  • Reset — un reset minimal (Andy Bell modern reset, ou :where sur des sélecteurs basiques) plutôt qu’un mega-reset.
  • Layer architecture — utiliser @layer pour gérer la cascade : @layer reset, base, components, utilities;
  • Tokens — variables CSS dans :root pour couleurs (oklch), espacements, typo, breakpoints.
  • Composants en container queries — chaque composant adapte sa mise en page selon son conteneur, pas selon la fenêtre.
  • Pas de Sass nesting — le nesting natif suffit pour 90 % des cas. Sass reste utile pour ses fonctions et mixins.
  • Fallbacks via @supports uniquement pour les fonctionnalités non Baseline (anchor positioning sur Safari, scroll-driven animations sur Safari).

Tester systématiquement sur Safari et iOS Safari — Apple est l’implémenteur le plus tardif sur les nouvelles fonctionnalités CSS. Caniuse.com et le tableau Baseline de webstatus.dev sont les références à consulter avant d’adopter une fonctionnalité en production.

Références

Patterns d’usage avancés et stratégie de compatibilité

Les features CSS modernes Baseline 2026 transforment ce qu’on peut faire en CSS pur, mais leur adoption demande une stratégie pensée. Sortir une feature en production sans plan de repli, c’est exposer une partie des utilisateurs — typiquement ceux sur Samsung Internet ancien ou sur des navigateurs in-app de réseaux sociaux — à des mises en page cassées. Les patterns ci-dessous combinent puissance moderne et robustesse universelle, avec à chaque fois la pourcentage de support natif (source caniuse.com en mai 2025) et le mécanisme de fallback testé.

Container queries au service du design système

Les container queries résolvent un vieux problème des media queries : un composant ne sait pas dans quel contexte il s’affiche. Un même composant carte produit doit pouvoir s’organiser en colonne sur une side-bar étroite et en ligne sur une zone large, sans connaître la taille du viewport. La syntaxe est simple : on déclare container-type: inline-size sur le parent, puis on écrit @container (min-width: 400px) { .card { display: flex; } }. Le composant devient réellement portable. Pour la compatibilité, tous les navigateurs evergreen depuis février 2023 supportent les container queries — Chrome 105 plus, Firefox 110 plus, Safari 16 plus. Pour les navigateurs antérieurs, une approche progressive consiste à fournir une mise en page minimale fonctionnelle par défaut, et à enrichir uniquement quand le navigateur supporte la feature : @supports (container-type: inline-size) { ... }.

Subgrid pour aligner les éléments d’enfants

Avant subgrid, aligner des éléments d’une grille enfant sur les colonnes ou les lignes de la grille parente nécessitait des hacks JavaScript ou des grids dupliquées. Avec grid-template-columns: subgrid, l’enfant hérite directement de la grille du parent, ce qui simplifie radicalement les layouts de cartes alignées (titre sur la même ligne, prix sur la même ligne, bouton sur la même ligne, sans connaître à l’avance le contenu de chaque carte). Safari a introduit subgrid en 2022, Firefox depuis longtemps, Chrome en septembre 2023 (version 117). Le seuil de 90 pour cent de support global est franchi début 2024.

Le sélecteur :has() et la fin de JavaScript pour les états parents

Le pseudo-classe :has() permet enfin de styler un élément parent en fonction de ses descendants. Exemple typique : un formulaire qui doit changer de bordure quand un champ enfant est invalide — .form:has(input:invalid) { border-color: red; }. Sans :has(), il fallait du JavaScript pour ajouter une classe au parent. Le support est arrivé dans Safari 15.4, Chrome 105, Firefox 121. Pour les navigateurs antérieurs, la stratégie est identique aux container queries : enrichir progressivement via @supports selector(:has(*)).

Scroll-driven animations et accessibilité

Les animations pilotées par le scroll sans JavaScript via animation-timeline: scroll() ouvrent des effets parallax et de progression visuelle natifs. Le support est plus jeune (Chrome 115 plus en 2023, Firefox derrière flag, Safari pas encore au moment d’écrire) — réserver pour des sites où la dégradation est acceptable. Surtout, toujours respecter @media (prefers-reduced-motion: reduce) et désactiver ces animations pour les utilisateurs ayant exprimé une préférence — c’est non seulement une bonne pratique d’accessibilité mais souvent une obligation légale.

Partager