12k
All articles

Quand utiliser CSS `if()` plutôt que les container style queries

Comparez CSS if() et les container style queries : quand utiliser chaque outil, pièges de syntaxe, syntaxe de plage et support navigateur 2026.

OpenReplay Team
OpenReplay Team
Quand utiliser CSS `if()` plutôt que les container style queries

Utilisez la fonction CSS if() lorsqu’un seul élément doit déterminer sa propre valeur en ligne — jetons de thème, états de composant, ajustement ponctuel d’une propriété responsive. Optez pour une container style query (@container style(...)) lorsque le style d’un parent doit piloter un ensemble de règles sur plusieurs descendants. Le modèle mental en une ligne : if() produit une valeur conditionnelle locale à l’élément ; une container style query établit un contexte, appliquant des blocs de règles conditionnels depuis un parent vers ses enfants.

Les deux sont des mécanismes CSS conditionnels qui lisent des propriétés personnalisées, tous deux utilisent la même primitive de requête style(), et leurs capacités se recoupent suffisamment pour que choisir le mauvais outil produise un code maladroit. Cet article dissipe cette ambiguïté : ce que chacun fait, la différence mécanique qui détermine lequel utiliser, les deux pièges syntaxiques qui font trébucher les développeurs (les points-virgules et la notation deux-points vs égal), la syntaxe de plage désormais présente dans les deux, un tableau de décision par scénario, et comment livrer l’un ou l’autre en production sachant que la prise en charge navigateur se divise en trois catégories à mi-2026.

Points clés à retenir

  • if() lit les propriétés personnalisées déclarées dans la même règle sur le même élément et retourne une valeur ; @container style() ne regarde jamais que vers le haut dans la cascade et applique des blocs de règles entiers aux descendants.
  • Dans if(), les points-virgules (et non les virgules) séparent les paires condition–valeur, et else fournit la valeur de repli ; en l’absence de condition correspondante et sans else, la fonction retourne une valeur garantie invalide qui se résout à la valeur initiale de la propriété.
  • Dans style(), la notation avec deux-points (style(--n: 3)) effectue une correspondance de jetons/chaînes sans calcul mathématique, tandis que la notation avec opérateur de comparaison (style(--n = 3)) analyse les deux côtés numériquement, évalue calc() en premier, puis compare.
  • À mi-2026, les situations divergent : les container style queries de base sont disponibles dans les quatre principaux moteurs (Chrome 111+, Edge 111+, Safari 18+, Firefox 151+), tandis que if() et la syntaxe de plage activée par défaut restent réservées à Chromium.
  • Comme if() est limité à Chromium, chaque déclaration if() nécessite une valeur de repli sur la même propriété placée avant elle, ou un garde @supports.

Ce que fait chaque mécanisme, en bref

La fonction if() est un conditionnel au niveau de la valeur que vous placez à l’intérieur de n’importe quelle propriété. Son argument est une liste de paires condition–valeur séparées par des points-virgules, où chaque condition est séparée de sa valeur par un deux-points, et un else optionnel fournit la valeur par défaut :

.badge {
  color: if(
    style(--variant: danger): white;
    style(--variant: muted): #555;
    else: black;
  );
}

Chaque condition est l’un des trois types de requêtes suivants :

  • style() teste une propriété personnalisée sur l’élément courant ;
  • media() exécute une media query en ligne ;
  • supports() exécute une requête de fonctionnalité.

La référence MDN documente les trois avec la même grammaire à points-virgules — par exemple, flex-direction: if(media(orientation: landscape): row; else: column;) bascule une seule propriété selon le résultat d’une media query.

Une container style query est une règle at-rule @container dont la condition est une ou plusieurs notations style() lisant les propriétés personnalisées d’un élément conteneur. Elle encapsule un bloc de règles plutôt que de produire une valeur unique :

.card { --status: active; }

@container style(--status: active) {
  .card-title { font-weight: 700; }
  .card-icon { opacity: 1; }
  .card-meta { color: green; }
}

Selon le guide MDN sur les container queries, chaque élément est un conteneur de style par défaut — contrairement aux requêtes de taille, définir un container-type n’est pas requis pour les style queries. Cela supprime la principale friction de configuration à laquelle les développeurs s’attendent avec les container queries.

