Profundizando en las consultas de estilo de contenedor

Nodo de origen: 1765194

Escribí algunos pensamientos iniciales sobre consultas de estilo de contenedor un poco de tiempo atrás. Aún es pronto. Ya están definidos en el Especificación de nivel 1 del módulo de contención CSS (actualmente en estado de Borrador del editor) pero todavía hay un par de debates pendientes.

La idea básica es que podemos definir un contenedor y luego aplicar estilos condicionalmente a sus descendientes en función de su estilo calculado.

@container ?  {
  /* conditional styles */
}

El mejor ejemplo que he visto hasta ahora es quitar la cursiva de algo como , y cuando se usan en un contexto donde el contenido ya está en cursiva:

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;
  }
}

Esa es la idea general. Pero si no lo sabía, Miriam Suzanne, quien es editora de la especificación, mantiene un conjunto continuo y completo de notas personales sobre consultas de estilo de contenedor que está disponible públicamente. Se actualizó el otro día y pasé un tiempo allí tratando de entender los aspectos más matizados de las consultas de estilo. Son cosas no oficiales, pero pensé en anotar algunas cosas que me llamaron la atención. ¿Quién sabe? ¡Tal vez son cosas que eventualmente podemos esperar!

Cada elemento es un contenedor de estilo.

Ni siquiera necesitamos asignar explícitamente un container-name or container-type para definir un contenedor de estilo porque todo es un contenedor de estilo por defecto.

Entonces, ¿ves ese ejemplo anterior que elimina las cursivas? Observe que no identifica un contenedor. Salta directamente a la consulta usando el style() función. Entonces, ¿qué contenedor se está consultando? va a ser el padre directo de los elementos recibiendo los estilos aplicados. Y si no es eso, entonces es el siguiente contenedor relativo más cercano eso tiene prioridad.

Me gusta eso. Es muy CSS-y que la consulta busque una coincidencia y luego continúe aumentando hasta que encuentre una condición coincidente.

Fue difícil para mi pequeño cerebro entender por qué podemos salirnos con la nuestra con un contenedor implícito basado en estilos, pero no tanto cuando tratamos con consultas dimensionales, como size y inline-size. Miriam lo explica muy bien:

Las consultas dimensionales requieren css contención en el tamaño, el diseño y el estilo del contenedor para evitar bucles de diseño. La contención es algo invasivo que se aplica ampliamente, por lo que era importante que los autores tuvieran un control cuidadoso sobre qué elementos son (o no son) contenedores de tamaño.

Las consultas basadas en estilos no tienen la misma limitación. Ya no hay forma en CSS para que los estilos descendientes tengan un impacto en los estilos calculados de un antepasado. Por lo tanto, no se requiere contención, y no hay efectos secundarios invasivos o inesperados al establecer un elemento como un contenedor de consulta de estilo.

(El énfasis es mío)

Todo se reduce a las consecuencias, de las cuales no hay ninguna en cuanto a que todo es un contenedor de consulta de estilo listo para usar.

  • Si se encuentra un contenedor: las condiciones se resuelven contra ese contenedor.
  • Si coinciden varios contenedores: el contenedor relativo más cercano tiene prioridad.
  • Si no se encuentran coincidencias: unknown devuelto.

Es lo mismo espíritu “perdonador” como el resto de CSS.

Un contenedor puede admitir consultas dimensionales y de estilo.

Digamos que queremos definir una consulta de estilo sin un explícito container-name:

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

Esto funciona porque todos los elementos son contenedores de estilo, no importa el container-type. Eso es lo que nos permite consultar implícitamente los estilos y confiar en la coincidencia más cercana. Y esto está totalmente bien ya que, nuevamente, no hay efectos secundarios adversos al establecer contenedores de estilo.

Tenemos que usar un explícito container-type para consultas dimensionales, pero no tanto para consultas de estilo ya que cada elemento es una consulta de estilo. Eso también significa que este contenedor es tanto un estilo y consulta dimensional:

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

Excluir un contenedor de ser consultado

Quizás no queramos que un contenedor participe en el proceso de emparejamiento. Ahí es donde podría ser posible establecer container-type: none en un elemento.

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

Los contenedores de consulta de estilo explícito ofrecen más control de lo que se consulta

Si, por ejemplo, tuviéramos que escribir una consulta de estilo para padding , no existe una forma confiable de determinar el contenedor que mejor se adapta, independientemente de si estamos trabajando con un contenedor con un nombre explícito o con el padre directo más cercano. Eso es porque padding no es una propiedad heredada.

Entonces, en esos casos, deberíamos usar container-name para informar explícitamente al navegador de qué contenedores pueden extraer. Incluso podemos dar a un contenedor varios nombres explícitos para que coincida con más condiciones:

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

Ah, y container-name acepta cualquier número de opcionales y reutilizables nombres para un contenedor! Eso es aún más flexibilidad cuando se trata de ayudar al navegador a tomar una decisión al buscar coincidencias.

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

Me pregunto si eso también podría considerarse un "retroceso" en el caso de que se pase por alto un contenedor.

Las consultas de estilo se pueden combinar

El or y and Los operadores nos permiten combinar wueries para mantener las cosas SECAS:

@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. */
}

Alternar estilos

Hay una pequeña superposición entre las consultas de estilo de contenedor y trabajo que se está realizando para definir un toggle() función. Por ejemplo, podemos pasar por dos font-style valores, digamos italic y normal:

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

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

Enfriar. Pero la propuesta de CSS Toggles sugiere que el toggle() La función sería un enfoque más simple:

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

Pero cualquier cosa más allá de este tipo de caso de uso binario es donde toggle() es menos adecuado. Las consultas de estilo, sin embargo, están listas para comenzar. Miriam identifica tres casos en los que las consultas de estilo son más adecuadas que una 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;
  }
}

Las consultas de estilo resuelven el "truco de cambio de propiedad personalizada"

Tenga en cuenta que las consultas de estilo son una solución formal para el "Truco de cambio de propiedad personalizada CSS". Allí, establecemos una propiedad personalizada vacía (--foo: ;) y use el método alternativo separado por comas para "activar y desactivar" las propiedades cuando la propiedad personalizada se establece en un valor real.

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. */
}

Eso es genial, también mucho trabajo que las consultas de contenedor de estilo hacen trivial.

Consultas de estilo y contenido generado por CSS

Para el contenido generado producido por el content propiedad de ::before y ::after pseudo-elementos, el contenedor coincidente es el elemento sobre el que se genera el contenido.

.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;
  }
}

Consultas de estilo y componentes web

Podemos definir un componente web como contenedor y consultarlo por estilo. Primero, tenemos el del componente:


  
… …

Entonces usamos el :host pseudo-elemento como un contenedor para establecer un container-name, container-type, y algunos atributos de alto nivel en él:

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

Elementos dentro del puede consultar los parámetros del elemento:

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

¿Qué es lo siguiente?

Una vez más, todo lo que he anotado aquí se basa en las notas de Miriam, y esas notas no reemplazan la especificación oficial. Pero son una indicación de lo que se está discutiendo y dónde podrían aterrizar las cosas en el futuro. Aprecio que Miriam haya vinculado un puñado de debates pendientes que aún se están llevando a cabo y que podemos seguir para estar al tanto de todo:

Sello de tiempo:

Mas de Trucos CSS