Kurs HTML i CSS · Moduł 9: Nowoczesny CSS

Container Queries - responsywność oparta na kontenerze

13 min czytania
W tej lekcji6

Twoja karta artefaktu świetnie wygląda w szerokiej kolumnie galerii, ale w wąskim panelu bocznym się rozjeżdża, a media query nic nie zauważa, bo okno przeglądarki się nie zmieniło. Karta nie wie, w jakiej komnacie stoi. Container Queries to funkcjonalność CSS, która umożliwia tworzenie komponentów responsywnych w oparciu o rozmiar ich kontenera, a nie okna przeglądarki. To przełom, który pozwala na prawdziwie modularne układy.

Czego się nauczysz

  • zamienisz element w kontener i napiszesz zapytanie @container,
  • nazwiesz kontenery i połączysz warunki operatorami,
  • odróżnisz zadania dla Container Queries i media queries,
  • użyjesz jednostek kontenera, takich jak cqw.

Podstawy Container Queries

Czym są Container Queries?

Container Queries pozwalają stosować style w zależności od rozmiaru kontenera rodzica, a nie viewport, więc komponenty reagują na swój lokalny kontekst. Porównaj oba zapytania:

1/* Tradycyjne Media Queries - bazują na viewport */
2@media (max-width: 768px) {
3  .card {
4    flex-direction: column;
5  }
6}
7
8/* Container Queries - bazują na rozmiarze kontenera */
9@container (max-width: 400px) {
10  .card {
11    flex-direction: column;
12  }
13}

Obie reguły ustawiają kartę w kolumnie. Składnia warunków zostaje ta sama, zmienia się tylko punkt odniesienia: okno albo kontener.

Ustanawianie kontekstu kontenera

Aby używać Container Queries, najpierw musimy zdefiniować element jako kontener właściwością container-type:

1.container {
2  container-type: inline-size; /* Monitoruje szerokość */
3}
4
5.container {
6  container-type: size; /* Monitoruje szerokość i wysokość */
7}
8
9.container {
10  container-type: normal; /* Wartość domyślna - brak monitorowania */
11}
12
13/* Skrócona forma z nazwą kontenera */
14.sidebar {
15  container: sidebar / inline-size;
16}

inline-size śledzi szerokość i wystarcza niemal zawsze. size śledzi też wysokość, ale element przestaje wtedy brać wysokość z treści. Najważniejsza zasada: element nie może odpytywać samego siebie, więc reguły w @container stylują potomków kontenera. Kolejność pracy: container-type na rodzicu, opcjonalna nazwa w container-name, zapytanie @container, style dla dzieci i test w różnych szerokościach.

Praktyczne zastosowania Container Queries

1. Responsywna karta produktu

Tutaj kontenerem jest cała siatka produktów, a karty to jej potomkowie, które zmieniają układ razem z nią:

1.product-grid {
2  display: grid;
3  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
4  gap: 1rem;
5  container-type: inline-size;
6}
7
8.product-card {
9  background: white;
10  border-radius: 8px;
11  padding: 1rem;
12  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
13}
14
15/* Gdy kontener jest wąski - układ pionowy */
16@container (max-width: 300px) {
17  .product-card {
18    text-align: center;
19  }
20
21  .product-card .image {
22    width: 100%;
23    margin-bottom: 1rem;
24  }
25
26  .product-card .title {
27    font-size: 1.1rem;
28    margin-bottom: 0.5rem;
29  }
30
31  .product-card .price {
32    font-size: 1.2rem;
33    color: #e74c3c;
34  }
35}
36
37/* Gdy kontener jest szerszy - układ poziomy */
38@container (min-width: 400px) {
39  .product-card {
40    display: flex;
41    align-items: center;
42    gap: 1rem;
43  }
44
45  .product-card .image {
46    width: 120px;
47    height: 120px;
48    flex-shrink: 0;
49  }
50
51  .product-card .content {
52    flex: 1;
53  }
54
55  .product-card .title {
56    font-size: 1.3rem;
57    margin-bottom: 0.5rem;
58  }
59
60  .product-card .description {
61    color: #666;
62    margin-bottom: 1rem;
63  }
64
65  .product-card .price {
66    font-size: 1.4rem;
67    font-weight: bold;
68    color: #27ae60;
69  }
70}

Pytamy o szerokość całej siatki, a nie pojedynczej karty. Jeśli każda karta ma reagować osobno, kontenerem zrób jej opakowanie.

