CSS container queries: mẫu dùng và những cái bẫy
Container query đã dùng được vài năm nay, nhưng mức độ áp dụng vẫn thua mức độ biết đến. Số liệu khảo sát State of CSS được trích trong các bài viết gần đây cho thấy khoảng 86 phần trăm lập trình viên biết đến nó nhưng chỉ khoảng 41 phần trăm thực sự dùng. Một phần khoảng cách này đến từ thói quen: người ta dùng @container như thể nó là một @media khác, vấp phải rào cản rồi quay lại cách cũ. Bài viết này đi qua mô hình tư duy, một mẫu tối thiểu chạy được và những cái bẫy gây ra phần lớn sự bực bội.
Hai câu hỏi khác nhau
Media query hỏi về viewport: cửa sổ rộng bao nhiêu, người dùng có thích chế độ tối không, đây có phải thiết bị cảm ứng không. Container query hỏi về không gian mà phần tử cha đã dành cho một component. Vì vậy media query là công cụ phù hợp cho cấu trúc cấp trang, như lưới chính hay thanh điều hướng trên cùng, còn container query là công cụ phù hợp cho các component xuất hiện ở nhiều chỗ khác nhau, ví dụ một product card nằm trong lưới rộng ở trang này và trong sidebar hẹp ở trang khác.
Các bình luận gần đây gợi ý nên coi chúng là bổ sung cho nhau chứ không phải thay thế nhau. Một bài viết năm 2026 trên Smashing Magazine mô tả đó là macro layout so với micro layout, và dẫn mức hỗ trợ trình duyệt khoảng 94 phần trăm cho size query, nên tính năng này an toàn để dùng trong production ở trường hợp cơ bản.
Một mẫu tối thiểu
Phần tử cha phải chủ động đồng ý để được đo. Nếu không, trình duyệt sẽ không theo dõi kích thước của nó, và không phần tử nào bên trong có thể truy vấn nó.
.card-slot {
container: card / inline-size;
}
.card {
display: grid;
gap: 0.75rem;
}
@container card (min-width: 28rem) {
.card {
grid-template-columns: 9rem 1fr;
align-items: start;
}
}
Dạng viết tắt này đặt tên container là card và cho nó đo inline size. Việc đặt tên là tùy chọn, nhưng nó giữ cho các quy tắc của bạn ổn định khi sau này có người bọc component trong một container khác. Card chuyển sang layout hai cột bất cứ khi nào slot của nó đủ rộng, bất kể cửa sổ rộng bao nhiêu.
Đặt kích thước bằng container unit
Các container query unit cho phép typography và khoảng cách co giãn theo slot thay vì theo màn hình. Các biến thể logic cqi và cqb theo hướng viết và nhìn chung nên dùng hơn cqw và cqh.
.card h3 {
font-size: clamp(1rem, 0.6rem + 2.5cqi, 1.75rem);
}
Nếu không có phần tử tổ tiên nào là query container, các unit này sẽ quay về giá trị dựa trên viewport, nên khai báo không bị hỏng; nó chỉ hoạt động như viewport unit cho đến khi có container.
Những cái bẫy hay gặp
Một component không thể tự truy vấn chính nó
Phần tử là container không thể đồng thời được đổi style bởi chính query của nó, vì kích thước của nó sẽ phụ thuộc vào kết quả của chính nó. Trên thực tế bạn cần một wrapper: wrapper là container, component bên trong phản hồi theo nó. Đây là lý do phổ biến nhất khiến lần thử đầu tiên có vẻ không làm gì cả.
Cẩn thận với size
Đặt container-type: size yêu cầu trình duyệt bỏ qua các phần tử con khi tính block size. Trừ khi phần tử có chiều cao khai báo, chiều cao tối thiểu hoặc tỷ lệ khung hình, nó có thể co lại thành không có gì. Hãy ưu tiên inline-size trừ khi bạn thật sự cần truy vấn chiều cao.
Custom property không dùng được trong điều kiện
Viết kiểu (min-width: var(--bp-md)) bên trong một quy tắc @container sẽ bị bỏ qua, vì custom property được cascade xuống cây DOM và nếu không thì có thể quay ngược lại ảnh hưởng chính query đang đọc chúng. Hãy dùng giá trị cụ thể, hoặc sinh chúng bằng preprocessor.
Grid item và flex item cần thêm một lớp
Biến chính grid item thành container là nguồn gây ra kết quả kỳ lạ khá phổ biến. Hãy thêm một wrapper bên trong và chỉ định wrapper đó làm container. Flex item không có kích thước xác định cũng có thể co lại, nên hãy cho chúng một basis hoặc kích thước nội tại.
Đừng bọc phần tử gốc
Truy vấn document root gây ra hành vi khó chịu. Hãy gắn container vào các vùng như main, một sidebar, hoặc slot chứa component.
Những gì chưa sẵn sàng
Size query và container unit được hỗ trợ tốt. Style query thì khác: việc truy vấn custom property như một cờ (flag) vẫn đang chờ Firefox hỗ trợ tính đến bài viết trên LogRocket được cập nhật vào tháng 3 năm 2026, còn dạng style query rộng hơn thì hoàn toàn chưa được hỗ trợ. Scroll-state query, cho phép đổi style của một phần tử sticky khi nó đã dính vào vị trí, chỉ hoạt động trên các trình duyệt nền Chromium. Nếu dùng chúng, hãy bọc trong feature query và coi hiệu ứng đó là progressive enhancement:
@supports (container-type: scroll-state) {
.header-wrap {
position: sticky;
top: 0;
container-type: scroll-state;
}
@container scroll-state(stuck: top) {
.site-header {
box-shadow: 0 2px 8px rgb(0 0 0 / 0.15);
}
}
}
Khả năng tiếp cận và debug
Những thay đổi layout do container điều khiển không bao giờ được sắp xếp lại nội dung theo cách phá vỡ thứ tự tab hợp lý hoặc thứ tự đọc. DevTools của cả Chrome lẫn Firefox đều đánh dấu các phần tử container trong bảng Elements, và rê chuột lên badge sẽ làm nổi bật container trên trang, đây là cách nhanh nhất để thấy phần tử tổ tiên nào đang thực sự được đo.
Với công việc nhiều component như section của theme và các block tái sử dụng, container query loại bỏ rất nhiều phỏng đoán về viewport. Nếu bạn đang lên kế hoạch thiết kế lại xoay quanh các component tái sử dụng, dịch vụ phát triển web của chúng tôi bao gồm kiểu tái cấu trúc front-end này, và các theme Shopify của chúng tôi cho thấy cách tiếp cận ưu tiên component trong thực tế.