Accueil / Articles

Envoyer du CSS moderne en toute sécurité : ancrages, voies de grille, portée et light-dark()

Découvrez comment le positionnement des ancrages, les popovers, la maçonnerie en voies de grille, les transitions d’affichage, light-dark() et @scope remplacent les bibliothèques JavaScript, ainsi que la manière d’adopter chacun d’eux avec des solutions de secours.

Éditeur : REACTAPP.TOP7 min de lecture1605 mots

Sources: CSS Can Already Do This. Developers Are Still Scared to Ship It.

De nombreux bundles front-end intègrent encore des bibliothèques dont la seule fonction est de déterminer l’emplacement d’un tooltip, comme Floating UI ou son prédécesseur Popper, ainsi que d’autres bibliothèques permettant d’organiser des cartes en grille de style Pinterest. Les navigateurs peuvent désormais effectuer ces tâches nativement, ainsi que plusieurs autres fonctions qui nécessitaient auparavant JavaScript, mais peu d’équipes ont supprimé ces dépendances. Ce guide présente cinq fonctionnalités CSS capables de remplacer du code de bibliothèques courantes, explique le fonctionnement de chacune d’elles et montre comment les adopter sans risquer un support inégal par les navigateurs.

Désirées et évitées en même temps

Les enquêtes menées auprès des développeurs reflètent bien cette tension. Selon les résultats de l’étude State of CSS pour 2026, le positionnement des ancrages a été élu comme la nouvelle fonctionnalité qui suscite le plus d’enthousiasme chez les développeurs, détrônant :has(), désormais si largement utilisé qu’il ne semble plus innovant. La même enquête a également demandé quelles fonctionnalités les gens évitaient délibérément en raison d’un manque de confiance quant à leur prise en charge par les navigateurs, et le positionnement des ancrages figurait également en tête de cette liste. Compléter cette lacune consiste principalement à adopter des fonctionnalités via des requêtes de fonctionnalité. Les chiffres des enquêtes et l’état de prise en charge évoluent rapidement, il convient donc de les considérer comme un instantané et de vérifier les données de compatibilité actuelles avant de compter sur une fonctionnalité donnée.

Positionnement des ancrages plutôt qu’une bibliothèque de tooltips

L’approche JavaScript attache un élément flottant à son déclencheur en mesurant les deux et en recalculant la position à chaque scroll ou redimensionnement. La mise en place d’ancres déplace cette relation dans le CSS.

Au-dessous, le déclencheur définit un nom et le menu se positionne par rapport à lui. La dernière déclaration remplace la détection de collisions d’une bibliothèque :

.trigger {
  anchor-name: --menu-trigger;
}
.menu {
  position: fixed;
  position-anchor: --menu-trigger;
  top: anchor(bottom);
  left: anchor(center);
  position-try-fallbacks: flip-block, flip-inline;
}

