HTML & CSS Mới 2026: Container Queries, @layer và Layout Hiện Đại Không Cần JavaScript

2 tháng trước · 7 phút đọc
Tại sao CSS 2026 là bước ngoặt với frontend dev
Mình nhớ hồi mới làm dự án đầu tiên, cứ nghĩ responsive design = viết media query + một đống JavaScript để toggle class. Card co lại? JS. Sidebar ẩn hiện? JS. Typography thay đổi theo viewport? JS nốt. Sau 2 năm làm vậy, codebase trở nên lộn xộn kinh khủng.
Thật ra vấn đề không phải ở JavaScript. Vấn đề là mình dùng JS để làm những việc CSS đã có thể làm - chỉ là trình duyệt chưa hỗ trợ đủ. Đến 2025-2026, bức tranh thay đổi hoàn toàn.
Browser support 2026 đã đủ mạnh để dùng các tính năng CSS mới trong production
Container Queries đạt 93-95% global browser support for @container size queries in 2024-2025 (Can I Use / caniuse.com) browser support toàn cầu. @layer được hỗ trợ trên tất cả evergreen browsers. :has() - cái selector từng bị gọi là "impossible" - giờ chạy được ở khắp nơi. Đây là thời điểm đúng để refactor cách bạn viết CSS.
Bài này mình sẽ đi qua từng tính năng với code thực tế, không lý thuyết suông.
Container Queries: responsive theo component, không theo viewport
Media queries có một điểm yếu cố hữu: chúng chỉ nhìn vào viewport. Card của bạn nằm trong sidebar hẹp hay main content rộng - media query không biết, không quan tâm.
Container Queries giải quyết đúng vấn đề này. Thay vì hỏi "viewport rộng bao nhiêu?", bạn hỏi "container của element này rộng bao nhiêu?"
Cùng một component, layout tự thích nghi theo container - không cần JavaScript
Đây là use case cực kỳ thực tế. Giả sử bạn có component card dùng ở cả sidebar (250px) lẫn main grid (600px). Trước đây bạn phải tạo 2 class riêng hoặc viết JS để detect. Giờ chỉ cần container-type: inline-size là xong.
Container Queries với Units
Cái hay hơn nữa là CSS còn có container query units:
cqi, cqb, cqw, cqh - những unit này giúp typography và spacing tự adjust theo container của chúng, không phải screen. Một khi quen dùng, bạn sẽ không muốn quay lại vw nữa.
@layer: kiểm soát cascade không còn là ác mộng
Bao nhiêu lần bạn thêm !important chỉ vì không hiểu tại sao style bị override? Hoặc import thư viện vào rồi mất cả buổi chiều debug specificity?
@layer sinh ra để fix đúng vấn đề này.
@layer đặt ra thứ tự rõ ràng - không còn specificity war trong team
Bên cạnh việc tự tổ chức, @layer còn giúp khi import thư viện bên ngoài:
Trước đây override Bootstrap là cả một nghệ thuật. Giờ chỉ cần đặt import của họ vào @layer, còn code của bạn để ngoài - specificity không còn là vấn đề nữa.
Nếu bạn muốn nắm vững CSS từ nền tảng trước khi vào các tính năng nâng cao này, khóa HTML CSS từ Zero đến Hero có thể giúp bạn build nền tốt.
:has() selector: CSS viết được logic mà trước đây chỉ JS làm được
:has() là cái selector mình đợi lâu nhất. Nó cho phép bạn style một element dựa trên con hoặc sibling của nó - thứ mà CSS trước đây không bao giờ làm được.
Ví dụ thực tế: form validation styling mà không cần JS.
:has() selector - từng được gọi là impossible, giờ chạy trên mọi evergreen browser
Use case yêu thích của mình là sidebar layout. Trước đây phải dùng JS để detect xem sidebar có open không rồi toggle class cho main content:
Code ngắn hơn. Không event listener. Không state management. Chỉ CSS thuần.
Layout hiện đại: Grid, subgrid và aspect-ratio
CSS Grid không còn mới, nhưng subgrid và grid-auto-flow dense thì vẫn bị nhiều người bỏ qua. Kết hợp với aspect-ratio, bạn có thể xây dựng layout phức tạp mà không cần tính toán tay.
Subgrid - khi children cần align với grid cha
Không cần min-height. Không cần flexbox stretch. Card tự align nhau.
grid-auto-flow: dense
Masonry-style grid bằng CSS thuần - không cần Masonry.js hay thư viện nào
aspect-ratio cho media
Trước khi có aspect-ratio, mình phải dùng padding-bottom hack (padding-bottom: 56.25% cho 16:9). Giờ thì không cần nhớ công thức đó nữa.
Responsive typography không cần breakpoint
Typography responsive là thứ tốn thời gian debug nhất. Trước đây cách thông thường là:
Ba breakpoints chỉ để scale font. Nhân với số heading levels và font properties khác - bạn có một mớ CSS khó maintain.
Giải pháp hiện đại là clamp() kết hợp với viewport units:
clamp() thay thế hàng chục breakpoints bằng một dòng CSS
Công thức tính giá trị giữa của clamp() cho fluid typography:
Font scale = preferred-size * 100vw + offset
Ví dụ muốn font từ 16px (320px viewport) đến 24px (1200px viewport):
- Slope = (24 - 16) / (1200 - 320) = 0.00909 → xấp xỉ 0.91vw
- Offset = 16 - 0.91 * 3.2 = 13.09px ≈ 0.82rem
- Kết quả:
clamp(1rem, 0.91vw + 0.82rem, 1.5rem)
Có thể tính tay hoặc dùng tool online như Fluid Type Scale Calculator để generate nhanh hơn.
