Premiers pas avec les requêtes de style

La possibilité d'interroger la taille intégrée d'un parent et les valeurs d'unité de requête de conteneur a récemment atteint la compatibilité stable dans tous les moteurs de navigateur modernes.

Browser Support

  • Chrome: 105.
  • Edge: 105.
  • Firefox: 110.
  • Safari: 16.

Source

Toutefois, la spécification de contenu inclut plus que de simples requêtes de taille. Elle permet également d'interroger les valeurs de style d'un parent. À partir de Chromium 111, vous pourrez appliquer le style de confinement pour les valeurs de propriétés personnalisées et interroger un élément parent pour obtenir la valeur d'une propriété personnalisée.

Browser Support

  • Chrome: 111.
  • Edge: 111.
  • Firefox: 151.
  • Safari: 18.

Cela signifie que nous avons encore plus de contrôle logique sur les styles en CSS, ce qui permet de mieux séparer la logique et la couche de données d'une application de ses styles.

La spécification du module de confinement CSS de niveau 3, qui couvre les requêtes de taille et de style, permet d'interroger n'importe quel style à partir d'un parent, y compris les paires propriété/valeur telles que font-weight: 800. Toutefois, lors du déploiement de cette fonctionnalité, les requêtes de style ne fonctionnent actuellement qu'avec les valeurs de propriétés personnalisées CSS. Cette fonctionnalité reste très utile pour combiner des styles et séparer les données du design. Examinons comment utiliser les requêtes de style avec les propriétés personnalisées CSS :

Premiers pas avec les requêtes de style

Imaginons que nous disposions du code HTML suivant :

<ul class="card-list">
  <li class="card-container">
    <div class="card">
      ...
    </div>
  </li>
</ul>

Pour utiliser des requêtes de style, vous devez d'abord configurer un élément de conteneur. L'approche est légèrement différente selon que vous interrogiez un parent direct ou indirect.

Interroger les parents directs

Diagramme d&#39;une requête de style.

Contrairement aux requêtes de style, vous n'avez pas besoin d'appliquer le confinement à l'aide de la propriété container-type ou container à .card-container pour que .card puisse interroger les styles de son parent direct. Toutefois, nous devons appliquer les styles (valeurs de propriétés personnalisées dans ce cas) à un conteneur (.card-container dans ce cas) ou à tout élément contenant l'élément que nous stylisons dans le DOM. Nous ne pouvons pas appliquer les styles que nous interrogeons sur l'élément direct que nous stylisons à l'aide de cette requête, car cela pourrait entraîner des boucles infinies.

Pour interroger directement un parent, vous pouvez écrire :

/* styling .card based on the value of --theme on .card-container */
@container style(--theme: warm) {
  .card {
    background-color: wheat;
    border-color: brown; 
    ...
  }
}

Vous avez peut-être remarqué que la requête de style entoure la requête avec style(). Cela permet de distinguer les valeurs de taille des styles. Par exemple, vous pouvez écrire une requête pour la largeur du conteneur sous la forme @container (min-width: 200px) { … }. Cela appliquerait des styles si le conteneur parent avait une largeur d'au moins 200 px. Toutefois, min-width peut également être une propriété CSS, et vous pouvez interroger la valeur CSS de min-width à l'aide de requêtes de style. C'est pourquoi vous devez utiliser le wrapper style() pour faire clairement la différence : @container style(min-width: 200px) { … }.

Mise en forme des parents non directs

Si vous souhaitez interroger les styles d'un élément qui n'est pas un parent direct, vous devez lui attribuer un container-name. Par exemple, nous pouvons appliquer des styles à .card en fonction des styles de .card-list en attribuant à .card-list un container-name et en le référençant dans la requête de style.

/* styling .card based on the value of --moreGlobalVar on .card-list */
@container cards style(--moreGlobalVar: value) {
  .card {
    ...
  }
}

Il est généralement recommandé de nommer vos conteneurs pour indiquer clairement ce que vous interrogez et pour pouvoir accéder plus facilement à ces conteneurs. Par exemple, cela peut être utile si vous souhaitez styliser directement des éléments dans .card. Sans conteneur nommé sur .card-container, vous ne pouvez pas l'interroger directement.

Mais tout cela prend beaucoup plus de sens en pratique. Voici quelques exemples :

Requêtes de style en action

Image de démonstration avec plusieurs fiches produit, dont certaines avec les tags &quot;Nouveau&quot; ou &quot;Stock faible&quot;, et la fiche &quot;Stock faible&quot; avec un arrière-plan rouge.

Les requêtes de style sont particulièrement utiles lorsque vous disposez d'un composant réutilisable avec plusieurs variantes ou lorsque vous n'avez pas le contrôle sur tous vos styles, mais que vous devez appliquer des modifications dans certains cas. Cet exemple montre un ensemble de fiches produit qui partagent le même composant de fiche. Certaines fiches produit comportent des détails/notes supplémentaires, comme "Nouveau" ou "Stock faible", déclenchés par une propriété personnalisée nommée --detail. De plus, si un produit est en "stock faible", il est entouré d'une bordure rouge foncé. Ce type d'informations est probablement rendu par le serveur et peut être appliqué aux cartes via des styles intégrés comme suit :

 <div class="product-list">
  <div class="product-card-container" style="--detail: new">
    <div class="product-card">
      <div class="media">
        <img .../>
      <div class="comment-block"></div>
    </div>
  </div>
  <div class="meta">
    ...
  </div>
  </div>
  <div class="product-card-container" style="--detail: low-stock">
    ...
  </div>
  <div class="product-card-container">
    ...
  </div>
  ...
