Consultas de contenedor CSS: casos de uso y estrategias de migración
Publicado: 2022-03-10Cuando escribimos consultas de medios para un elemento de la interfaz de usuario, siempre describimos el estilo de ese elemento según las dimensiones de la pantalla. Este enfoque funciona bien cuando la capacidad de respuesta de la consulta de medios del elemento de destino solo debe depender del tamaño de la ventana gráfica. Echemos un vistazo al siguiente ejemplo de diseño de página adaptable.
Sin embargo, el diseño web receptivo (RWD) no se limita a un diseño de página: los componentes individuales de la interfaz de usuario generalmente tienen consultas de medios que pueden cambiar su estilo según las dimensiones de la ventana gráfica.
Es posible que ya haya notado un problema con la declaración anterior: el diseño de los componentes de la interfaz de usuario individual a menudo no depende exclusivamente de las dimensiones de la ventana gráfica. Mientras que el diseño de la página es un elemento estrechamente relacionado con las dimensiones de la ventana gráfica y es uno de los elementos principales en HTML, los componentes de la interfaz de usuario se pueden usar en diferentes contextos y contenedores. Si lo piensa, la ventana gráfica es solo un contenedor, y los componentes de la interfaz de usuario se pueden anidar dentro de otros contenedores con estilos que afectan las dimensiones y el diseño del componente.
Aunque se usa el mismo componente de tarjeta de producto en las secciones superior e inferior, los estilos de los componentes no solo dependen de las dimensiones de la ventana gráfica, sino también del contexto y las propiedades CSS del contenedor (como la cuadrícula en el ejemplo) donde se coloca.
Por supuesto, podemos estructurar nuestro CSS para admitir las variaciones de estilo para diferentes contextos y contenedores para abordar el problema del diseño manualmente. En el peor de los casos, esta variación se agregaría con anulación de estilo, lo que conduciría a problemas de especificidad y duplicación de código.
.product-card { /* Default card style */ } .product-card--narrow { /* Style variation for narrow viewport and containers */ } @media screen and (min-width: 569px) { .product-card--wide { /* Style variation for wider viewport and containers */ } }
Sin embargo, esto es más una solución para las limitaciones de las consultas de medios que una solución adecuada. Cuando escribimos consultas de medios para elementos de la interfaz de usuario, intentamos encontrar un valor de ventana gráfica "mágico" para un punto de interrupción cuando el elemento de destino tiene dimensiones mínimas donde el diseño no se rompe. En resumen, estamos vinculando un valor de dimensión de ventana gráfica "mágico" al valor de dimensiones del elemento . Este valor suele ser diferente de la dimensión de la ventana gráfica y es propenso a errores cuando cambian las dimensiones del contenedor interno o el diseño.
El siguiente ejemplo muestra este problema exacto: aunque se implementó un elemento de tarjeta de producto receptivo y se ve bien en un caso de uso estándar, se ve roto si se mueve a un contenedor diferente con propiedades CSS que afectan las dimensiones del elemento. Cada caso de uso adicional requiere que se agregue un código CSS adicional, lo que puede conducir a un código duplicado, a un exceso de código y a un código que es difícil de mantener.
Este es uno de los problemas que las consultas de contenedor intentan solucionar. Las consultas de contenedores amplían la funcionalidad de las consultas de medios existentes con consultas que dependen de las dimensiones del elemento de destino. Hay tres ventajas principales de utilizar este enfoque:
- Los estilos de consulta de contenedor se aplican según las dimensiones del propio elemento de destino. Los componentes de la interfaz de usuario podrán adaptarse a cualquier contexto o contenedor dado.
- Los desarrolladores no necesitarán buscar un valor de dimensión de ventana gráfica de "número mágico" que vincule una consulta de medios de ventana gráfica a una dimensión de destino del componente de la interfaz de usuario en un contenedor específico o un contexto específico.
- No es necesario agregar clases de CSS adicionales o consultas de medios para diferentes contextos y casos de uso.
“El sitio web receptivo ideal es un sistema de componentes flexibles y modulares que se pueden reutilizar para servir en múltiples contextos”.
— “Consultas sobre contenedores: una vez más hasta la brecha”, Mat Marquis
Antes de profundizar en las consultas de contenedores, debemos verificar el soporte del navegador y ver cómo podemos habilitar la función experimental en nuestro navegador.
Compatibilidad con navegador
Las consultas de contenedores son una función experimental, disponible actualmente en la versión Chrome Canary en el momento de escribir este artículo. Si desea seguir y ejecutar los ejemplos de CodePen en este artículo, deberá habilitar las consultas de contenedor en la siguiente URL de configuración.
chrome://flags/#enable-container-queries
En caso de que esté utilizando un navegador que no admite consultas de contenedor, se proporcionará una imagen que muestra el ejemplo de trabajo previsto junto con la demostración de CodePen.
Trabajar con consultas de contenedores
Las consultas de contenedores no son tan sencillas como las consultas de medios regulares. Tendremos que agregar una línea adicional de código CSS a nuestro elemento de la interfaz de usuario para que las consultas de contenedores funcionen, pero hay una razón para eso y lo cubriremos a continuación.
Propiedad de contención
La propiedad de contain
de CSS se ha agregado a la mayoría de los navegadores modernos y tiene un 75% de soporte de navegador decente en el momento de escribir este artículo. La propiedad de contain
se usa principalmente para la optimización del rendimiento al indicarle al navegador qué partes (subárboles) de la página se pueden tratar como independientes y no afectarán los cambios en otros elementos de un árbol. De esa forma, si ocurre un cambio en un solo elemento, el navegador volverá a mostrar solo esa parte (subárbol) en lugar de la página completa. Con valores de propiedad de contain
, podemos especificar qué tipos de contención queremos usar: layout
, size
o paint
.
Hay muchos artículos excelentes sobre la propiedad de contain
que describen las opciones disponibles y los casos de uso con mucho más detalle, por lo que me centraré solo en las propiedades relacionadas con las consultas de contenedores.
¿Qué tiene que ver la propiedad de satisfacción de CSS que se usa para la optimización con las consultas de contenedor? Para que las consultas de contenedores funcionen, el navegador necesita saber si se produce un cambio en el diseño de los elementos secundarios que debe volver a representar solo ese componente. El navegador sabrá aplicar el código en la consulta del contenedor al componente coincidente cuando se representa el componente o cambia la dimensión del componente.
Usaremos los style
de layout
estilo para la contain
, pero también necesitaremos un valor adicional que señale al navegador sobre el eje en el que ocurrirá el cambio.
-
inline-size
Contención en el eje en línea. Se espera que este valor tenga muchos más casos de uso, por lo que se implementará primero. -
block-size
Contención en eje de bloque. Todavía está en desarrollo y no está disponible actualmente.
Una desventaja menor de la propiedad de contain
es que nuestro elemento de diseño debe ser un elemento secundario de un elemento de contain
, lo que significa que estamos agregando un nivel de anidamiento adicional.
<section> <article class="card"> <div class="card__wrapper"> <!-- Card content --> </div> </article> </section>
.card { contain: layout inline-size style; } .card__wrapper { display: grid; grid-gap: 1.5em; grid-template-rows: auto auto; /* ... */ }
Observe cómo no agregamos este valor a una section
similar a un padre más distante y mantenemos el contenedor lo más cerca posible del elemento afectado.
“El rendimiento es el arte de evitar el trabajo y hacer que cualquier trabajo que realice sea lo más eficiente posible. En muchos casos, se trata de trabajar con el navegador, no contra él”.
— “Rendimiento de interpretación”, Paul Lewis
Es por eso que debemos señalar correctamente al navegador sobre el cambio. Envolver un elemento principal distante con una propiedad de contain
puede ser contraproducente y afectar negativamente el rendimiento de la página. En el peor de los casos de mal uso de la propiedad de contain
, el diseño puede incluso romperse y el navegador no lo representará correctamente.
Consulta de contenedor
Después de agregar la propiedad de contain
al envoltorio del elemento de la tarjeta, podemos escribir una consulta de contenedor. Agregamos una propiedad de contain
a un elemento con clase de card
, por lo que ahora podemos incluir cualquiera de sus elementos secundarios en una consulta de contenedor.
Al igual que con las consultas de medios regulares, necesitamos definir una consulta usando propiedades de ancho min-width
o ancho max-width
y anidar todos los selectores dentro del bloque. Sin embargo, usaremos la palabra clave @container
en lugar de @media
para definir una consulta de contenedor.
@container (min-width: 568px) { .card__wrapper { align-items: center; grid-gap: 1.5em; grid-template-rows: auto; grid-template-columns: 150px auto; } .card__image { min-width: auto; height: auto; } }
Tanto el elemento card__wrapper
como card__image
son elementos secundarios del elemento card
que tiene definida la propiedad de contain
. Cuando reemplazamos las consultas de medios regulares con consultas de contenedores, eliminamos las clases CSS adicionales para contenedores estrechos y ejecutamos el ejemplo de CodePen en un navegador que admite consultas de contenedores, obtenemos el siguiente resultado.
Tenga en cuenta que las consultas de contenedores actualmente no aparecen en las herramientas para desarrolladores de Chrome , lo que dificulta un poco la depuración de consultas de contenedores. Se espera que el soporte de depuración adecuado se agregue al navegador en el futuro.
Puede ver cómo las consultas de contenedor nos permiten crear componentes de interfaz de usuario más robustos y reutilizables que pueden adaptarse a prácticamente cualquier contenedor y diseño. Sin embargo, el soporte adecuado del navegador para las consultas de contenedores aún está lejos en la función. Probemos y veamos si podemos implementar consultas de contenedores mediante la mejora progresiva.
Realce Progresivo y Polyfills
Veamos si podemos agregar una alternativa a la variación de clases de CSS y las consultas de medios. Podemos usar consultas de funciones CSS con la regla @supports
para detectar las funciones disponibles del navegador. Sin embargo, no podemos verificar otras consultas, por lo que debemos agregar una verificación para un contain: layout inline-size style
. Tendremos que asumir que los navegadores que admiten la propiedad inline-size
también admiten consultas de contenedores.
/* Check if the inline-size value is supported */ @supports (contain: inline-size) { .card { contain: layout inline-size style; } } /* If the inline-size value is not supported, use media query fallback */ @supports not (contain: inline-size) { @media (min-width: 568px) { /* ... */ } } /* Browser ignores @container if it's not supported */ @container (min-width: 568px) { /* Container query styles */ }
Sin embargo, este enfoque podría dar lugar a estilos duplicados, ya que la consulta de contenedor y la consulta de medios aplican los mismos estilos. Si decide implementar consultas de contenedores con mejoras progresivas, querrá usar un preprocesador de CSS como SASS o un posprocesador como PostCSS para evitar la duplicación de bloques de código y usar combinaciones de CSS u otro enfoque en su lugar.
Dado que esta especificación de consulta de contenedor aún se encuentra en una fase experimental, es importante tener en cuenta que la especificación o implementación es propensa a cambiar en versiones futuras.
Alternativamente, puede usar polyfills para proporcionar un respaldo confiable. Hay dos polyfills de JavaScript que me gustaría destacar, que actualmente parecen mantenerse activamente y brindan las funciones de consulta de contenedor necesarias:
-
cqfill
por jonathan neal
Polyfill JavaScript para CSS y PostCSS -
react-container-query
por Chris Garcia
Gancho y componente personalizados para React
Migración de consultas de medios a consultas de contenedores
Si decide implementar consultas de contenedor en un proyecto existente que usa consultas de medios, deberá refactorizar el código HTML y CSS. Descubrí que esta es la forma más rápida y sencilla de agregar consultas de contenedores al tiempo que proporciona una alternativa confiable a las consultas de medios. Echemos un vistazo al ejemplo de tarjeta anterior.
<section> <div class="card__wrapper card__wrapper--wide"> <!-- Wide card content --> </div> </section> /* ... */ <aside> <div class="card__wrapper"> <!-- Narrow card content --> </div> </aside>
.card__wrapper { display: grid; grid-gap: 1.5em; grid-template-rows: auto auto; /* ... */ } .card__image { /* ... */ } @media screen and (min-width: 568px) { .card__wrapper--wide { align-items: center; grid-gap: 1.5em; grid-template-rows: auto; grid-template-columns: 150px auto; } .card__image { /* ... */ } }
Primero, envuelva el elemento HTML raíz que tiene una consulta de medios aplicada con un elemento que tenga la propiedad de contain
.
<section> <article class="card"> <div class="card__wrapper"> <!-- Card content --> </div> </article> </section>
@supports (contain: inline-size) { .card { contain: layout inline-size style; } }
A continuación, envuelva una consulta de medios en una consulta de características y agregue una consulta de contenedor.
@supports not (contain: inline-size) { @media (min-width: 568px) { .card__wrapper--wide { /* ... */ } .card__image { /* ... */ } } } @container (min-width: 568px) { .card__wrapper { /* Same code as .card__wrapper--wide in media query */ } .card__image { /* Same code as .card__image in media query */ } }
Si bien este método da como resultado un exceso de código y un código duplicado, al usar SASS o PostCSS puede evitar la duplicación del código de desarrollo, por lo que el código fuente de CSS sigue siendo mantenible.
Una vez que las consultas de contenedor reciban el soporte adecuado del navegador, es posible que desee considerar eliminar los bloques de código @supports not (contain: inline-size)
y continuar admitiendo consultas de contenedor exclusivamente.
Stephanie Eckles ha publicado recientemente un excelente artículo sobre consultas de contenedores que cubre varias estrategias de migración. Recomiendo revisarlo para más información sobre el tema.
Escenarios de casos de uso
Como hemos visto en los ejemplos anteriores, las consultas de contenedor se usan mejor para componentes altamente reutilizables con un diseño que depende del espacio disponible del contenedor y que se puede usar en varios contextos y agregar a diferentes contenedores en la página.
Otros ejemplos incluyen (los ejemplos requieren un navegador que admita consultas de contenedores):
- Componentes modulares como tarjetas, elementos de formulario, pancartas, etc.
- Diseños adaptables
- Paginación con diferentes funcionalidades para móvil y escritorio
- Experimentos divertidos con el cambio de tamaño de CSS
Conclusión
Una vez que la especificación se haya implementado y sea ampliamente admitida en los navegadores, las consultas de contenedores pueden convertirse en una característica que cambie el juego. Permitirá a los desarrolladores escribir consultas a nivel de componente, acercando las consultas a los componentes relacionados, en lugar de utilizar las consultas de medios de visualización distantes y apenas relacionadas. Esto dará como resultado componentes más robustos, reutilizables y mantenibles que podrán adaptarse a varios casos de uso, diseños y contenedores.
En su forma actual, las consultas de contenedores aún se encuentran en una fase experimental temprana y la implementación es propensa a cambios. Si desea comenzar a usar consultas de contenedor en sus proyectos hoy, deberá agregarlas mediante la mejora progresiva con detección de características o usar un polyfill de JavaScript. Ambos casos darán como resultado una sobrecarga en el código, por lo que si decide utilizar consultas de contenedor en esta fase inicial, asegúrese de planificar la refactorización del código una vez que la característica sea ampliamente compatible.
Referencias
- "Consultas de contenedores: una guía de inicio rápido" por David A. Herron
- “Diga hola a las consultas de contenedores CSS”, Ahmad Shadeed
- “Contención de CSS en Chrome 52”, Paul Lewis
- “Ayudando a los navegadores a optimizar con la propiedad CSS Contain”, Rachel Andrew