2. Adaptacyjna nawigacja

Kontener możesz nazwać, a wtedy zapytanie @container nav sprawdza tylko ten kontener, nawet jeśli bliżej stoi inny:

1.navigation {
2  container-name: nav;
3  container-type: inline-size;
4}
5
6.nav-list {
7  display: flex;
8  list-style: none;
9  margin: 0;
10  padding: 0;
11  gap: 1rem;
12}
13
14/* Kompaktowa nawigacja dla małych kontenerów */
15@container nav (max-width: 600px) {
16  .nav-list {
17    flex-direction: column;
18    gap: 0;
19  }
20
21  .nav-item {
22    border-bottom: 1px solid #eee;
23  }
24
25  .nav-link {
26    display: block;
27    padding: 1rem;
28    text-decoration: none;
29    transition: background-color 0.2s;
30  }
31
32  .nav-link:hover {
33    background-color: #f8f9fa;
34  }
35
36  /* Ukryj teksty i pokaż tylko ikony */
37  .nav-text {
38    display: none;
39  }
40
41  .nav-icon {
42    display: block;
43    font-size: 1.5rem;
44  }
45}
46
47/* Pełna nawigacja dla większych kontenerów */
48@container nav (min-width: 601px) {
49  .nav-list {
50    justify-content: space-between;
51    align-items: center;
52  }
53
54  .nav-link {
55    display: flex;
56    align-items: center;
57    gap: 0.5rem;
58    padding: 0.5rem 1rem;
59    text-decoration: none;
60    border-radius: 4px;
61    transition: all 0.2s;
62  }
63
64  .nav-link:hover {
65    background-color: #007bff;
66    color: white;
67  }
68
69  .nav-text {
70    display: inline;
71  }
72
73  .nav-icon {
74    font-size: 1.2rem;
75  }
76}

Do 600px nawigacja staje się pionową listą ikon, a od 601px pokazuje pełne linki z tekstem.

3. Dashboard z elastycznymi widgetami

W panelu każdy widget sam jest kontenerem, więc w wąskiej i szerokiej kolumnie pokaże inną ilość szczegółów:

1.dashboard {
2  display: grid;
3  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
4  gap: 1rem;
5  padding: 1rem;
6}
7
8.widget {
9  background: white;
10  border-radius: 8px;
11  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
12  container-type: inline-size;
13  overflow: hidden;
14}
15
16.widget-header {
17  padding: 1rem;
18  background: #f8f9fa;
19  border-bottom: 1px solid #dee2e6;
20}
21
22.widget-content {
23  padding: 1rem;
24}
25
26/* Mały widget - podstawowe informacje */
27@container (max-width: 350px) {
28  .widget-header h3 {
29    font-size: 1rem;
30    margin: 0;
31  }
32
33  .widget-stats {
34    display: block;
35  }
36
37  .stat-item {
38    margin-bottom: 0.5rem;
39    text-align: center;
40  }
41
42  .stat-value {
43    font-size: 1.5rem;
44    font-weight: bold;
45    display: block;
46  }
47
48  .stat-label {
49    font-size: 0.8rem;
50    color: #666;
51  }
52
53  .chart {
54    height: 150px;
55  }
56}
57
58/* Średni widget - więcej szczegółów */
59@container (min-width: 351px) and (max-width: 500px) {
60  .widget-header h3 {
61    font-size: 1.2rem;
62    margin: 0;
63  }
64
65  .widget-stats {
66    display: flex;
67    justify-content: space-around;
68    margin-bottom: 1rem;
69  }
70
71  .stat-item {
72    text-align: center;
73  }
74
75  .stat-value {
76    font-size: 1.8rem;
77    font-weight: bold;
78    display: block;
79    color: #007bff;
80  }
81
82  .stat-label {
83    font-size: 0.9rem;
84    color: #666;
85  }
86
87  .chart {
88    height: 200px;
89  }
90}
91
92/* Duży widget - pełne detale */
93@container (min-width: 501px) {
94  .widget-header {
95    display: flex;
96    justify-content: space-between;
97    align-items: center;
98  }
99
100  .widget-header h3 {
101    font-size: 1.4rem;
102    margin: 0;
103  }
104
105  .widget-actions {
106    display: flex;
107    gap: 0.5rem;
108  }
109
110  .widget-stats {
111    display: grid;
112    grid-template-columns: repeat(3, 1fr);
113    gap: 1rem;
114    margin-bottom: 1.5rem;
115  }
116
117  .stat-item {
118    text-align: center;
119    padding: 1rem;
120    background: #f8f9fa;
121    border-radius: 4px;
122  }
123
124  .stat-value {
125    font-size: 2rem;
126    font-weight: bold;
127    display: block;
128    color: #28a745;
129  }
130
131  .stat-label {
132    font-size: 1rem;
133    color: #666;
134    margin-top: 0.5rem;
135  }
136
137  .chart {
138    height: 250px;
139  }
140
141  .widget-details {
142    display: block;
143    margin-top: 1rem;
144    padding-top: 1rem;
145    border-top: 1px solid #dee2e6;
146  }
147}

