Approfondir les requêtes de style conteneur

Nœud source: 1765194

j'ai écrit quelques premières réflexions sur les requêtes de style conteneur il y a peu de temps. C'est encore tôt. Ils sont déjà définis dans le Spécification du module de confinement CSS de niveau 1 (actuellement au statut de brouillon de l'éditeur) mais il y a encore quelques discussions en cours.

L'idée de base est que nous pouvons définir un conteneur, puis appliquer des styles de manière conditionnelle à ses descendants en fonction de son style calculé.

@container ?  {
  /* conditional styles */
}

Le meilleur exemple que j'ai vu jusqu'à présent consiste à supprimer les italiques de quelque chose comme , et lorsqu'ils sont utilisés dans un contexte où le contenu est déjà en italique :

em, i, q {
  font-style: italic; /* default UA behavior */
}

/* When the container's font-style is italic, remove italics from these elements. */
@container style(font-style: italic) {
  em, i, q {
    font-style: normal;
  }
}

C'est l'idée générale. Mais si vous ne le saviez pas, Miriam Suzanne, qui est rédactrice en chef de la spécification, conserve un ensemble continu et approfondi de notes personnelles sur les requêtes de style conteneur qui est accessible au public. Il a été mis à jour l'autre jour et j'y ai passé du temps à essayer de comprendre les aspects plus nuancés des requêtes de style. Ce n'est pas officiel, mais j'ai pensé noter certaines choses qui m'ont marqué. Qui sait? Peut-être que c'est quelque chose que nous pouvons éventuellement attendre avec impatience!

Chaque élément est un conteneur de style

Nous n'avons même pas besoin d'attribuer explicitement un container-name or container-type pour définir un conteneur de style car tout est un conteneur de style par défaut.

Donc, vous voyez cet exemple ci-dessus qui supprime les italiques ? Notez qu'il n'identifie pas un conteneur. Il saute directement à la requête en utilisant le style() fonction. Alors, quel conteneur est interrogé ? Ce sera le parent direct des éléments recevoir les styles appliqués. Et si ce n'est pas ça, alors c'est le prochain conteneur relatif le plus proche qui prime.

J'aime ça. C'est très CSS-y pour que la requête recherche une correspondance, puis continue à remonter jusqu'à ce qu'elle trouve une condition correspondante.

Il était difficile pour mon petit cerveau de comprendre pourquoi nous pouvons nous en sortir avec un conteneur implicite basé sur des styles, mais pas tellement lorsque nous traitons des requêtes dimensionnelles, comme size ainsi que inline-size. Myriam l'explique bien :

Les requêtes dimensionnelles nécessitent CSS endiguement sur la taille, la disposition et le style du conteneur afin d'éviter les boucles de disposition. Le confinement est une chose invasive à appliquer largement, il était donc important que les auteurs aient un contrôle minutieux sur les éléments qui sont (ou ne sont pas) des conteneurs de taille.

Les requêtes basées sur le style n'ont pas la même limitation. Il n'existe déjà aucun moyen en CSS pour que les styles descendants aient un impact sur les styles calculés d'un ancêtre. Ainsi, aucun confinement n'est requis et il n'y a pas d'effets secondaires invasifs ou inattendus dans l'établissement d'un élément en tant que conteneur de requête de style.

(Souligner le mien)

Tout se résume à des conséquences - dont il n'y en a pas dans la mesure où tout est un conteneur de requête de style dès la sortie de la boîte.

  • Si un conteneur est trouvé : les conditions sont résolues pour ce conteneur.
  • Si plusieurs conteneurs correspondent : le conteneur relatif le plus proche est prioritaire.
  • Si aucune correspondance n'est trouvée : unknown revenu.

C'est la meme chose esprit "indulgent" comme le reste de CSS.

Un conteneur peut prendre en charge à la fois les requêtes dimensionnelles et de style

Disons que nous voulons définir une requête de style sans explicite container-name:

@container style(font-style: italic) {
  em {
    font-style: normal;
  }
}

Cela fonctionne parce que tous les éléments sont des conteneurs de style, peu importe le container-type. C'est ce qui nous permet d'interroger implicitement les styles et de nous fier à la correspondance la plus proche. Et c'est tout à fait correct puisque, encore une fois, il n'y a pas d'effets secondaires indésirables lors de l'établissement de conteneurs de style.

Il faut utiliser un explicite container-type pour les requêtes dimensionnelles, mais pas tant pour les requêtes de style puisque chaque élément est une requête de style. Cela signifie également que ce conteneur est à la fois un style ainsi que requête dimensionnelle :

.card-container {
  container: card / inline-size; /* implictly a style query container as well */
}

Exclusion d'un conteneur de l'interrogation

Peut-être ne voulons-nous pas qu'un conteneur participe au processus de correspondance. C'est là qu'il pourrait être possible de définir container-type: none sur un élément.

.some-element {
  container-type: none;
}

Les conteneurs de requêtes de style explicite offrent plus de contrôle sur ce qui est interrogé

Si, par exemple, nous devions écrire une requête de style pour padding , il n'existe aucun moyen fiable de déterminer le meilleur conteneur correspondant, que nous travaillions avec un conteneur explicitement nommé ou avec le parent direct le plus proche. C'est parce que padding n'est pas une propriété héritée.

Donc, dans ces cas, nous devons utiliser container-name pour informer explicitement le navigateur des conteneurs à partir desquels il peut extraire. Nous pouvons même donner à un conteneur plusieurs noms explicites pour le faire correspondre à plus de conditions :

.card {
  container-name: card layout theme;
}

Oh, et container-name accepte n'importe quel nombre d'options et réutilisable des noms pour un conteneur ! C'est encore plus de flexibilité lorsqu'il s'agit d'aider le navigateur à faire un choix lors de la recherche de correspondances.

.theme {
  container-name: theme;
}
.grid {
  container-name: layout;
}
.card {
  container-name: card layout theme;
}

Je me demande en quelque sorte si cela pourrait également être considéré comme un "repli" dans le cas où un conteneur serait ignoré.

Les requêtes de style peuvent être combinées

La or ainsi que and les opérateurs nous permettent de combiner les wueries pour garder les choses au SEC :

@container bubble style(--arrow-position: start start) or style(--arrow-position: end start) {
  .bubble::after {
    border-block-end-color: inherit;
    inset-block-end: 100%;
  }
}

/* is the same as... */
@container bubble style(--arrow-position: start start) {
  /* etc. */
}
@container bubble style(--arrow-position: end start) {
  /* etc. */
}

Basculer les styles

Il y a un léger chevauchement entre les requêtes de style conteneur et travaux en cours pour définir un toggle() fonction. Par exemple, nous pouvons faire défiler deux font-style valeurs, disons italic ainsi que normal:

em, i, q {
  font-style: italic;
}

@container style(font-style: italic) {
  em, i, q {
    font-style: normal;
  }
}

Cool. Mais la proposition de CSS Toggles suggère que le toggle() fonction serait une approche plus simple:

em, i, q {
  font-style: toggle(italic, normal);
}

Mais tout ce qui va au-delà de ce type de cas d'utilisation binaire est l'endroit où toggle() est moins adapté. Les requêtes de style, cependant, sont bonnes à faire. Miriam identifie trois cas où les requêtes de style sont plus appropriées qu'un toggle():

/* When font-style is italic, apply background color. */
/* Toggles can only handle one property at a time. */
@container style(font-style: italic) {
  em, i, q {
    background: lightpink;
  }
}

/* When font-style is italic and --color-mode equals light */
/* Toggles can only evaluate one condition at a time */
@container style((font-style: italic) and (--color-mode: light)) {
  em, i, q {
    background: lightpink;
  }
}

/* Apply the same query condition to multiple properties */
/* Toggles have to set each one individually as separate toggles */
@container style(font-style: italic) {
  em, i, q {
    /* clipped gradient text */
    background: var(--feature-gradient);
    background-clip: text;
    box-decoration-break: clone;
    color: transparent;
    text-shadow: none;
  }
}

Les requêtes de style résolvent le « Custom Property Toggle Hack »

Notez que les requêtes de style sont une solution formelle pour le "Astuce de basculement de propriété personnalisée CSS". Là, nous définissons une propriété personnalisée vide (--foo: ;) et utilisez la méthode de secours séparée par des virgules pour « activer et désactiver » les propriétés lorsque la propriété personnalisée est définie sur une valeur réelle.

button {
  --is-raised: ; /* off by default */
  
  border: 1px solid var(--is-raised, rgb(0 0 0 / 0.1));
  box-shadow: var(
    --is-raised,
    0 1px hsl(0 0% 100% / 0.8) inset,
    0 0.1em 0.1em -0.1em rgb(0 0 0 / 0.2)
  );
  text-shadow: var(--is-raised, 0 -1px 1px rgb(0 0 0 / 0.3));
}

button:active {
  box-shadow: var(--is-raised, 0 1px 0.2em black inset);
}

#foo {
  --is-raised: initial; /* turned on, all fallbacks take effect. */
}

