Cómo comenzar a usar las consultas de estilo

La capacidad de consultar el tamaño intercalado de un elemento superior y los valores de la unidad de consulta de contenedores recientemente alcanzaron la compatibilidad estable en todos los motores de navegador modernos.

Navegadores compatibles

  • 105
  • 105
  • 110
  • 16

Origen

Sin embargo, la especificación de contención incluye más que solo consultas de tamaño; también permite consultar los valores de estilo de un elemento superior. A partir de Chromium 111, podrás aplicar contención de estilo para valores de propiedades personalizadas y consultar un elemento superior para el valor de una propiedad personalizada.

Navegadores compatibles

  • 111
  • 111
  • x
  • x

Origen

Esto significa que tenemos un control aún más lógico de los estilos en CSS y permite una mejor separación de la lógica y la capa de datos de una aplicación de sus estilos.

La especificación del nivel 3 del módulo de contención de CSS, que abarca las consultas de tamaño y estilo, permite que cualquier estilo se consulte desde un elemento superior, incluidos los pares de propiedad y valor, como font-weight: 800. Sin embargo, en el lanzamiento de esta función, las consultas de diseño actualmente solo funcionan con valores de propiedades personalizadas de CSS. Esto sigue siendo muy útil para combinar estilos y separar los datos del diseño. Veamos cómo puedes utilizar las consultas de estilo con las propiedades personalizadas de CSS:

Comienza a usar las consultas de estilo

Supongamos que tenemos el siguiente código HTML:

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

Para usar consultas de diseño, primero debes configurar un elemento contenedor. Esto requiere un enfoque ligeramente diferente que depende de si consultas un elemento superior directo o indirecto.

Consulta a los elementos superiores directos

Diagrama de una consulta de estilo.

A diferencia de las consultas de estilo, no necesitas aplicar contención mediante las propiedades container-type o container a .card-container para que .card pueda consultar los estilos de su elemento superior directo. Sin embargo, debemos aplicar los estilos (en este caso, los valores de propiedad personalizada) a un contenedor (en este caso, .card-container) o a cualquier elemento que contenga el elemento al que aplicaremos diseño en el DOM. No podemos aplicar los estilos que consultamos en el elemento directo al que aplicamos diseño con esa consulta porque esto podría causar bucles infinitos.

Para consultar directamente a un elemento superior, puedes escribir lo siguiente:

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

Quizás hayas notado que la consulta de estilo une la consulta con style(). Esto es para desambiguar los valores de tamaño de los estilos. Por ejemplo, puedes escribir una consulta para el ancho del contenedor como @container (min-width: 200px) { … }. Esto aplicaría estilos si el contenedor superior tenía al menos 200 px de ancho. Sin embargo, min-width también puede ser una propiedad de CSS, y puedes consultar el valor de min-width usando consultas de estilo. Por eso, debes usar el wrapper style() para que la diferencia sea clara: @container style(min-width: 200px) { … }.

Cómo aplicar diseños a elementos superiores no directos

Si deseas consultar diseños de cualquier elemento que no sea un elemento superior directo, debes asignarle un container-name. Por ejemplo, podemos aplicar estilos a .card según los estilos de .card-list si otorgamos a .card-list un container-name y hacemos referencia a él en la consulta de estilo.

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

Por lo general, se recomienda asignar nombres a tus contenedores para dejar en claro lo que estás consultando y desbloquear la capacidad de acceder a esos contenedores con mayor facilidad. Esto resulta útil, por ejemplo, si deseas aplicar diseño a los elementos de .card directamente. Sin un contenedor con nombre en .card-container, no puede buscarlo directamente.

Pero todo esto tiene mucho más sentido en la práctica. Veamos algunos ejemplos:

Consultas de diseño en acción

Imagen de demostración con varias tarjetas de producto, algunas con etiquetas de &quot;nuevo&quot; o &quot;stock bajo&quot; y la tarjeta &quot;stock bajo&quot; con un fondo rojo.

