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 du conteneur a récemment atteint une compatibilité stable dans tous les moteurs de navigateur modernes.

Navigateurs pris en charge

  • 105
  • 105
  • 110
  • 16

Source

Cependant, la spécification de confinement ne se limite pas aux requêtes de taille. Elle permet également d'interroger les valeurs de style d'un parent. À partir de Chromium 111, vous pourrez appliquer le confinement du style pour les valeurs des propriétés personnalisées et interroger un élément parent pour connaître la valeur d'une propriété personnalisée.

Navigateurs pris en charge

  • 111
  • 111
  • x
  • x

Source

Cela signifie que nous avons un contrôle encore plus logique des styles dans CSS, et que la logique et la couche de données d'une application sont mieux séparées de ses styles.

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

Premiers pas avec les requêtes de style

Supposons que nous ayons le 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 conteneur. Cela nécessite une approche légèrement différente selon que vous interrogez un parent direct ou indirect.

Interroger les parents directs

Schéma d&#39;une requête de style.

Contrairement aux requêtes de style, vous n'avez pas besoin d'appliquer un 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é personnalisées dans ce cas) à un conteneur (.card-container dans ce cas) ou à tout élément contenant l'élément que nous modifions dans le DOM. Nous ne pouvons pas appliquer les styles que nous interrogeons à l'élément direct que nous modifions à l'aide de cette requête, car cela pourrait provoquer 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 encapsule 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 s'applique aux styles si le conteneur parent mesure au moins 200 pixels de large. Cependant, 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) { … }.

Appliquer un style aux parents indirects

Si vous souhaitez interroger des styles pour un élément qui n'est pas un parent direct, vous devez lui attribuer une 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é d'attribuer un nom à vos conteneurs afin d'indiquer clairement ce que vous interrogez et de permettre à ces conteneurs d'y accéder plus facilement. Cela s'avère utile, par exemple, si vous souhaitez appliquer un style directement aux éléments dans .card. Sans conteneur nommé sur .card-container, ils ne peuvent pas l'interroger directement.

Mais tout cela a beaucoup plus de sens dans la pratique. Voici quelques exemples:

Appliquer un style aux requêtes en action

Image de démonstration avec plusieurs fiches produit, certaines avec des tags &quot;nouveaux&quot; ou &quot;en rupture de stock&quot; et la fiche &quot;stock faible&quot; sur fond rouge.

Les requêtes de style sont particulièrement utiles lorsque vous disposez d'un composant réutilisable avec plusieurs variantes ou lorsque vous ne contrôlez pas tous vos styles, mais que vous devez appliquer des modifications dans certains cas. Cet exemple présente un ensemble de fiches produit qui partagent le même composant. Certaines fiches produit comportent des informations ou des remarques supplémentaires, comme "Nouveau" ou "Faible stock", déclenchées par une propriété personnalisée nommée --detail. En outre, si un produit est en "Faible stock", il sera entouré d'une bordure rouge profond. Ce type d'informations est probablement affiché 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 redondance dans le bloc de code. Actuellement, il n'existe aucun moyen de rechercher uniquement la présence de --detail avec @container style(--detail), ce qui permettrait un meilleur partage des styles et moins de répétitions. Cette fonctionnalité est actuellement en cours de discussion au sein du groupe de travail.

Cartes météo

L'exemple précédent utilisait une seule propriété personnalisée avec plusieurs valeurs possibles pour appliquer des styles. Mais vous pouvez aussi combiner plusieurs propriétés en utilisant et en interrogeant plusieurs propriétés personnalisées. Prenons cet exemple de carte météo:

Démonstration des cartes météo

Pour appliquer un style aux dégradés d'arrière-plan et aux icônes de ces cartes, recherchez des 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;
  }
}

De cette façon, vous pouvez styliser chaque carte en fonction de ses caractéristiques uniques. Toutefois, vous pouvez également appliquer un style à des combinaisons de caractéristiques (propriété personnalisée) à l'aide du combinateur and de la même manière que pour les requêtes média. Par exemple, une journée à la fois nuageuse et ensoleillée ressemblerait à ceci:

@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émonstrations, il présente un avantage structurel de séparer la couche de données (DOM qui serait affichée sur la page) des styles appliqués. Les styles sont écrits en tant que variantes possibles qui correspondent au style des composants, tandis qu'un point de terminaison peut envoyer les données qu'il utiliserait ensuite pour appliquer un style au composant. Vous pouvez utiliser une seule valeur, par exemple en mettant à jour la valeur --detail dans le premier cas, ou plusieurs variables, comme dans le second (en définissant --rainy, --cloudy ou --sunny). L'avantage, c'est que vous pouvez également combiner ces valeurs. En recherchant --sunny et --cloudy, vous pourriez obtenir un style partiellement nuageux.

Vous pouvez facilement mettre à jour des valeurs de propriété personnalisées via JavaScript lors de la configuration du modèle DOM (c'est-à-dire lors de la création du composant dans un framework) ou à tout moment à l'aide de <parentElem>.style.setProperty('--myProperty’, <value>). I

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

Appliquez un style à la fiche à 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. Les requêtes de conteneur vont vous permettre de créer davantage d'interfaces responsives et dynamiques. Concernant les requêtes de style en particulier, il reste quelques problèmes à résoudre. La première consiste à implémenter des requêtes de style pour les styles CSS au-delà des propriétés personnalisées. Ces fonctionnalités sont déjà incluses dans les spécifications actuelles, mais elles ne sont pas encore implémentées dans les navigateurs. L'évaluation du contexte booléen devrait être ajoutée au niveau de spécification actuel une fois le problème en suspens résolu, tandis que les requêtes de plage sont prévues pour le niveau suivant de la spécification.