C'est super cool, aussi beaucoup de travail que les requêtes de conteneur de style rendent triviale.

Requêtes de style et contenu généré par CSS

Pour le contenu généré produit par le content propriété de ::before ainsi que ::after pseudo-éléments, le conteneur correspondant est l'élément sur lequel le contenu est généré.

.bubble {
  --arrow-position: end end;
  container: bubble;
  border: medium solid green;
  position: relative;
}

.bubble::after {
  content: "";
  border: 1em solid transparent;
  position: absolute;
}

@container bubble style(--arrow-position: end end) {
  .bubble::after {
    border-block-start-color: inherit;
    inset-block-start: 100%;
    inset-inline-end: 1em;
  }
}

Requêtes de style et composants Web

Nous pouvons définir un composant Web en tant que conteneur et l'interroger par style. Premièrement, nous avons le du composant :


  
… …

Ensuite, nous utilisons le :host pseudo-élément en tant que conteneur pour définir un container-nameun container-type, et quelques attributs de haut niveau :

:host {
  container: media-host / inline-size;
  --media-location: before;
  --media-style: square;
  --theme: light;
}

Éléments à l'intérieur du peut interroger les paramètres de élément:

@container media-host style(--media-style: round) {
  [part='img'] {
    border-radius: 100%;
  }
}

Quelle est la prochaine?

Encore une fois, tout ce que j'ai noté ici est basé sur les notes de Miriam, et ces notes ne remplacent pas les spécifications officielles. Mais ils sont une indication de ce qui est discuté et où les choses pourraient arriver à l'avenir. J'apprécie que Miriam ait lié une poignée de discussions exceptionnelles toujours en cours que nous pouvons suivre pour rester au courant des choses :

Horodatage:

Plus de Astuces CSS