Trzy przedziały nie nachodzą na siebie, dlatego zawsze działa dokładnie jeden zestaw stylów.

Zaawansowane techniki Container Queries

1. Logiczne operatory

Warunki łączysz słowami and, or i not, tak jak w media queries. Kontenerem jest tu rodzic komponentu:

1.component-wrapper {
2  container-type: inline-size; /* kontenerem jest rodzic */
3}
4
5/* Połączenie warunków */
6@container (min-width: 300px) and (max-width: 600px) {
7  .component {
8    background: lightblue;
9  }
10}
11
12/* Alternatywa */
13@container (max-width: 300px) or (min-width: 800px) {
14  .component {
15    border: 2px solid red;
16  }
17}
18
19/* Negacja */
20@container not (min-width: 400px) {
21  .component {
22    font-size: 0.9rem;
23  }
24}

Opakowanie jest kontenerem, bo .component nie mógłby odpytywać samego siebie. Trzecia reguła działa zawsze wtedy, gdy kontener jest węższy niż 400px.

2. Zagnieżdżone kontenery

Na stronie może działać wiele kontenerów naraz, jeden w drugim, a nazwy wskazują, o który z nich pytasz:

1.main-container {
2  container-name: main;
3  container-type: inline-size;
4}
5
6.sidebar {
7  container-name: sidebar;
8  container-type: inline-size;
9}
10
11.article {
12  container-name: article;
13  container-type: inline-size;
14}
15
16/* Różne style w zależności od kontekstu */
17@container main (min-width: 1200px) {
18  .layout {
19    display: grid;
20    grid-template-columns: 300px 1fr;
21    gap: 2rem;
22  }
23}
24
25@container sidebar (max-width: 250px) {
26  .sidebar-item {
27    padding: 0.5rem;
28  }
29
30  .sidebar-text {
31    display: none;
32  }
33}
34
35@container article (min-width: 600px) {
36  .article-content {
37    columns: 2;
38    column-gap: 2rem;
39  }
40}

Zapytanie z nazwą szuka najbliższego przodka o tej nazwie, a zapytanie bez nazwy bierze najbliższy kontener dowolnego rodzaju.

3. Container Queries z CSS Grid

Container Queries świetnie łączą się z Gridem. Galeria ma od jednej do czterech kolumn, zależnie od szerokości swojego opakowania:

1.gallery-wrapper {
2  container-type: inline-size; /* kontenerem jest opakowanie galerii */
3}
4
5/* Responsywna galeria bazująca na rozmiarze kontenera */
6@container (max-width: 400px) {
7  .gallery {
8    display: grid;
9    grid-template-columns: 1fr;
10    gap: 1rem;
11  }
12}
13
14@container (min-width: 401px) and (max-width: 800px) {
15  .gallery {
16    display: grid;
17    grid-template-columns: repeat(2, 1fr);
18    gap: 1rem;
19  }
20}
21
22@container (min-width: 801px) and (max-width: 1200px) {
23  .gallery {
24    display: grid;
25    grid-template-columns: repeat(3, 1fr);
26    gap: 1.5rem;
27  }
28}
29
30@container (min-width: 1201px) {
31  .gallery {
32    display: grid;
33    grid-template-columns: repeat(4, 1fr);
34    gap: 2rem;
35  }
36}
37
38/* Adaptacyjne zdjęcia */
39.gallery-item {
40  position: relative;
41  overflow: hidden;
42  border-radius: 8px;
43}
44
45@container (max-width: 400px) {
46  .gallery-item {
47    aspect-ratio: 16/9;
48  }
49
50  .gallery-item img {
51    width: 100%;
52    height: 100%;
53    object-fit: cover;
54  }
55
56  .gallery-caption {
57    position: absolute;
58    bottom: 0;
59    left: 0;
60    right: 0;
61    background: linear-gradient(transparent, rgba(0,0,0,0.8));
62    color: white;
63    padding: 1rem;
64    font-size: 0.9rem;
65  }
66}
67
68@container (min-width: 801px) {
69  .gallery-item {
70    aspect-ratio: 1;
71    transition: transform 0.3s ease;
72  }
73
74  .gallery-item:hover {
75    transform: scale(1.05);
76  }
77
78  .gallery-caption {
79    position: absolute;
80    bottom: 0;
81    left: 0;
82    right: 0;
83    background: rgba(0,0,0,0.9);
84    color: white;
85    padding: 1rem;
86    transform: translateY(100%);
87    transition: transform 0.3s ease;
88  }
89
90  .gallery-item:hover .gallery-caption {
91    transform: translateY(0);
92  }
93}

