Interogări de containere CSS: cazuri de utilizare și strategii de migrare

Publicat: 2022-03-10
Rezumat rapid ↬ Interogările de containere CSS aduc interogările media mai aproape de elementele țintă în sine și le permit să se adapteze la aproape orice container sau aspect dat. În acest articol, vom aborda elementele de bază ale interogărilor de containere CSS și cum să le folosim astăzi cu îmbunătățiri progresive sau polyfills.

Câ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.

Exemplu de aspect de pagină receptiv. Imaginea din stânga arată aspectul desktopului la lățimea ferestrei de vizualizare de 1280 px, iar imaginea din dreapta arată aspectul mobil la lățimea ferestrei de vizualizare de 568 px.
Exemplu de aspect de pagină receptiv. Imaginea din stânga arată aspectul desktopului la lățimea ferestrei de vizualizare de 1280 px, iar imaginea din dreapta arată aspectul mobil la lățimea ferestrei de vizualizare de 568 px. (Previzualizare mare)

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.

Exemplu de componentă a cardului de produs receptiv. Imaginea din stânga arată o componentă la lățimea ferestrei de vizualizare de 720 px, iar imaginea din dreapta arată aspectul componentei la lățimea ferestrei de vizualizare de 568 px.
Exemplu de componentă a cardului de produs receptiv. Imaginea din stânga arată o componentă la lățimea ferestrei de vizualizare de 720 px, iar imaginea din dreapta arată aspectul componentei la lățimea ferestrei de vizualizare de 568 px. (Previzualizare mare)

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.

Exemplu de aspect de pagină cu aceeași componentă a interfeței de utilizare a cardului de produs în grila cu 3 coloane din secțiunea de sus și lista secțiunii de jos.
Exemplu de aspect de pagină cu aceeași componentă a interfeței de utilizare a cardului de produs în grila cu 3 coloane din secțiunea de sus și lista secțiunii de jos. (Previzualizare mare)

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ă.

Exemplu de modul în care interogarea media nu poate fi legată în mod fiabil de dimensiunile elementului. Diverse proprietăți CSS pot afecta dimensiunile elementului dintr-un container. În acest exemplu, umplutura containerului este diferită între cele două imagini.
Exemplu de modul în care interogarea media nu poate fi legată în mod fiabil de dimensiunile elementului. Diverse proprietăți CSS pot afecta dimensiunile elementului dintr-un container. În acest exemplu, umplutura containerului este diferită între cele două imagini. (Previzualizare mare)

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.

Vezi stiloul [Cartele de produs: diverse containere](https://codepen.io/smashingmag/pen/eYvWVxz) de Adrian Bece.

Vezi Fișele de produs stilou: diverse containere de Adrian Bece.

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
Interogări de containere
(Previzualizare mare)

Î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.

Mai multe după săritură! Continuați să citiți mai jos ↓

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.

În acest exemplu, nu redimensionăm fereastra de vizualizare, ci însuși elementul container <section> care are aplicată proprietatea CSS de redimensionare. Componenta comută automat între layout-uri în funcție de dimensiunile containerului.
În acest exemplu, nu redimensionăm fereastra, ci însuși elementul container <section> care are aplicată proprietatea CSS de redimensionare. Componenta comută automat între layout-uri în funcție de dimensiunile containerului. (Previzualizare mare)

Vezi stiloul [Product Cards: Container Queries](https://codepen.io/smashingmag/pen/PopmQLV) de Adrian Bece.

Vedeți Cardurile de produs stilou: Interogări de containere de Adrian Bece.

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.

Vedeți Pen [Cartele de produs: Interogări de containere cu îmbunătățire progresivă](https://codepen.io/smashingmag/pen/zYZwRXZ) de Adrian Bece.

Vedeți Carduri de produs stilou: Interogări de containere cu îmbunătățire progresivă de Adrian Bece.

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