Interogări de containere CSS: cazuri de utilizare și strategii de migrare
Publicat: 2022-03-10Când scriem interogări media pentru un element UI, descriem întotdeauna modul în care este stilat acel element în funcție de dimensiunile ecranului. Această abordare funcționează bine atunci când capacitatea de răspuns a interogării media elementului țintă ar trebui să depindă numai de dimensiunea ferestrei de vizualizare. Să aruncăm o privire la următorul exemplu de aspect al paginii receptive.
Cu toate acestea, responsive Web Design (RWD) nu se limitează la un aspect de pagină - componentele individuale ale interfeței de utilizator au de obicei interogări media care își pot schimba stilul în funcție de dimensiunile ferestrei de vizualizare.
Este posibil să fi observat deja o problemă cu declarația anterioară - aspectul individual al componentei UI adesea nu depinde exclusiv de dimensiunile ferestrei de vizualizare. În timp ce aspectul paginii este un element strâns legat de dimensiunile ferestrei de vizualizare și este unul dintre elementele de top din HTML, componentele UI pot fi utilizate în diferite contexte și containere. Dacă vă gândiți bine, fereastra este doar un container, iar componentele UI pot fi imbricate în alte containere cu stiluri care afectează dimensiunile și aspectul componentei.
Chiar dacă aceeași componentă a cardului de produs este utilizată atât în secțiunea de sus, cât și în cea de jos, stilurile componente nu depind numai de dimensiunile ferestrei de vizualizare, ci și de context și de proprietățile CSS ale containerului (cum ar fi grila din exemplu) unde este plasat.
Desigur, ne putem structura CSS astfel încât să acceptăm variațiile de stil pentru diferite contexte și containere pentru a aborda manual problema aspectului. În cel mai rău caz, această variație ar fi adăugată cu modificarea stilului, ceea ce ar duce la duplicarea codului și probleme de specificitate.
.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 */ } }
Cu toate acestea, aceasta este mai degrabă o soluție pentru limitările interogărilor media decât o soluție adecvată. Când scriem interogări media pentru elementele UI, încercăm să găsim o valoare „magică” a ferestrei de vizualizare pentru un punct de întrerupere când elementul țintă are dimensiuni minime în care aspectul nu se întrerupe. Pe scurt, conectăm o valoare „magică” a dimensiunii ferestrei de vizualizare la valoarea dimensiunilor elementului . Această valoare este de obicei diferită de dimensiunea ferestrei de vizualizare și este predispusă la erori atunci când dimensiunile containerului interior sau aspectul se modifică.
Următorul exemplu prezintă exact această problemă – chiar dacă a fost implementat un element de card de produs receptiv și arată bine într-un caz de utilizare standard, pare stricat dacă este mutat într-un container diferit cu proprietăți CSS care afectează dimensiunile elementului. Fiecare caz de utilizare suplimentar necesită adăugarea unui cod CSS suplimentar , ceea ce poate duce la cod duplicat, umflare de cod și cod dificil de întreținut.
Aceasta este una dintre problemele pe care interogările de container încearcă să le rezolve. Interogările container extind funcționalitatea interogărilor media existente cu interogări care depind de dimensiunile elementului țintă. Există trei beneficii majore ale utilizării acestei abordări:
- Stilurile de interogare container sunt aplicate în funcție de dimensiunile elementului țintă însuși. Componentele UI se vor putea adapta la orice context sau container dat.
- Dezvoltatorii nu vor trebui să caute o valoare pentru dimensiunea ferestrei de vizualizare „număr magic” care leagă o interogare media de vizualizare la o dimensiune țintă a componentei UI într-un anumit container sau într-un anumit context.
- Nu este nevoie să adăugați clase CSS suplimentare sau interogări media pentru diferite contexte și cazuri de utilizare.
„Site-ul web adaptabil ideal este un sistem de componente flexibile, modulare, care pot fi reutilizate pentru a servi în mai multe contexte.”
— „Interogări privind containerele: încă o dată la breche”, Mat Marquis
Înainte de a ne aprofunda în interogările containerului, trebuie să verificăm suportul pentru browser și să vedem cum putem activa funcția experimentală în browserul nostru.
Suport pentru browser
Interogările containerului sunt o funcție experimentală, disponibilă în prezent în versiunea Chrome Canary la momentul scrierii acestui articol. Dacă doriți să urmați și să rulați exemplele CodePen din acest articol, va trebui să activați interogările containerului în următoarea adresă URL de setări.
chrome://flags/#enable-container-queries
În cazul în care utilizați un browser care nu acceptă interogări de container, o imagine care prezintă exemplul de lucru dorit va fi furnizată alături de demonstrația CodePen.
Lucrul cu interogări container
Interogările de containere nu sunt la fel de simple ca interogările media obișnuite. Va trebui să adăugăm o linie suplimentară de cod CSS la elementul nostru UI pentru a face ca interogările containerului să funcționeze, dar există un motiv pentru asta și îl vom trata în continuare.
Proprietatea de izolare
Proprietatea contain
CSS a fost adăugată la majoritatea browserelor moderne și are un suport decent pentru browser de 75% la momentul scrierii acestui articol. Proprietatea contain
este utilizată în principal pentru optimizarea performanței, indicând browserului care părți (subarbori) ale paginii pot fi tratate ca independente și nu vor afecta modificările altor elemente dintr-un arbore. În acest fel, dacă are loc o modificare într-un singur element, browserul va reda doar acea parte (subtree) în loc de întreaga pagină. Cu valorile proprietății contain
, putem specifica ce tipuri de izolare dorim să folosim — layout
, size
sau paint
.
Există multe articole grozave despre proprietatea contain
care descriu opțiunile disponibile și cazurile de utilizare mult mai detaliat, așa că mă voi concentra doar pe proprietățile legate de interogările containerului.
Ce legătură are proprietatea CSS contentment care este utilizată pentru optimizare cu interogările containerului? Pentru ca interogările containerului să funcționeze, browserul trebuie să știe dacă are loc o modificare în aspectul secundar al elementului și ar trebui să redea doar acea componentă. Browserul va ști să aplice codul din interogarea containerului la componenta potrivită atunci când componenta este redată sau dimensiunea componentei se schimbă.
Vom folosi valorile de layout
style
pentru contain
, dar vom avea nevoie și de o valoare suplimentară care să semnaleze browser-ul despre axa în care va avea loc modificarea.
-
inline-size
Reținere pe axa în linie. Este de așteptat ca această valoare să aibă mult mai multe cazuri de utilizare, așa că este implementată mai întâi. -
block-size
Reținere pe axa blocului. Este încă în dezvoltare și nu este disponibil momentan.
Un dezavantaj minor al proprietății contain
este că elementul nostru de aspect trebuie să fie un copil al unui element contain
, ceea ce înseamnă că adăugăm un nivel suplimentar de imbricare.
<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; /* ... */ }
Observați cum nu adăugăm această valoare la o section
mai îndepărtată asemănătoare părintelui și păstrăm containerul cât mai aproape de elementul afectat.
„Performanța este arta de a evita munca și de a face orice lucru pe care îl faci cât mai eficient posibil. În multe cazuri, este vorba despre lucrul cu browserul, nu împotriva lui.”
— „Performanță de redare”, Paul Lewis
De aceea ar trebui să semnalăm corect browser-ul despre schimbare. Încheierea unui element părinte îndepărtat cu o proprietate contain
poate fi contraproductivă și poate afecta negativ performanța paginii. În cel mai rău caz de utilizare abuzivă a proprietății de contain
, aspectul se poate chiar rupe și browserul nu îl va reda corect.
Interogare container
După ce proprietatea contain
a fost adăugată în învelișul elementului cardului, putem scrie o interogare de container. Am adăugat o proprietate contain
la un element cu clasă card
, așa că acum putem include oricare dintre elementele sale secundare într-o interogare container.
La fel ca și în cazul interogărilor media obișnuite, trebuie să definim o interogare folosind proprietăți min-width
max-width
și să plasăm toți selectorii în interiorul blocului. Cu toate acestea, vom folosi cuvântul cheie @container
în loc de @media
pentru a defini o interogare de 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; } }
Atât card__wrapper
, cât și card__image
element contain
copii ai elementului card
care are proprietatea container definită. Când înlocuim interogările media obișnuite cu interogări container, eliminăm clasele CSS suplimentare pentru containere înguste și rulăm exemplul CodePen într-un browser care acceptă interogări container, obținem următorul rezultat.
Rețineți că interogările containerului nu apar momentan în instrumentele pentru dezvoltatori Chrome , ceea ce face puțin dificilă depanarea interogărilor containerului. Este de așteptat ca suportul adecvat de depanare să fie adăugat în browser în viitor.
Puteți vedea cum interogările de containere ne permit să creăm componente UI mai robuste și mai reutilizabile, care se pot adapta la aproape orice container și aspect. Cu toate acestea, suportul adecvat al browserului pentru interogările containerului este încă departe în funcție. Să încercăm să vedem dacă putem implementa interogări container folosind îmbunătățirea progresivă.
Îmbunătățire progresivă și poliumplere
Să vedem dacă putem adăuga o alternativă la variația clasei CSS și interogările media. Putem folosi interogări de caracteristici CSS cu regula @supports
pentru a detecta funcțiile disponibile ale browserului. Cu toate acestea, nu putem verifica alte interogări, așa că trebuie să adăugăm o verificare pentru o valoare a contain: layout inline-size style
. Va trebui să presupunem că browserele care acceptă proprietăți inline-size
acceptă și interogări container.
/* 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 */ }
Cu toate acestea, această abordare poate duce la stiluri duplicate, deoarece aceleași stiluri sunt aplicate atât prin interogarea containerului, cât și prin interogarea media. Dacă decideți să implementați interogări de container cu îmbunătățire progresivă, ați dori să utilizați un pre-procesor CSS precum SASS sau un post-procesor precum PostCSS pentru a evita duplicarea blocurilor de cod și să utilizați mixuri CSS sau o altă abordare.
Deoarece această specificație de interogare a containerului este încă într-o fază experimentală, este important să rețineți că specificațiile sau implementarea sunt predispuse să se modifice în versiunile viitoare.
Alternativ, puteți utiliza polyfills pentru a oferi o rezervă fiabilă. Există două polyfill-uri JavaScript pe care aș dori să le evidențiez, care în prezent par a fi întreținute în mod activ și oferă caracteristicile necesare de interogare a containerului:
-
cqfill
de Jonathan Neal
Polyfill JavaScript pentru CSS și PostCSS -
react-container-query
de Chris Garcia
Cârlig și componentă personalizate pentru React
Migrarea de la interogări media la interogări container
Dacă decideți să implementați interogări container într-un proiect existent care utilizează interogări media, va trebui să refactorizați codul HTML și CSS. Am descoperit că acesta este cel mai rapid și mai simplu mod de a adăuga interogări container, oferind în același timp o soluție de încredere pentru interogările media. Să aruncăm o privire la exemplul de card 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 { /* ... */ } }
Mai întâi, împachetați elementul HTML rădăcină care are o interogare media aplicată cu un element care are proprietatea contain
.
<section> <article class="card"> <div class="card__wrapper"> <!-- Card content --> </div> </article> </section>
@supports (contain: inline-size) { .card { contain: layout inline-size style; } }
Apoi, includeți o interogare media într-o interogare de caracteristică și adăugați o interogare container.
@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 */ } }
Deși această metodă are ca rezultat o umflare a codului și cod duplicat, prin utilizarea SASS sau PostCSS puteți evita duplicarea codului de dezvoltare, astfel încât codul sursă CSS rămâne menținut.
Odată ce interogările container primesc suport adecvat pentru browser, este posibil să vă gândiți să eliminați blocurile de cod @supports not (contain: inline-size)
și să continuați să susțineți exclusiv interogări container.
Stephanie Eckles a publicat recent un articol grozav despre interogări de containere care acoperă diverse strategii de migrare. Recomand să-l verificați pentru mai multe informații despre subiect.
Scenarii de cazuri de utilizare
După cum am văzut din exemplele anterioare, interogările de container sunt cel mai bine utilizate pentru componentele foarte reutilizabile, cu un aspect care depinde de spațiul disponibil al containerului și care poate fi utilizat în diferite contexte și adăugat la diferite containere de pe pagină.
Alte exemple includ (exemplele necesită un browser care acceptă interogări container):
- Componente modulare precum carduri, elemente de formular, bannere etc.
- Aspecte adaptabile
- Paginare cu diferite funcționalități pentru mobil și desktop
- Experimente distractive cu redimensionarea CSS
Concluzie
Odată ce specificația a fost implementată și acceptată pe scară largă în browsere, interogările de containere pot deveni o caracteristică care schimbă jocul. Acesta va permite dezvoltatorilor să scrie interogări la nivel de componentă, mutând interogările mai aproape de componentele aferente, în loc să utilizeze interogări media din fereastra de vizualizare îndepărtată și abia înrudite. Acest lucru va avea ca rezultat componente mai robuste, reutilizabile și mai ușor de întreținut, care se vor putea adapta la diferite cazuri de utilizare, layout-uri și containere.
În prezent, interogările de containere sunt încă într-o fază incipientă, experimentală, iar implementarea este predispusă la schimbare. Dacă doriți să începeți să utilizați interogări container în proiectele dvs. astăzi, va trebui să le adăugați folosind îmbunătățirea progresivă cu detectarea caracteristicilor sau să utilizați o completare polivalentă JavaScript. Ambele cazuri vor avea ca rezultat o suprasarcină în cod, așa că dacă decideți să utilizați interogări container în această fază incipientă, asigurați-vă că planificați refactorizarea codului odată ce caracteristica devine acceptată pe scară largă.
Referințe
- „Container Queries: A Quick Start Guide” de David A. Herron
- „Salutați interogările de containere CSS”, Ahmad Shadeed
- „Containment CSS în Chrome 52”, Paul Lewis
- „Ajutând browserele să optimizeze cu proprietatea CSS Contain”, Rachel Andrew