Lekcja 34
Container queries
Container queries pozwalają stylować komponent zależnie od rozmiaru jego kontenera, a nie tylko od szerokości okna przeglądarki. Dzięki temu ten sam komponent może wyglądać inaczej w sidebarze, siatce kart i szerokim panelu dashboardu.
Cele lekcji
Po ukończeniu tej lekcji będziesz umieć:
- wyjaśnić różnicę między
@mediai@container, - ustawić element jako kontener przez
container-type, - użyć nazwanego kontenera przez
container-name, - pisać zapytania
@containeroparte o szerokość rodzica, - stosować jednostki
cqi,cqb,cqminicqmax, - projektować komponenty bardziej niezależne od konkretnego layoutu strony.
Kontekst praktyczny
Media queries odpowiadają na pytanie: „jak szeroki jest viewport?”. Container queries odpowiadają na pytanie: „ile miejsca ma ten komponent?”. To ważna różnica, bo karta produktu może mieć 280px w szerokim desktopie, jeśli stoi w sidebarze, albo 620px w tym samym viewportcie, jeśli stoi w głównej kolumnie.
Media vs container
@media nadal jest potrzebne do decyzji na poziomie całej strony: głównej siatki, globalnej nawigacji czy dużych zmian layoutu. @container lepiej pasuje do komponentów wielokrotnego użycia.
/* Decyzja strony */
@media (min-width: 64rem) {
.page-layout {
grid-template-columns: 18rem 1fr;
}
}
/* Decyzja komponentu */
@container (min-width: 32rem) {
.product-card {
grid-template-columns: 12rem 1fr;
}
}
Dobry podział: layout strony reaguje na viewport, a komponent reaguje na swój kontener.
container-type
Element musi zostać oznaczony jako kontener, zanim jego dzieci będą mogły reagować przez @container. Najczęściej użyjesz inline-size, czyli obserwacji szerokości w osi inline.
.card-shell {
container-type: inline-size;
}
.card {
display: grid;
gap: 1rem;
}
Kontenerem jest zwykle wrapper komponentu albo region layoutu. Sam element stylowany wewnątrz @container musi być potomkiem tego kontenera.
container-name
Gdy w komponencie lub stronie masz kilka kontenerów, możesz nadać im nazwy. Dzięki temu zapytanie odnosi się do konkretnego kontekstu, a nie do najbliższego kontenera.
.dashboard-panel {
container-type: inline-size;
container-name: dashboard-panel;
}
@container dashboard-panel (min-width: 42rem) {
.metric-card {
grid-template-columns: auto 1fr auto;
}
}
Nazwy są przydatne w bardziej złożonych layoutach, ale w małym komponencie często wystarczy najbliższy kontener bez nazwy.
@container
Reguła @container działa podobnie do @media, ale warunek dotyczy kontenera. Najczęstszy wzorzec to komponent, który domyślnie jest pionowy, a po przekroczeniu szerokości kontenera przechodzi w układ poziomy.
.profile-card {
display: grid;
gap: 1rem;
}
@container (min-width: 30rem) {
.profile-card {
grid-template-columns: 7rem 1fr;
align-items: center;
}
}
To pozwala użyć tej samej karty w wąskiej kolumnie i szerokim panelu bez dopisywania klas typu .profile-card--wide zależnych od strony.
Jednostki container query
Container queries mają własne jednostki. cqi oznacza 1% rozmiaru kontenera w osi inline, cqb w osi block, a cqmin i cqmax biorą mniejszy albo większy wymiar kontenera.
.card-title {
font-size: clamp(1rem, 6cqi, 1.6rem);
}
.card-media {
border-radius: clamp(.5rem, 3cqi, 1.25rem);
}
Jednostki kontenerowe są przydatne, gdy chcesz skalować typografię, promień albo odstępy względem miejsca dostępnego dla komponentu.
Fallback i wsparcie
Projektuj komponent tak, aby domyślny styl był poprawny bez zapytania kontenerowego. Potem użyj @container jako progresywnego ulepszenia. Dzięki temu komponent nie rozpada się, jeśli zapytanie nie zadziała.
.media-card {
display: grid;
gap: 1rem;
}
@container (min-width: 36rem) {
.media-card {
grid-template-columns: minmax(10rem, .4fr) 1fr;
}
}
Najbezpieczniejszy fallback to wersja mobile-first: pionowa, czytelna i bez założeń o szerokości.
Demo container queries
Ten sam komponent znajduje się w dwóch różnych kontenerach. Wąski kontener pokazuje układ pionowy, a szeroki kontener uruchamia regułę @container i zmienia kartę na układ poziomy.
Karta kursu
Ten sam komponent dostaje więcej miejsca i sam przełącza układ przez @container.
Najczęstsze błędy początkujących
- Próba użycia
@containerbez ustawieniacontainer-typena rodzicu. - Stylowanie samego kontenera w zapytaniu, zamiast jego potomków.
- Używanie container queries do decyzji globalnych, które nadal powinny być w
@media. - Nadawanie nazw wszystkim kontenerom, nawet gdy wystarcza najbliższy kontener.
- Brak sensownego stylu bazowego przed zapytaniem kontenerowym.
- Tworzenie zbyt wielu progów dla jednego komponentu zamiast prostych, czytelnych zmian.
Mini zadanie
Zbuduj kartę profilu, która reaguje na rozmiar kontenera.
- Utwórz wrapper
.profile-card-shelli ustaw na nimcontainer-type: inline-size. - Zaprojektuj bazową kartę jako pionowy układ mobile-first.
- Dodaj
@container (min-width: 32rem), który przełączy kartę na dwie kolumny. - Użyj
cqido płynnego rozmiaru tytułu lub odstępów. - Sprawdź kartę w sidebarze i w szerokiej sekcji bez dodawania klas wariantów.
- Zostaw styl bazowy czytelny nawet wtedy, gdy zapytanie kontenerowe się nie uruchomi.
Checklista
- Rozumiem, że
@containerreaguje na kontener, a nie viewport. - Potrafię wskazać element, na którym trzeba ustawić
container-type. - Wiem, kiedy użyć nazwanego kontenera.
- Potrafię zbudować komponent z bazowym układem i ulepszeniem przez
@container. - Rozumiem podstawowe jednostki
cqi,cqb,cqminicqmax. - Nie zastępuję wszystkich media queries przez container queries.
Podsumowanie lekcji
Container queries przenoszą responsywność bliżej komponentu. Dzięki nim komponent może być odporny na różne konteksty layoutu i reagować na realną przestrzeń, którą otrzymuje od rodzica.
Kolejny krok roadmapy to logical properties.
Przejdź do Lekcji 35