Chaque élément a un rôle spécifique :

  • anchor-name attribue à un élément un identifiant pointillé que les autres éléments peuvent utiliser pour se positionner par rapport à lui.
  • position-anchor indique à un élément positionné quel ancre nommé il doit suivre.
  • anchor() lit directement sur cet élément une coordonnée, telle que le bord inférieur ou le centre horizontal de l’ancrage.
  • position-try-fallbacks énumère les alternatives que le navigateur essaie lorsque l’élément risque de dépasser la zone visible ; flip-block et flip-inline le reflètent verticalement ou horizontalement.
  • Association avec l’attribut popover

    L’attribut popover, également fourni mais largement ignoré, définit le comportement d’un menu déroulant : un bouton doté de popovertarget active l’élément correspondant par son id, sans nécessiter de script :

    <button popovertarget="menu">Options</button>
    <div id="menu" popover anchor-name-target="--menu-trigger">...</div>
    

    Faites attention à ce fragment : anchor-name-target n’est pas une propriété HTML standard, donc ne le copiez pas tel quel. Connectez le popover à son ancre en CSS en utilisant anchor-name et position-anchor comme indiqué ci-dessus. Certains navigateurs traitent également le bouton d’appel comme une ancre implicite pour un popover ouvert via popovertarget ; consultez la documentation actuelle avant de compter sur ce comportement.

    Ce que popover offre nativement, c’est un affichage en premier plan, de sorte que le menu apparaît au-dessus de tout sans conflits de z-index dans l’application, un fermeture automatique lorsque l’utilisateur clique à l’extérieur ou appuie sur Escape, ainsi qu’un gestionnement approprié du focus lors de son ouverture et de sa fermeture. Il ne retient pas le focus comme le fait un dialog modal, donc pour du contenu véritablement modal, utilisez plutôt dialog.

    Pour en savoir plus sur les API de navigateur qui rendent les packages npm inutiles, consultez notre aperçu des API natives des navigateurs remplaçant les packages populaires.

    Masonry natif avec des pistes de grille

    Puisque CSS Grid est apparu dans les navigateurs en 2017, les layouts de type masonry nécessitaient encore JavaScript, d’où l’installation de millions de fois de la bibliothèque Masonry et de packages similaires. La situation est en train de changer. La règle suivante définit le layout complet :

    .gallery {
      display: grid-lanes;
      gap: 10px;
      grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    }
    

    Chaque carte est placée dans la colonne qui dispose actuellement de l’espace le plus important, ce qui permet aux éléments d’être regroupés serré sans espaces inégaux. Il n’y a ni ResizeObserver, ni réajustement automatique des colonnes dans le script chaque fois qu’une image se charge. La syntaxe a fait l’objet de débats pendant des années entre les fabricants de navigateurs, Apple et Google préférant des approches différentes, avant que display: grid-lanes ne devienne la solution de compromis.

    D’après les informations de compatibilité à l’époque de la rédaction, Safari l’a intégré en premier, tandis que Chrome et Firefox le gardaient sous forme de fonctionnalité débloquable. Cela explique pourquoi si peu de sites l’utilisent, et pourquoi il est utile de le mettre en œuvre maintenant. Placez grid-lanes à l’intérieur d’un bloc @supports (display: grid-lanes) et laissez un grille ordinaire servir de solution de secours.

    Transitions d’affichage, avec une réelle prudence

    Ici, la prudence est justifiée. Une prise en charge de l’API View Transition existe, mais elle est inégale, ce qui la place, aux côtés du positionnement des ancrages, parmi les fonctionnalités les plus évitées, et cette hésitation est raisonnable.

    Le mécanisme de base est simple : on enrobe une mise à jour du DOM dans document.startViewTransition(), et le navigateur prend des captures d’écran de l’état ancien et du nouvel état avant de faire un fondu entre eux, sans bibliothèque d’animation ni gestion manuelle des états avant et après. Les pseudo-éléments ci-dessous contrôlent cette transition par défaut, en la raccourcissant ici à 0,3 seconde :

    ::view-transition-old(root),
    ::view-transition-new(root) {
      animation-duration: 0.3s;
    }
    

    Aujourd’hui, on peut lui faire confiance en tant qu’amélioration progressive. Un navigateur qui ne dispose pas de cette API passe simplement à l’état nouveau, ce qui évite tout problème. Cela en fait une solution sûre pour des moments non critiques tels que le changement de page ou la réorganisation d’une liste. Dans les scripts, vérifiez que document.startViewTransition existe avant de l’appeler, et optez sinon pour une mise à jour directe.

    Une seule palette pour mode clair et sombre avec light-dark()

    Un mode sombre typique conserve deux systèmes de couleurs complets, l’un à l’intérieur d’une requête média prefers-color-scheme: dark et l’autre à l’extérieur. La fonction light-dark() regroupe chaque paire en une seule déclaration.

    Le fragment ci-dessous configure d’abord le document pour les deux modes avec color-scheme, requis par light-dark() pour fonctionner, puis attribue à chaque propriété ses valeurs pour le mode clair et celui pour le mode sombre côte à côte :

    :root {
      color-scheme: light dark;
    }
    
    body {
      background: light-dark(#ffffff, #1a1a1a);
      color: light-dark(#111111, #eeeeee);
    }
    

    Lorsque le color-scheme est défini, chaque appel à light-dark() renvoie la valeur appropriée en fonction des préférences du système de l’utilisateur, ce qui évite de dupliquer les requêtes médias dans le feuille de style. Pour d’autres méthodes de mise en œuvre de thématisation, consultez notre comparaison des techniques de mode sombre.

    Contenir les styles avec @scope

    Une grande partie de la motivation derrière CSS-in-JS ou une nomenclature BEM stricte réside dans la fuite de spécificité : une règle .card destinée à un composant applique également des styles à un élément .card non lié, situé plus profondément dans le markup d’un autre composant. @scope limite naturellement les styles à un sous-arbre.

    Dans la règle ci-dessous, les styles des paragraphes s’appliquent à l’intérieur de .card mais s’arrêtent à tout élément .card-nested :

    @scope (.card) to (.card-nested) {
      p {
        color: var(--card-text);
      }
    }
    

    Le premier sélecteur définit la racine du champ d’application. La clause to fixe une limite inférieure, créant ainsi un espace permettant à un composant imbriqué de gérer ses propres styles au lieu d’hériter ceux de son parent. Rien de tout cela n’a besoin d’une étape de compilation, de noms de classes générés ou de JavaScript à l’exécution. Cela ne remplacera pas tout ce que CSS-in-JS permet, comme les styles calculés à partir des propriétés du composant, mais cela élimine l’une des principales raisons pour lesquelles les équipes l’adoptent.

    Le navigateur est l’écosystème

    Toutes ces cinq fonctionnalités sont disponibles dans au moins un navigateur réel aujourd’hui, et aucune d’elles ne dépend de React, d’un plugin de bundler ou d’une version de framework. La distance entre ce que CSS peut faire et ce que les équipes mettent en œuvre est principalement due à une habitude : depuis une décennie, résoudre un problème d’interface utilisateur signifie installer un paquet.

    Choisissez un endroit où votre projet utilise une solution de contournement en JavaScript pour l’un de ces problèmes, remplacez-la par du code situé derrière @supports, et voyez ce qui cesse de fonctionner. Généralement, moins que vous ne l’imaginez.

    Questions fréquentes

    Le positionnement des liens est-il prêt pour la production ?

    Cette fonctionnalité est déjà disponible et peut être utilisée dès aujourd’hui à condition d’ajouter une vérification @supports. Son classement parmi les fonctionnalités les plus évitées reflète une prudence face à des implémentations inégales entre navigateurs, et non un défaut de fonctionnement. L’association avec une solution de secours, comme un positionnement statique plus simple, constitue le niveau d’attention approprié.

    Avez-vous encore besoin d’une bibliothèque de type masonry ?

    Pour un nouveau projet, probablement pas, à condition que display: grid-lanes soit placé à l’intérieur d’un bloc @supports avec un grille ordinaire en tant que solution de secours. Pour un layout existant destiné à la production et disposant d’une matrice de compatibilité stricte avec les navigateurs, conservez cette bibliothèque encore un certain temps.

    Quelle fonctionnalité devriez-vous adopter en premier ?

    light-dark(). Il s’agit d’une modification en une seule ligne pour chaque valeur de couleur, il bénéficie du plus large soutien parmi les cinq à l’heure de la rédaction, et il élimine un bloc de CSS pour le mode sombre redondant sans aucun risque pour la mise en page.

    Points clés

    • Considérez chaque fonctionnalité comme un amélioration progressive : comportement natif lorsque c’est possible, solution existante ailleurs.
    • La positionnement par ancre combiné à popover couvre la plupart des besoins en indications contextuelles et menus déroulants, mais reliez l’ancre en CSS et utilisez dialog lorsque vous avez besoin d’un véritable modal.
    • grid-lanes et les transitions d’affichage sont les moins matures ; restreignez leur utilisation avec @supports ou une vérification en temps de exécution.
    • light-dark() et @scope suppriment immédiatement du code et présentent le moindre risque.

    Lectures complémentaires