La distinction fondamentale qui guide le choix

La différence mécanique réside dans la portée de la lecture. if() lit les propriétés personnalisées déclarées dans la même règle sur le même élément et est autonome, tandis que @container style() ne regarde jamais que vers le haut dans la cascade, vers un conteneur ancêtre. MDN l’énonce clairement : utiliser une style query dans if() permet de cibler un élément selon qu’une propriété personnalisée lui est définie, plutôt que de vérifier les styles d’un parent. Les deux sont explicitement présentés comme complémentaires, et non redondants.

Cette différence de portée de lecture a trois conséquences pratiques :

  1. Granularité de la sortie. if() définit exactement une valeur. @container applique un bloc de règles entier — plusieurs déclarations sur plusieurs sélecteurs — lorsque sa condition est vraie. Comme le précise MDN, on ne peut définir que des valeurs de propriété uniques avec les style queries if(), tandis que les requêtes @container appliquent conditionnellement des ensembles de règles complets.

  2. Direction. if() peut lire une propriété déclarée sur l’élément même qu’il stylise. @container ne peut pas lire la valeur de son propre élément pour le styliser ; il lit celle d’un ancêtre et stylise les descendants.

  3. Portée. Seules les container style queries peuvent limiter leur portée via container-name. Nommez un conteneur et la requête s’évalue uniquement par rapport à ce conteneur ; si le conteneur nommé ne définit pas la propriété interrogée, le bloc ne s’exécute tout simplement pas. if() n’a pas de contrôle de portée équivalent — il se résout toujours par rapport à l’élément sur lequel il est placé.

Le critère décisif dans la zone de chevauchement est donc le suivant : si une seule propriété sur un seul élément doit basculer, if() implique moins d’indirection. Si plusieurs descendants doivent réagir à une seule valeur parente, @container exprime cela avec une condition unique plutôt que de répéter if() sur chaque enfant.

Les pièges syntaxiques qui font trébucher les développeurs

Des points-virgules, et non des virgules, séparent les paires dans if(). Cela piège tous ceux qui viennent de JavaScript ou des fonctions CSS à virgules comme rgb(). La grammaire est if(condition-1: valeur-1; condition-2: valeur-2; else: repli), le point-virgule après la dernière paire étant optionnel. Il ne doit également y avoir aucun espace entre if et la parenthèse ouvrante, sinon toute la déclaration est invalide. Et lorsqu’aucune condition ne correspond et que vous avez omis else, if() retourne une valeur garantie invalide — dans un contexte de propriété normale, cela se résout à la valeur initiale de la propriété, ce qui est rarement le comportement souhaité. Incluez toujours else.

La distinction deux-points vs égal dans style() est le détail subtil et crucial que presque personne n’explique. if() et les container style queries acceptent toutes deux deux notations au comportement réellement différent, désormais documentées sur MDN.

La notation simple (deux-points) effectue une correspondance de jetons/chaînes sur les valeurs calculées et n’effectue aucun calcul mathématique. La notation de plage (opérateur) — utilisant =, <, <=, > ou >= — résout chaque côté, les analyse tous deux en tant que type numérique, évalue tout calc(), et compare numériquement. La conséquence, tirée directement de l’exemple MDN :

.box { --n: calc(6 / 2); }

/* FAUX : la valeur calculée de --n est la chaîne "calc(6/2)",
   qui n'est pas le jeton "3" */
@container style(--n: 3) { /* … */ }

/* VRAI : les deux côtés sont analysés comme <number>, calc(6/2) vaut 3,
   et 3 = 3 */
@container style(--n = 3) { /* … */ }