Kontenerem jest .gallery-wrapper, bo galeria nie może pytać o własną szerokość. W szerokim kontenerze zdjęcia stają się kwadratami z podpisem wysuwanym po najechaniu kursorem.

Container Queries vs Media Queries

Kiedy używać Container Queries?

Obie techniki się uzupełniają: Container Queries służą komponentom, a media queries zmianom całej strony:

1/* Dobre zastosowanie Container Queries */
2.component {
3  container-type: inline-size;
4}
5
6/* Komponenty, które mogą być używane w różnych kontekstach */
7@container (max-width: 300px) {
8  .card {
9    /* Style dla wąskiego kontenera */
10    flex-direction: column;
11  }
12}
13
14/* Media Queries dla globalnych zmian */
15@media (max-width: 768px) {
16  body {
17    font-size: 14px;
18  }
19
20  .global-header {
21    padding: 1rem;
22  }
23}
24
25/* Nie używaj Container Queries dla globalnych zmian */
26@container (max-width: 768px) {
27  body { /* To nie zadziała jak oczekujesz */
28    font-size: 14px;
29  }
30}

Ostatnia reguła nie zadziała, bo body nie ma nad sobą żadnego kontenera. Globalne zmiany zostawiaj media queries.

Praktyczny przykład - modularna karta

Ten sam HTML karty profilu stoi raz w szerokiej kolumnie głównej, a raz w wąskim panelu bocznym:

1<!-- HTML -->
2<div class="page-layout">
3  <main class="main-content">
4    <div class="card-container">
5      <div class="profile-card">
6        <img src="avatar.jpg" alt="Profil" class="avatar">
7        <div class="info">
8          <h3 class="name">Jan Kowalski</h3>
9          <p class="role">Frontend Developer</p>
10          <p class="description">Passionate about creating amazing web experiences</p>
11          <div class="actions">
12            <button class="btn primary">Follow</button>
13            <button class="btn secondary">Message</button>
14          </div>
15        </div>
16      </div>
17    </div>
18  </main>
19
20  <aside class="sidebar">
21    <div class="card-container">
22      <div class="profile-card">
23        <!-- Ta sama karta, ale w węższym kontenerze -->
24        <img src="avatar.jpg" alt="Profil" class="avatar">
25        <div class="info">
26          <h3 class="name">Jan Kowalski</h3>
27          <p class="role">Frontend Developer</p>
28          <p class="description">Passionate about creating amazing web experiences</p>
29          <div class="actions">
30            <button class="btn primary">Follow</button>
31            <button class="btn secondary">Message</button>
32          </div>
33        </div>
34      </div>
35    </div>
36  </aside>
37</div>

Obie kopie mają identyczny kod. Style poniżej sprawią, że każda karta dopasuje się do swojego kontenera .card-container:

