Query sui contenitori CSS: casi d'uso e strategie di migrazione

Pubblicato: 2022-03-10
Riepilogo rapido ↬ Le query CSS Container avvicinano le query multimediali agli elementi di destinazione stessi e consentono loro di adattarsi praticamente a qualsiasi container o layout. In questo articolo, tratteremo le nozioni di base sulle query del contenitore CSS e come utilizzarle oggi con il miglioramento progressivo o i polyfill.

Quando scriviamo query multimediali per un elemento dell'interfaccia utente, descriviamo sempre lo stile di tale elemento in base alle dimensioni dello schermo. Questo approccio funziona bene quando la reattività della query multimediale dell'elemento di destinazione dovrebbe dipendere solo dalle dimensioni del viewport. Diamo un'occhiata al seguente esempio di layout di pagina reattivo.

Esempio di layout di pagina reattivo. L'immagine a sinistra mostra il layout del desktop con una larghezza del viewport di 1280px e l'immagine di destra mostra il layout per dispositivi mobili con una larghezza del viewport di 568px.
Esempio di layout di pagina reattivo. L'immagine a sinistra mostra il layout del desktop con una larghezza del viewport di 1280px e l'immagine di destra mostra il layout per dispositivi mobili con una larghezza del viewport di 568px. (Grande anteprima)

Tuttavia, il responsive Web Design (RWD) non si limita a un layout di pagina: i singoli componenti dell'interfaccia utente di solito hanno query multimediali che possono cambiare il loro stile a seconda delle dimensioni del viewport.

Esempio di componente di scheda prodotto reattiva. L'immagine a sinistra mostra un componente con una larghezza della vista di 720 px e l'immagine a destra mostra il layout del componente con una larghezza della vista di 568 px.
Esempio di componente di scheda prodotto reattiva. L'immagine a sinistra mostra un componente con una larghezza della vista di 720 px e l'immagine a destra mostra il layout del componente con una larghezza della vista di 568 px. (Grande anteprima)

Potresti aver già notato un problema con l'affermazione precedente: il layout dei singoli componenti dell'interfaccia utente spesso non dipende esclusivamente dalle dimensioni della finestra. Mentre il layout di pagina è un elemento strettamente legato alle dimensioni del viewport ed è uno degli elementi più importanti in HTML, i componenti dell'interfaccia utente possono essere utilizzati in diversi contesti e contenitori. Se ci pensi, la finestra è solo un contenitore e i componenti dell'interfaccia utente possono essere nidificati all'interno di altri contenitori con stili che influiscono sulle dimensioni e sul layout del componente.

Esempio di layout di pagina con lo stesso componente dell'interfaccia utente della scheda prodotto nella griglia a 3 colonne della sezione superiore e nell'elenco delle sezioni inferiori.
Esempio di layout di pagina con lo stesso componente dell'interfaccia utente della scheda prodotto nella griglia a 3 colonne della sezione superiore e nell'elenco delle sezioni inferiori. (Grande anteprima)

