Query sui contenitori CSS: casi d'uso e strategie di migrazione
Pubblicato: 2022-03-10Quando 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.
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.
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.
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.
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.
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
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.
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.
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.
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