1/* CSS z Container Queries */
2.card-container {
3  container-type: inline-size;
4}
5
6.profile-card {
7  background: white;
8  border-radius: 12px;
9  padding: 1.5rem;
10  box-shadow: 0 4px 6px rgba(0,0,0,0.1);
11  transition: all 0.3s ease;
12}
13
14/* Wąski kontener - układ pionowy */
15@container (max-width: 350px) {
16  .profile-card {
17    text-align: center;
18  }
19
20  .avatar {
21    width: 80px;
22    height: 80px;
23    border-radius: 50%;
24    margin: 0 auto 1rem;
25    display: block;
26  }
27
28  .name {
29    font-size: 1.2rem;
30    margin-bottom: 0.5rem;
31  }
32
33  .role {
34    color: #666;
35    font-size: 0.9rem;
36    margin-bottom: 1rem;
37  }
38
39  .description {
40    display: none; /* Ukryj w wąskim kontenerze */
41  }
42
43  .actions {
44    display: flex;
45    flex-direction: column;
46    gap: 0.5rem;
47  }
48
49  .btn {
50    padding: 0.75rem;
51    border: none;
52    border-radius: 6px;
53    font-size: 0.9rem;
54    cursor: pointer;
55    transition: all 0.2s;
56  }
57
58  .btn.primary {
59    background: #007bff;
60    color: white;
61  }
62
63  .btn.secondary {
64    background: #f8f9fa;
65    color: #666;
66  }
67}
68
69/* Średni kontener - układ mieszany */
70@container (min-width: 351px) and (max-width: 500px) {
71  .profile-card {
72    display: flex;
73    align-items: center;
74    gap: 1rem;
75    text-align: left;
76  }
77
78  .avatar {
79    width: 100px;
80    height: 100px;
81    border-radius: 50%;
82    flex-shrink: 0;
83  }
84
85  .info {
86    flex: 1;
87  }
88
89  .name {
90    font-size: 1.3rem;
91    margin-bottom: 0.25rem;
92  }
93
94  .role {
95    color: #666;
96    font-size: 1rem;
97    margin-bottom: 0.5rem;
98  }
99
100  .description {
101    font-size: 0.9rem;
102    color: #888;
103    margin-bottom: 1rem;
104    display: -webkit-box;
105    -webkit-line-clamp: 2;
106    -webkit-box-orient: vertical;
107    overflow: hidden;
108  }
109
110  .actions {
111    display: flex;
112    gap: 0.5rem;
113  }
114
115  .btn {
116    padding: 0.5rem 1rem;
117    border: none;
118    border-radius: 6px;
119    font-size: 0.9rem;
120    cursor: pointer;
121    transition: all 0.2s;
122  }
123
124  .btn.primary {
125    background: #007bff;
126    color: white;
127  }
128
129  .btn.secondary {
130    background: #f8f9fa;
131    color: #666;
132    border: 1px solid #dee2e6;
133  }
134}
135
136/* Szeroki kontener - pełny układ */
137@container (min-width: 501px) {
138  .profile-card {
139    display: flex;
140    align-items: flex-start;
141    gap: 1.5rem;
142  }
143
144  .avatar {
145    width: 120px;
146    height: 120px;
147    border-radius: 50%;
148    flex-shrink: 0;
149  }
150
151  .info {
152    flex: 1;
153  }
154
155  .name {
156    font-size: 1.5rem;
157    margin-bottom: 0.5rem;
158    font-weight: 600;
159  }
160
161  .role {
162    color: #007bff;
163    font-size: 1.1rem;
164    margin-bottom: 1rem;
165    font-weight: 500;
166  }
167
168  .description {
169    font-size: 1rem;
170    color: #555;
171    line-height: 1.6;
172    margin-bottom: 1.5rem;
173  }
174
175  .actions {
176    display: flex;
177    gap: 1rem;
178  }
179
180  .btn {
181    padding: 0.75rem 1.5rem;
182    border: none;
183    border-radius: 8px;
184    font-size: 1rem;
185    font-weight: 500;
186    cursor: pointer;
187    transition: all 0.2s;
188  }
189
190  .btn.primary {
191    background: #007bff;
192    color: white;
193  }
194
195  .btn.primary:hover {
196    background: #0056b3;
197  }
198
199  .btn.secondary {
200    background: white;
201    color: #666;
202    border: 2px solid #dee2e6;
203  }
204
205  .btn.secondary:hover {
206    border-color: #007bff;
207    color: #007bff;
208  }
209}

W panelu karta staje pionowo i ukrywa opis, a w kolumnie głównej pokazuje wszystko, bez ani jednego media query.

Najlepsze praktyki Container Queries

1. Wydajność i optymalizacja

Wybieraj najwęższy potrzebny typ kontenera, grupuj reguły o tych samych progach i unikaj zagnieżdżania zapytań:

1/* Użyj tylko potrzebnego typu kontenera */
2.width-only {
3  container-type: inline-size; /* Lepsze dla wydajności */
4}
5
6.both-dimensions {
7  container-type: size; /* Tylko gdy potrzebujesz wysokości */
8}
9
10/* Grupuj podobne breakpointy */
11@container (max-width: 400px) {
12  .component-a { /* style */ }
13  .component-b { /* style */ }
14  .component-c { /* style */ }
15}
16
17/* Unikaj nadmiernego zagnieżdżania */
18@container (min-width: 300px) {
19  @container (max-width: 600px) { /* Trudne do zarządzania */
20    .component { /* style */ }
21  }
22}

Zagnieżdżony @container jest poprawny, ale trudny w czytaniu, więc łącz warunki operatorem and.

2. Fallback dla starszych przeglądarek

Container Queries działają we wszystkich głównych przeglądarkach od 2023 roku, a @supports pozwala dodać je jako ulepszenie:

1/* Fallback bez Container Queries */
2.responsive-component {
3  padding: 1rem;
4  background: #f8f9fa;
5}
6
7.responsive-component .title {
8  font-size: 1.2rem;
9  margin-bottom: 1rem;
10}
11
12.responsive-component .content {
13  display: block;
14}
15
16/* Container Queries jako enhancement */
17@supports (container-type: inline-size) {
18  .responsive-container {
19    container-type: inline-size;
20  }
21
22  @container (min-width: 400px) {
23    .responsive-component {
24      padding: 2rem;
25    }
26
27    .responsive-component .title {
28      font-size: 1.5rem;
29    }
30
31    .responsive-component .content {
32      display: flex;
33      gap: 1rem;
34    }
35  }
36}

Każda przeglądarka dostaje style bazowe, a nowsze dodatkowo układ zależny od kontenera. Nic się nie psuje.

Jednostki Container Query

Container Queries wprowadzają specjalne jednostki, które odnoszą się do rozmiaru kontenera:

JednostkaOpis
cqw1% szerokości kontenera (container query width)
cqh1% wysokości kontenera (container query height)
cqi1% rozmiaru inline kontenera (container query inline)
cqb1% rozmiaru block kontenera (container query block)

Użyjmy jednostki cqw, żeby tytuł karty rósł razem z jej kontenerem:

1@container (min-width: 400px) {
2  .card-title {
3    font-size: 5cqw; /* 5% szerokości kontenera */
4  }
5}

Jednostki te działają jak vw i vh, tylko względem kontenera. Gdy element nie ma nad sobą kontenera, liczą się względem okna.

Moja rada: media queries zostaw dla układu strony, a komponenty buduj z Container Queries. W dalszej części modułu poznasz selektor :has(), który stylizuje rodzica na podstawie dzieci i razem z Container Queries tworzy duet nowoczesnej responsywności. W piaskownicy poniżej dodaj container-type: inline-size do .card-container i obserwuj kartę.

Pamiętaj: media query pyta o rozmiar całego pałacu, a container query o rozmiar komnaty, w której stoi Twój komponent.

Kod do tej lekcji: index.html
1<!DOCTYPE html>
2<html lang="pl">
3<head>
4    <meta charset="UTF-8">
5    <title>Container Queries</title>
6    <link rel="stylesheet" href="style.css">
7</head>
8<body>
9    <h1>Container Queries</h1>
10    <div class="wide-wrapper">
11        <div class="card-container">
12            <div class="card">
13                <div class="card-img">IMG</div>
14                <div class="card-body">
15                    <h2>Skarabeusz</h2>
16                    <p>Symbol odrodzenia w starozytnym Egipcie.</p>
17                </div>
18            </div>
19        </div>
20    </div>
21    <div class="narrow-wrapper">
22        <div class="card-container">
23            <div class="card">
24                <div class="card-img">IMG</div>
25                <div class="card-body">
26                    <h2>Ankh</h2>
27                    <p>Klucz zycia - symbol wiecznosci.</p>
28                </div>
29            </div>
30        </div>
31    </div>
32</body>
33</html>

Widzisz błąd w tej lekcji?

Sprawdź się

Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.

  1. 1. Co to są Container Queries w CSS?

  2. 2. Jak zdefiniować container dla Container Queries?

Zadania praktyczne w grze

  • Edytor kodu

    Stwórz .card-container { container-type: inline-size }. Wewnątrz .card która przy @container (min-width: 300px) zmienia flex-direction z column na row. Umieść w różnych szerokościach - karta się dostosuje!

Przydatne artykuły