Cómo comenzar a usar las consultas de estilo

Recientemente, la capacidad de consultar el tamaño intercalado de un elemento superior y los valores de las unidades de consulta del contenedor alcanzó la compatibilidad estable en todos los motores del navegador modernos.

Navegadores compatibles

  • 105
  • 105
  • 110
  • 16

Origen

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

Comienza a usar consultas de diseño

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 estilo, primero debes configurar un elemento contenedor. Esto requiere un enfoque ligeramente diferente que dependa de si consultas una entidad superior directa o indirecta.

Realiza consultas a padres directos

Diagrama de una consulta de estilo.

A diferencia de las consultas de estilo, no necesitas aplicar contención usando la propiedad 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, valores de propiedad personalizada) a un contenedor (.card-container en este caso) o a cualquier elemento que contenga el elemento al que aplicaremos diseño en el DOM. No podemos aplicar los estilos que consultamos al elemento directo al que le aplicamos el estilo con esa consulta, ya que esto podría causar bucles infinitos.

Para realizar una consulta directa a una madre, 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 permite 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 fuera de al menos 200 px de ancho. Sin embargo, min-width también puede ser una propiedad de CSS, y puedes buscar el valor de CSS de min-width mediante consultas de estilo. Por eso, debes usar el wrapper style() para que la diferencia sea clara: @container style(min-width: 200px) { … }.

Diseño de productos superiores no directos

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

/* 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 los contenedores para que quede claro lo que estás consultando y puedas acceder a ellos con mayor facilidad. Esto resulta útil, por ejemplo, si deseas definir el estilo de elementos dentro de .card directamente. Sin un contenedor con nombre en .card-container, no puede consultarlo directamente.

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

Consultas sobre diseños en acción

Imagen de demostración con varias tarjetas de productos, algunas con etiquetas &quot;nuevos&quot; o &quot;de 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 tus 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 "Poco stock", que se activan mediante una propiedad personalizada llamada --detail. Además, si un producto está en stock, obtiene un fondo de 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 con 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. Esta función se encuentra debatiendo actualmente en el grupo de trabajo.

Tarjetas del clima

En el ejemplo anterior, se usó una sola propiedad personalizada con varios valores posibles para aplicar estilos. Pero también puedes combinarlo usando y consultando varias propiedades personalizadas. Observa este ejemplo de tarjeta del clima:

Demostración de tarjetas del clima

Para definir el estilo de los íconos y gradientes de fondo de estas tarjetas, busca las características del clima, 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 diseñar para combinaciones de características (propiedad personalizada) usando la combinación and de la misma manera que para 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 datos del diseño

En ambas demostraciones, se obtiene un beneficio estructural separar la capa de datos (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 (configurando --rainy, --cloudy o --sunny. Y la mejor parte es que también puedes combinar estos valores. Verificar --sunny y --cloudy podría mostrar un estilo parcialmente nublado.

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

A continuación, se muestra una demostración que, con unas pocas líneas de código, actualiza el --theme de un botón y aplica estilos con consultas de estilo y esa propiedad personalizada (--theme):

Diseña la tarjeta con consultas de estilo. El código 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 que se detallan en este artículo son solo el comienzo. Puedes esperar que las consultas de contenedor te ayuden a compilar interfaces dinámicas y responsivas. En cuanto a las consultas de estilo específicamente, aún hay algunos problemas sin resolver. Una es la implementación de consultas de estilo para estilos CSS más allá de las propiedades personalizadas. Esto ya forma parte del nivel de especificaciones actual, pero aún no se implementó en ningún navegador. Se espera que la evaluación del contexto booleano se agregue al nivel de especificación actual cuando se resuelva el problema pendiente, mientras que la consulta de rango está planificada para el siguiente nivel de la especificación.