Ainsi, style(--n: 3) échoue face à --n: calc(6/2), tandis que style(--n = 3) réussit. Deux autres règles sont importantes. Avec la notation simple, le nom de la propriété personnalisée doit être à gauche sans var()style(var(--n): 3) est invalide — tandis que la notation de plage accepte un nom, var(), un littéral ou calc() de chaque côté dans n’importe quel ordre. Et les valeurs équivalentes (comme blue et #0000ff) ne correspondent sous la notation deux-points que si la propriété est enregistrée avec @property et un descripteur syntax. La conclusion : utilisez la notation deux-points pour la correspondance de mots-clés/chaînes (style(--theme: dark)), et la notation opérateur pour la comparaison numérique (style(--columns >= 3)).

La syntaxe de plage est désormais présente dans les deux

La syntaxe de plage étend les deux mécanismes au-delà de la correspondance exacte vers la comparaison numérique, en tirant ses opérandes de propriétés personnalisées, de attr() ou de valeurs littérales. Selon les notes de version de Chrome 142, une comparaison ne se résout que lorsque les deux côtés se résolvent au même type de données, limité à sept types numériques : <length>, <number>, <percentage>, <angle>, <time>, <frequency> et <resolution>. La forme d’intervalle à trois valeurs fonctionne également — @container style(0 < --n < 10) — avec les deux opérateurs de comparaison pointant dans le même sens.

La syntaxe de plage est ce qui rend propres des patterns comme un badge de notification. Lisez un compteur depuis un attribut et branchez sur des seuils :

@container style(--count > 99) {
  .badge::after { content: "99+"; }
}

Comme la notation de plage analyse numériquement, style(--s = new) est faux pour un mot-clé comme new, tandis que style(--s: new) est vrai — une raison supplémentaire pour laquelle le choix de notation est déterminant.

Quel outil utiliser, selon le scénario

ScénarioOutil recommandéPourquoi
Jeton de thème → une propriété (ex. color depuis --theme)if()Un élément détermine sa propre valeur en ligne
L’état d’un composant bascule plusieurs styles enfants (actif/erreur)@container style()Une valeur parente pilote un bloc de règles sur les descendants
Ajustement responsive d’une seule propriétéif() avec media()Logique par propriété localisée ; aucun bloc @media nécessaire
Seuils d’un badge de compteur de notificationsL’un ou l’autre, via la syntaxe de plage@container si des éléments frères réagissent aussi ; if() si seul le badge change
Texte adapté au contraste sur un parent thématisé@container style()Les enfants lisent le thème du parent et se restylisent ensemble
Valeur conditionnelle à une fonctionnalité (ex. lch() avec repli)if() avec supports()Remplacement de valeur unique selon la prise en charge
Changement de mise en page multi-propriétés à un point de rupture@media (aucun des deux)Les deux sont inadaptés aux changements de mise en page importants

La règle générale : une valeur conditionnelle sur un élément est un travail pour if() ; un contexte conditionnel qui restylise un sous-arbre depuis un parent est un travail pour une container style query.

Livrer en production en 2026

La prise en charge navigateur se divise en trois catégories, et l’ancienne formulation « le CSS conditionnel est réservé à Chromium » n’est plus exacte. Prise en charge navigateur à juin 2026 :

FonctionnalitéChrome / EdgeSafariFirefox
Container style queries (propriétés personnalisées, syntaxe simple)111+18+151+
Fonction if()137+Non disponibleNon disponible
Syntaxe de plage dans style() / if()142+ (activée par défaut)Non disponible151, derrière layout.css.attr.enabled

Les container style queries de base sont désormais véritablement multi-moteurs. Elles ont été livrées dans Chrome et Edge 111, intégrées à WebKit dans Safari 18.0, et sont arrivées dans Firefox 151, publié le 19 mai 2026, qui a ajouté la prise en charge des requêtes style() de @container basées sur les propriétés personnalisées d’un conteneur. La fonction if(), en revanche, reste réservée à Chromium — MDN la signale comme expérimentale avec une disponibilité limitée — et la syntaxe de plage n’est activée par défaut que dans Chromium. Firefox 151 intègre la syntaxe de plage style() de @container mais désactivée par défaut, derrière la préférence layout.css.attr.enabled.

La conclusion pratique : if() ne se dégrade pas gracieusement, donc écrivez d’abord une déclaration par défaut et laissez les navigateurs compatibles la remplacer, ou encapsulez la règle dans un garde @supports. Un pattern couvre les deux mécanismes :

/* repli en premier — tous les navigateurs appliquent ceci */
.card-title { font-weight: 400; }

/* amélioration progressive : seuls les moteurs compatibles avec les style queries appliquent ceci */
@supports (container-type: normal) {
  @container style(--status: active) {
    .card-title { font-weight: 700; }
  }
}

/* if() : valeur statique d'abord, puis le remplacement conditionnel */
.badge { padding: 0.25em; }
.badge { padding: if(style(--size: lg): 0.5em; else: 0.25em); }

Comme if() et la syntaxe de plage activée par défaut restituent leur chemin de repli dans Firefox et Safari — des navigateurs qu’un développeur utilisant Chrome ouvre rarement pendant le développement — la relecture de session cross-navigateur est une technique utile pour observer réellement le rendu de la branche de repli dans le navigateur d’un utilisateur réel, plutôt que de supposer qu’elle s’affiche correctement. Pour les patterns plus avancés avec calc() et l’imbrication dans if(), le guide axé sur la prise de décision de LogRocket va plus loin que ce que l’espace ici permet.

Choisissez selon l’intention, pas selon la nouveauté : une valeur conditionnelle sur un seul élément appelle if() ; un contexte conditionnel qui restylise un sous-arbre depuis un parent appelle une container style query. Livrez l’un ou l’autre derrière un repli dès aujourd’hui, vérifiez à nouveau la matrice de prise en charge avant de vous appuyer sur if() en production, et les deux couvriront de plus en plus l’espace CSS conditionnel qui nécessitait autrefois des contournements avec des propriétés personnalisées et du JavaScript.

FAQ

Les container style queries peuvent-elles lire la propriété personnalisée de l'élément lui-même, comme le fait if() ?

Non. Une container style query regarde toujours vers le haut dans la cascade vers un conteneur ancêtre et applique ses règles aux descendants, elle ne peut donc pas lire une propriété personnalisée sur l'élément même qu'elle styliserait. La fonction if() est l'opposé : elle lit les propriétés personnalisées déclarées dans la même règle sur le même élément et résout une valeur en ligne. Si vous avez besoin qu'un élément se branche sur sa propre propriété, utilisez if() ; si la valeur d'un parent doit restyliser ses enfants, utilisez une container style query.

Les container style queries nécessitent-elles de définir un container-type, comme le font les size queries ?

Non. Chaque élément est un conteneur de style par défaut, donc les style queries sur des propriétés personnalisées fonctionnent sans déclarer container-type. Cela diffère des container size queries, qui requièrent bien container-type (comme inline-size) sur l'ancêtre interrogé avant qu'une condition de taille puisse se résoudre. Cela est documenté dans le guide MDN sur les container queries, et cela supprime la principale étape de configuration à laquelle les développeurs s'attendent lorsqu'ils utilisent la règle at-rule @container pour la première fois.

Pourquoi ma style query avec calc() ne correspond-elle jamais ?

Parce que la notation simple avec deux-points effectue une correspondance de jetons et de chaînes sans calcul mathématique. Une propriété définie à calc(6 / 2) se calcule en la chaîne 'calc(6/2)', qui ne correspond jamais au jeton 3, donc style(--n: 3) retourne faux. Pour comparer numériquement, utilisez la notation avec opérateur de comparaison, style(--n = 3), qui résout chaque côté, évalue calc(), analyse les deux en tant que type numérique et compare numériquement, retournant ainsi vrai. Utilisez la forme avec deux-points pour la correspondance de mots-clés et la forme avec opérateur pour la comparaison numérique.

if() se dégrade-t-il gracieusement dans Safari et Firefox ?

Non. À mi-2026, if() n'est disponible que dans les navigateurs Chromium (Chrome et Edge 137+), donc Safari et Firefox ignorent toute déclaration le contenant et reviennent à la valeur appliquée précédemment. Écrivez d'abord une déclaration statique par défaut et laissez les navigateurs compatibles la remplacer, ou encapsulez la règle conditionnelle dans un garde @supports. Les container style queries de base sont plus sûres sur plusieurs moteurs, disponibles dans Chrome 111+, Edge 111+, Safari 18+ et Firefox 151+, mais la syntaxe de plage activée par défaut reste réservée à Chromium.

Open-source session replay

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.