Las consultas de estilo son particularmente útiles cuando tienes un componente reutilizable con múltiples variaciones o cuando no tienes control sobre todos los estilos, pero necesitas aplicar cambios en ciertos casos. En este ejemplo, se muestra un conjunto de tarjetas de productos que comparten el mismo componente de tarjeta. Algunas tarjetas de productos tienen notas o detalles adicionales, como "Nuevo" o "Inventario bajo", que se activan mediante una propiedad personalizada llamada --detail. Además, si un producto se encuentra en stock bajo, tendrá un fondo con un borde de color rojo intenso. Es probable que este tipo de información se renderice en el servidor y se pueda aplicar a las tarjetas a través de estilos intercalados de la siguiente manera:

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

Con estos datos estructurados, puedes pasar valores a --detail y usar esta propiedad personalizada de CSS para aplicar los estilos:

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

El código anterior nos permite aplicar un chip para --detail: low-stock y --detail: new, pero es posible que hayas notado cierta redundancia en el bloque de código. Actualmente, no hay forma de consultar solo la presencia de --detail con @container style(--detail), lo que permitiría compartir mejor los estilos y reducir la repetición. Actualmente, esta función se encuentra en discusión en el grupo de trabajo.

Tarjetas del clima

En el ejemplo anterior, se usaba una sola propiedad personalizada con varios valores posibles para aplicar estilos. Pero también puedes mezclarlos si usas y consultan varias propiedades personalizadas. Tomemos este ejemplo de tarjeta del clima:

Demostración de tarjetas meteorológicas.

Para definir el estilo de los gradientes y los íconos de fondo de estas tarjetas, busca las características climáticas, como “nublado”, “lluvioso” o “soleado”:

@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 esta manera, puedes diseñar cada tarjeta según sus características únicas. Sin embargo, también puedes aplicar estilos para combinaciones de características (propiedad personalizada) usando el combinador and de la misma manera que en las consultas de medios. Por ejemplo, un día nublado y soleado se vería de la siguiente manera:

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

Separar los datos del diseño

En ambas demostraciones, hay un beneficio estructural de separar la capa de datos (el DOM que se renderizaría en la página) de los estilos aplicados. Los estilos se escriben como posibles variantes que se encuentran dentro del estilo de los componentes, mientras que un extremo podría enviar los datos que luego usaría para definir el estilo del componente. Puedes usar un solo valor, como en el primer caso, actualizar el valor --detail, o múltiples variables, como en el segundo caso (configurar --rainy, --cloudy o --sunny). La mejor parte es que también puedes combinar estos valores, por lo que si se verifican --sunny y --cloudy, se podría mostrar un estilo parcialmente nublado.

La actualización de los valores de propiedades personalizadas a través de JavaScript se puede realizar sin problemas, ya sea mientras se configura el modelo del DOM (es decir, mientras se compila el componente en un framework) o se actualiza en cualquier momento con <parentElem>.style.setProperty('--myProperty’, <value>). I

Esta es una demostración en la que, en unas pocas líneas de código, actualiza el --theme de un botón y aplica estilos mediante consultas de estilo y esa propiedad personalizada (--theme):

Aplica diseño a la tarjeta con consultas de estilo; el JavaScript que se usa para actualizar los valores de las propiedades personalizadas es el siguiente:

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

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

Las funciones detalladas en este artículo son solo el comienzo. Las consultas de contenedores ofrecen más beneficios que te ayudarán a crear interfaces dinámicas y responsivas. En cuanto a las consultas de estilo específicamente, todavía hay algunos problemas sin resolver. Una es la implementación de consultas de estilo para los estilos CSS más allá de las propiedades personalizadas. Esta opción ya forma parte del nivel de especificaciones actual, pero aún no se implementó en ningún navegador. Se espera que la evaluación de contexto booleano se agregue al nivel de especificaciones actual cuando el problema pendiente se resuelva, mientras que la consulta de rango está planificada para el siguiente nivel de la especificación.