Anche se lo stesso componente della scheda prodotto viene utilizzato sia nella sezione superiore che in quella inferiore, gli stili dei componenti non dipendono solo dalle dimensioni del viewport, ma dipendono anche dal contesto e dalle proprietà CSS del contenitore (come la griglia nell'esempio) in cui è posizionato.

Naturalmente, possiamo strutturare il nostro CSS in modo da supportare le variazioni di stile per diversi contesti e contenitori per affrontare manualmente il problema del layout. Nello scenario peggiore, questa variazione verrebbe aggiunta con la sostituzione dello stile che porterebbe a problemi di duplicazione del codice e di specificità.

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

Tuttavia, questa è più una soluzione alternativa per i limiti delle query multimediali piuttosto che una soluzione adeguata. Quando scriviamo media query per elementi dell'interfaccia utente, stiamo cercando di trovare un valore di viewport "magico" per un punto di interruzione quando l'elemento di destinazione ha dimensioni minime in cui il layout non si interrompe. In breve, stiamo collegando un valore di dimensione della finestra "magica" al valore delle dimensioni dell'elemento . Questo valore è in genere diverso dalla dimensione della finestra ed è soggetto a bug quando le dimensioni o il layout del contenitore interno vengono modificati.

Esempio di come la media query non può essere collegata in modo affidabile alle dimensioni dell'elemento. Varie proprietà CSS possono influenzare le dimensioni degli elementi all'interno di un contenitore. In questo esempio, il riempimento del contenitore è diverso tra le due immagini.
Esempio di come la media query non può essere collegata in modo affidabile alle dimensioni dell'elemento. Varie proprietà CSS possono influenzare le dimensioni degli elementi all'interno di un contenitore. In questo esempio, il riempimento del contenitore è diverso tra le due immagini. (Grande anteprima)

L'esempio seguente mostra esattamente questo problema: anche se è stato implementato un elemento di scheda prodotto reattivo e sembra buono in un caso d'uso standard, sembra danneggiato se viene spostato in un contenitore diverso con proprietà CSS che influiscono sulle dimensioni dell'elemento. Ogni caso d'uso aggiuntivo richiede l'aggiunta di codice CSS aggiuntivo che può portare a codice duplicato, bloat del codice e codice difficile da mantenere.

Guarda la penna [Schede prodotto: contenitori vari](https://codepen.io/smashingmag/pen/eYvWVxz) di Adrian Bece.

Vedi le schede prodotto della penna: vari contenitori di Adrian Bece.

Questo è uno dei problemi che le query del contenitore tentano di risolvere. Le query contenitore estendono la funzionalità delle query multimediali esistenti con query che dipendono dalle dimensioni dell'elemento di destinazione. Ci sono tre vantaggi principali dell'utilizzo di questo approccio:

  • Gli stili di query del contenitore vengono applicati in base alle dimensioni dell'elemento di destinazione stesso. I componenti dell'interfaccia utente saranno in grado di adattarsi a qualsiasi contesto o contenitore.
  • Gli sviluppatori non dovranno cercare un valore di dimensione viewport "numero magico" che colleghi una query multimediale viewport a una dimensione di destinazione del componente dell'interfaccia utente in un contenitore specifico o in un contesto specifico.
  • Non è necessario aggiungere classi CSS aggiuntive o query multimediali per contesti e casi d'uso diversi.
"Il sito Web reattivo ideale è un sistema di componenti flessibili e modulari che possono essere riproposti per essere utilizzati in più contesti".

— "Domande sui container: ancora una volta la violazione", Mat Marquis

Prima di approfondire le query sui contenitori, dobbiamo controllare il supporto del browser e vedere come possiamo abilitare la funzione sperimentale nel nostro browser.

Supporto del browser

Le query sui contenitori sono una funzionalità sperimentale, attualmente disponibile nella versione Chrome Canary al momento della stesura di questo articolo. Se vuoi seguire ed eseguire gli esempi CodePen in questo articolo, dovrai abilitare le query contenitore nell'URL delle impostazioni seguente.

 chrome://flags/#enable-container-queries
Query sui contenitori
(Grande anteprima)

Nel caso in cui utilizzi un browser che non supporta le query contenitore, insieme alla demo di CodePen verrà fornita un'immagine che mostra l'esempio di lavoro previsto.

Altro dopo il salto! Continua a leggere sotto ↓

Lavorare con le query sui contenitori

Le query sui contenitori non sono così semplici come le normali query multimediali. Dovremo aggiungere una riga aggiuntiva di codice CSS al nostro elemento dell'interfaccia utente per far funzionare le query del contenitore, ma c'è una ragione per questo e lo tratteremo in seguito.

Proprietà di contenimento

La proprietà CSS contain è stata aggiunta alla maggior parte dei browser moderni e ha un supporto del browser decente del 75% al ​​momento della stesura di questo articolo. La proprietà contain viene utilizzata principalmente per l'ottimizzazione delle prestazioni suggerendo al browser quali parti (sottostrutture) della pagina possono essere trattate come indipendenti e non influiranno sulle modifiche ad altri elementi in una struttura ad albero. In questo modo, se si verifica una modifica in un singolo elemento, il browser eseguirà nuovamente il rendering solo di quella parte (sottostruttura) anziché dell'intera pagina. Con i valori delle proprietà di contain , possiamo specificare quali tipi di contenimento vogliamo utilizzare: layout , size o paint .

Ci sono molti ottimi articoli sulla proprietà contain che descrivono le opzioni disponibili e i casi d'uso in modo molto più dettagliato, quindi mi concentrerò solo sulle proprietà relative alle query del contenitore.

Che cosa ha a che fare la proprietà di contentment CSS utilizzata per l'ottimizzazione con le query contenitore? Affinché le query del contenitore funzionino, il browser deve sapere se si verifica una modifica nel layout figlio dell'elemento per eseguire nuovamente il rendering solo di quel componente. Il browser saprà applicare il codice nella query del contenitore al componente corrispondente quando viene eseguito il rendering del componente o quando la dimensione del componente cambia.

Utilizzeremo i style​ di layout​ style per la contain​ , ma avremo anche bisogno di un valore aggiuntivo che segnali al browser l'asse in cui avverrà la modifica.

  • inline-size
    Contenimento sull'asse in linea. Si prevede che questo valore abbia un numero significativamente maggiore di casi d'uso, quindi viene implementato per primo.
  • block-size
    Contenimento sull'asse del blocco. È ancora in fase di sviluppo e non è attualmente disponibile.

Uno svantaggio minore della proprietà contain è che il nostro elemento di layout deve essere figlio di un elemento di contain , il che significa che stiamo aggiungendo un livello di annidamento aggiuntivo.

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

Nota come non stiamo aggiungendo questo valore a una section simile a un genitore più distante e mantenendo il contenitore il più vicino possibile all'elemento interessato.

“La performance è l'arte di evitare il lavoro e di rendere il lavoro che si fa il più efficiente possibile. In molti casi si tratta di lavorare con il browser, non contro di esso".

— "Rendering Performance", Paul Lewis

Ecco perché dovremmo segnalare correttamente al browser la modifica. Il wrapping di un elemento padre distante con una proprietà contain può essere controproducente e influire negativamente sulle prestazioni della pagina. Negli scenari peggiori di uso contain della proprietà container, il layout potrebbe persino interrompersi e il browser non lo visualizzerà correttamente.

Interrogazione del contenitore

Dopo che la proprietà contain è stata aggiunta al wrapper dell'elemento card, possiamo scrivere una query contenitore. Abbiamo aggiunto una proprietà container a un elemento con classe card , quindi ora possiamo contain qualsiasi suo elemento figlio in una query contenitore.

Proprio come con le normali media query, è necessario definire una query utilizzando le proprietà min-width o max-width e annidare tutti i selettori all'interno del blocco. Tuttavia, utilizzeremo la parola chiave @container invece di @media per definire una query container.

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

Sia l'elemento card__wrapper che l'elemento card__image sono figli dell'elemento card che ha la proprietà contain definita. Quando sostituiamo le normali media query con le query contenitore, rimuoviamo le classi CSS aggiuntive per contenitori stretti ed eseguiamo l'esempio CodePen in un browser che supporta le query contenitore, otteniamo il seguente risultato.

In questo esempio, non stiamo ridimensionando la finestra, ma l'elemento contenitore <section> stesso a cui è stata applicata la proprietà CSS di ridimensionamento. Il componente passa automaticamente da un layout all'altro in base alle dimensioni del contenitore.
In questo esempio, non stiamo ridimensionando il viewport, ma l'elemento contenitore <section> stesso a cui è stata applicata la proprietà CSS di ridimensionamento. Il componente passa automaticamente da un layout all'altro in base alle dimensioni del contenitore. (Grande anteprima)

Vedi la penna [Schede prodotto: Container Query](https://codepen.io/smashingmag/pen/PopmQLV) di Adrian Bece.

Vedi le schede prodotto Pen: Container Query di Adrian Bece.

Tieni presente che le query contenitore al momento non vengono visualizzate negli strumenti per sviluppatori di Chrome , il che rende un po' difficile il debug delle query contenitore. Si prevede che il corretto supporto per il debug verrà aggiunto al browser in futuro.

Puoi vedere come le query sui container ci consentono di creare componenti dell'interfaccia utente più robusti e riutilizzabili che possono adattarsi praticamente a qualsiasi container e layout. Tuttavia, il supporto corretto del browser per le query del contenitore è ancora lontano nella funzionalità. Proviamo a vedere se possiamo implementare le query container utilizzando il miglioramento progressivo.

Miglioramento progressivo e Polyfill

Vediamo se possiamo aggiungere un fallback alla variazione della classe CSS e alle media query. Possiamo utilizzare le query sulle funzionalità CSS con la regola @supports per rilevare le funzionalità del browser disponibili. Tuttavia, non possiamo verificare la presenza di altre query, quindi è necessario aggiungere un controllo per contain: layout inline-size style . Dovremo presumere che i browser che supportano la proprietà inline-size supportino anche le query contenitore.

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

Tuttavia, questo approccio potrebbe portare a stili duplicati poiché gli stessi stili vengono applicati sia dalla query contenitore che dalla query multimediale. Se decidi di implementare le query container con un miglioramento progressivo, ti consigliamo di utilizzare un preprocessore CSS come SASS o un postprocessore come PostCSS per evitare la duplicazione di blocchi di codice e utilizzare invece i mixin CSS o un altro approccio.

Vedi la penna [Schede prodotto: query sui contenitori con miglioramento progressivo](https://codepen.io/smashingmag/pen/zYZwRXZ) di Adrian Bece.

Vedi le schede prodotto Pen: Container Query con il progressivo miglioramento di Adrian Bece.

Poiché questa specifica della query del contenitore è ancora in una fase sperimentale, è importante tenere presente che la specifica o l'implementazione è soggetta a modifiche nelle versioni future.

In alternativa, puoi utilizzare i polyfill per fornire un affidabile fallback. Ci sono due polyfill JavaScript che vorrei evidenziare, che attualmente sembrano essere attivamente mantenuti e forniscono le funzionalità di query del contenitore necessarie:

  • cqfill di Jonathan Neal
    Polyfill JavaScript per CSS e PostCSS
  • react-container-query di Chris Garcia
    Gancio e componente personalizzati per React

Migrazione dalle query multimediali alle query container

Se decidi di implementare query contenitore su un progetto esistente che utilizza query multimediali, dovrai eseguire il refactoring del codice HTML e CSS. Ho scoperto che questo è il modo più rapido e diretto per aggiungere query contenitore fornendo al contempo un affidabile fallback alle query multimediali. Diamo un'occhiata all'esempio di carta precedente.

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

In primo luogo, avvolgere l'elemento HTML radice a cui è applicata una query multimediale con un elemento che ha la proprietà contain .

 <section> <article class="card"> <div class="card__wrapper"> <!-- Card content --> </div> </article> </section>
 @supports (contain: inline-size) { .card { contain: layout inline-size style; } }

Quindi, avvolgi una query multimediale in una query di funzionalità e aggiungi una query contenitore.

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

Sebbene questo metodo si traduca in un bloat del codice e codice duplicato, utilizzando SASS o PostCSS è possibile evitare la duplicazione del codice di sviluppo, quindi il codice sorgente CSS rimane gestibile.

Una volta che le query container ricevono il supporto appropriato del browser, potresti prendere in considerazione la rimozione di blocchi di codice @supports not (contain: inline-size) e continuare a supportare esclusivamente le query container.

Stephanie Eckles ha recentemente pubblicato un ottimo articolo sulle query sui container che copre varie strategie di migrazione. Consiglio di dare un'occhiata per ulteriori informazioni sull'argomento.

Scenari di casi d'uso

Come abbiamo visto dagli esempi precedenti, le query contenitore vengono utilizzate al meglio per componenti altamente riutilizzabili con un layout che dipende dallo spazio contenitore disponibile e che può essere utilizzato in vari contesti e aggiunto a contenitori diversi nella pagina.

Altri esempi includono (gli esempi richiedono un browser che supporti le query contenitore):

  • Componenti modulari come carte, elementi modulo, banner, ecc.
  • Layout adattabili
  • Impaginazione con diverse funzionalità per mobile e desktop
  • Divertenti esperimenti con il ridimensionamento CSS

Conclusione

Una volta che le specifiche sono state implementate e ampiamente supportate nei browser, le query del contenitore potrebbero diventare una funzionalità rivoluzionaria. Consentirà agli sviluppatori di scrivere query a livello di componente, spostando le query più vicino ai componenti correlati, invece di utilizzare le query multimediali viewport distanti e poco correlate. Ciò si tradurrà in componenti più robusti, riutilizzabili e manutenibili che saranno in grado di adattarsi a vari casi d'uso, layout e contenitori.

Allo stato attuale, le query container sono ancora in una fase sperimentale iniziale e l'implementazione è soggetta a modifiche. Se desideri iniziare a utilizzare le query contenitore nei tuoi progetti oggi, dovrai aggiungerle utilizzando il miglioramento progressivo con il rilevamento delle funzionalità o utilizzare un polyfill JavaScript. Entrambi i casi comporteranno un sovraccarico nel codice, quindi se decidi di utilizzare le query del contenitore in questa fase iniziale, assicurati di pianificare il refactoring del codice una volta che la funzionalità sarà ampiamente supportata.

Riferimenti

  • "Query sui contenitori: una guida rapida" di David A. Herron
  • "Saluta le query sui contenitori CSS", Ahmad Shadeed
  • "Contenimento CSS in Chrome 52", Paul Lewis
  • "Aiutare i browser a ottimizzare con la proprietà Conte CSS", Rachel Andrew