</div>

Compte tenu de ces données structurées, vous pouvez transmettre des valeurs à --detail et utiliser cette propriété CSS personnalisée pour appliquer les styles :

@container style(--detail: new) {
  .comment-block {
    display: block;
  }
  
  .comment-block::after {
    content: 'New';
    border: 1px solid currentColor;
    background: white;
    ...
  }
}

@container style(--detail: low-stock) {
  .comment-block {
    display: block;
  }
  
  .comment-block::after {
    content: 'Low Stock';
    border: 1px solid currentColor;
    background: white;
    ...
  }
  
  .media-img {
    border: 2px solid brickred;
  }
}

Le code ci-dessus nous permet d'appliquer un chip pour --detail: low-stock et --detail: new, mais vous avez peut-être remarqué une certaine redondance dans le bloc de code. Actuellement, il n'est pas possible d'interroger uniquement la présence de --detail avec @container style(--detail), ce qui permettrait de mieux partager les styles et de réduire les répétitions. Cette fonctionnalité est actuellement en discussion au sein du groupe de travail.

Fiches météo

L'exemple précédent utilisait une seule propriété personnalisée avec plusieurs valeurs possibles pour appliquer des styles. Toutefois, vous pouvez varier les plaisirs en utilisant et en interrogeant plusieurs propriétés personnalisées. Prenons l'exemple de cette fiche météo :

Démonstration des cartes météo.

Pour styliser les dégradés d'arrière-plan et les icônes de ces cartes, recherchez les caractéristiques météorologiques, telles que "nuageux", "pluvieux" ou "ensoleillé" :

@container style(--sunny: true) {
  .weather-card {
    background: linear-gradient(-30deg, yellow, orange);
  }
  
  .weather-card:after {
    content: url(<data-uri-for-demo-brevity>);
    background: gold;
  }
}

Vous pouvez ainsi appliquer un style à chaque fiche en fonction de ses caractéristiques uniques. Toutefois, vous pouvez également appliquer un style à des combinaisons de caractéristiques (propriétés personnalisées) à l'aide du combinateur and de la même manière que pour les requêtes média. Par exemple, un jour à la fois nuageux et ensoleillé se présenterait comme suit :

@container style(--sunny: true) and style(--cloudy: true) {
    .weather-card {
      background: linear-gradient(24deg, pink, violet);
    }
  
  .weather-card:after {
      content: url(<data-uri-for-demo-brevity>);
      background: violet;
  }
}

Séparer les données de la conception

Dans ces deux démos, la séparation de la couche de données (DOM qui serait affiché sur la page) des styles appliqués présente un avantage structurel. Les styles sont écrits sous forme de variantes possibles qui se trouvent dans le style des composants, tandis qu'un point de terminaison peut envoyer les données qu'il utiliserait ensuite pour styliser le composant. Vous pouvez utiliser une seule valeur, comme dans le premier cas, en mettant à jour la valeur --detail, ou plusieurs variables, comme dans le deuxième cas (en définissant --rainy, --cloudy ou --sunny). Et le meilleur, c'est que vous pouvez également combiner ces valeurs. Par exemple, si vous recherchez à la fois --sunny et --cloudy, vous pouvez obtenir un style partiellement nuageux.

La mise à jour des valeurs des propriétés personnalisées via JavaScript peut se faire de manière fluide, soit lors de la configuration du modèle DOM (c'est-à-dire lors de la création du composant dans un framework), soit à tout moment à l'aide de <parentElem>.style.setProperty('--myProperty’, <value>). I

Voici une démo qui, en quelques lignes de code, met à jour le --theme d'un bouton et applique des styles à l'aide de requêtes de style et de cette propriété personnalisée (--theme) :

Pour styliser la carte à l'aide de requêtes de style, le code JavaScript utilisé pour mettre à jour les valeurs des propriétés personnalisées est le suivant :

const themePicker = document.querySelector('#theme-picker')
const btnParent = document.querySelector('.btn-section');

themePicker.addEventListener('input', (e) => {
  btnParent.style.setProperty('--theme', e.target.value);
})

Les fonctionnalités décrites dans cet article ne sont qu'un début. Vous pouvez vous attendre à ce que les requêtes de conteneur vous aident davantage à créer des interfaces dynamiques et responsives. En ce qui concerne plus précisément les requêtes de style, il reste encore quelques problèmes ouverts. La première concerne l'implémentation des requêtes de style pour les styles CSS au-delà des propriétés personnalisées. Cette fonctionnalité fait déjà partie du niveau de spécification actuel, mais n'est pas encore implémentée dans les navigateurs. L'évaluation du contexte booléen devrait être ajoutée au niveau de spécification actuel lorsque le problème en suspens sera résolu, tandis que l'interrogation par plage est prévue pour le prochain niveau de spécification.