Container Queries are a revolutionary CSS feature that enables creating responsive components based on the size of their container, rather than the browser window. This is a breakthrough in component design that allows for truly modular and flexible layouts.
Container Queries allow you to apply styles depending on the size of the parent container, not the viewport. This means that components can react to their local context, making them more predictable and reusable.
1/* Traditional Media Queries - based on viewport */
2@media (max-width: 768px) {
3 .card {
4 flex-direction: column;
5 }
6}
7
8/* Container Queries - based on container size */
9@container (max-width: 400px) {
10 .card {
11 flex-direction: column;
12 }
13}To use Container Queries, we first need to define an element as a container:
1.container {
2 container-type: inline-size; /* Monitors width */
3}
4
5.container {
6 container-type: size; /* Monitors width and height */
7}
8
9.container {
10 container-type: normal; /* Default value - no monitoring */
11}
12
13/* Shorthand with container name */
14.sidebar {
15 container: sidebar / inline-size;
16}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/* When container is narrow - vertical layout */
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/* When container is wider - horizontal layout */
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}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/* Compact navigation for small containers */
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 /* Hide text and show only icons */
37 .nav-text {
38 display: none;
39 }
40
41 .nav-icon {
42 display: block;
43 font-size: 1.5rem;
44 }
45}
46
47/* Full navigation for larger containers */
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}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/* Small widget - basic information */
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/* Medium widget - more details */
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/* Large widget - full details */
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}1.component {
2 container-type: inline-size;
3}
4
5/* Combining conditions */
6@container (min-width: 300px) and (max-width: 600px) {
7 .component {
8 background: lightblue;
9 }
10}
11
12/* Alternative */
13@container (max-width: 300px), (min-width: 800px) {
14 .component {
15 border: 2px solid red;
16 }
17}
18
19/* Negation */
20@container not (min-width: 400px) {
21 .component {
22 font-size: 0.9rem;
23 }
24}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/* Different styles depending on context */
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}1.gallery {
2 container-type: inline-size;
3}
4
5/* Responsive gallery based on container size */
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/* Adaptive images */
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}1/* Good use of Container Queries */
2.component {
3 container-type: inline-size;
4}
5
6/* Components that can be used in different contexts */
7@container (max-width: 300px) {
8 .card {
9 /* Styles for narrow container */
10 flex-direction: column;
11 }
12}
13
14/* Media Queries for global changes */
15@media (max-width: 768px) {
16 body {
17 font-size: 14px;
18 }
19
20 .global-header {
21 padding: 1rem;
22 }
23}
24
25/* Don't use Container Queries for global changes */
26@container (max-width: 768px) {
27 body { /* This won't work as expected */
28 font-size: 14px;
29 }
30}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="Profile" class="avatar">
7 <div class="info">
8 <h3 class="name">John Smith</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 <!-- Same card, but in a narrower container -->
24 <img src="avatar.jpg" alt="Profile" class="avatar">
25 <div class="info">
26 <h3 class="name">John Smith</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>1/* CSS with 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/* Narrow container - vertical layout */
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; /* Hide in narrow container */
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/* Medium container - mixed layout */
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/* Wide container - full layout */
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}1/* Use only the needed container type */
2.width-only {
3 container-type: inline-size; /* Better for performance */
4}
5
6.both-dimensions {
7 container-type: size; /* Only when you need height */
8}
9
10/* Group similar breakpoints */
11@container (max-width: 400px) {
12 .component-a { /* styles */ }
13 .component-b { /* styles */ }
14 .component-c { /* styles */ }
15}
16
17/* Avoid excessive nesting */
18@container (min-width: 300px) {
19 @container (max-width: 600px) { /* Hard to manage */
20 .component { /* styles */ }
21 }
22}1/* Fallback without 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 as 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}Container Queries introduce special units that refer to the container size:
| Unit | Description | |------|-------------| |
cqw | 1% of container width (container query width) |
| cqh | 1% of container height (container query height) |
| cqi | 1% of container inline size (container query inline) |
| cqb | 1% of container block size (container query block) |1@container (min-width: 400px) {
2 .card-title {
3 font-size: 5cqw; /* 5% of container width */
4 }
5}These units allow for smooth scaling of elements depending on the container size, similar to how
vw/vh work relative to the viewport.Container Queries represent the future of responsive web design, enabling the creation of truly modular components that adapt to their context regardless of viewport size. This tool fundamentally changes the way we think about responsiveness in CSS.