Học cách dùng CSS Container Queries để tạo layout đáp ứng linh hoạt theo kích thước vùng chứa, không chỉ giới hạn bởi khung nhìn (viewport).
CSS Container Queries: Hướng Dẫn Toàn Diện về Định Nghĩa Truy Vấn Vùng Chứa
Thiết kế web đáp ứng (Responsive web design) đã phát triển vượt bậc. Ban đầu, media query là nền tảng, cho phép bố cục thích ứng dựa trên kích thước của khung nhìn (viewport). Tuy nhiên, khi các trang web trở nên phức tạp và dựa trên component nhiều hơn, nhu cầu về một phương pháp tiếp cận chi tiết và linh hoạt hơn đã trở nên rõ ràng. Đây là lúc CSS Container Queries phát huy tác dụng.
CSS Container Queries là gì?
CSS Container Queries cho phép lập trình viên áp dụng các style cho một phần tử dựa trên kích thước hoặc trạng thái của phần tử chứa nó, thay vì dựa vào khung nhìn. Sự thay đổi cơ bản này tạo điều kiện để xây dựng các component thực sự có thể tái sử dụng và thích ứng, dễ dàng tích hợp vào nhiều bối cảnh khác nhau trên một trang web.
Hãy tưởng tượng một component thẻ (card) cần điều chỉnh bố cục tùy thuộc vào việc nó được đặt trong một thanh bên hẹp hay một khu vực nội dung chính rộng lớn. Với container queries, việc thích ứng này trở nên đơn giản, đảm bảo hiển thị tối ưu bất kể bối cảnh xung quanh.
Tại sao nên sử dụng Container Queries?
- Cải thiện khả năng tái sử dụng Component: Các component trở nên thực sự độc lập và dễ thích ứng, giúp đơn giản hóa việc bảo trì và thúc đẩy tính nhất quán trên các phần khác nhau của trang web.
- Kiểm soát chi tiết hơn: Không giống như media query phụ thuộc vào khung nhìn, container query cung cấp khả năng kiểm soát chi tiết đối với việc tạo style dựa trên môi trường cụ thể của một component.
- Đơn giản hóa việc phát triển: Giảm nhu cầu sử dụng các giải pháp JavaScript phức tạp để quản lý style của component dựa trên vị trí của nó trong bố cục.
- Nâng cao trải nghiệm người dùng: Mang lại trải nghiệm tối ưu trên nhiều thiết bị và kích thước màn hình khác nhau, đảm bảo nội dung luôn được trình bày theo cách phù hợp nhất.
Định nghĩa một Vùng chứa (Container)
Trước khi có thể sử dụng container queries, bạn cần xác định phần tử nào sẽ đóng vai trò là vùng chứa. Điều này được thực hiện bằng cách sử dụng thuộc tính container-type
.
Thuộc tính container-type
Thuộc tính container-type
chỉ định một phần tử có phải là vùng chứa truy vấn hay không, và nếu có, nó là loại vùng chứa nào. Nó chấp nhận các giá trị sau:
size
: Các điều kiện truy vấn của vùng chứa sẽ dựa trên inline-size (chiều rộng trong chế độ viết ngang, chiều cao trong chế độ viết dọc) và block-size (chiều cao trong chế độ viết ngang, chiều rộng trong chế độ viết dọc). Đây là tùy chọn phổ biến và linh hoạt nhất.inline-size
: Các điều kiện truy vấn của vùng chứa sẽ dựa trên inline-size của nó (chiều rộng trong chế độ viết ngang, chiều cao trong chế độ viết dọc).normal
: Phần tử không phải là vùng chứa truy vấn. Đây là giá trị mặc định.style
: Phần tử là một vùng chứa style. Vùng chứa style hiển thị các thuộc tính tùy chỉnh được định nghĩa trên chúng cho các phần tử con cháu thông qua truy vấn@container style()
. Điều này hữu ích cho việc tạo style dựa trên các thuộc tính tùy chỉnh.container-name
(Tùy chọn): Nếu bạn đã đặt tên cho vùng chứa của mình bằng thuộc tínhcontainer-name
, bạn có thể chỉ định nó ở đây để nhắm mục tiêu đến vùng chứa cụ thể đó. Nếu bỏ qua, nó sẽ áp dụng cho vùng chứa tổ tiên gần nhất.condition
: Điều kiện phải được đáp ứng để các style được áp dụng. Điều này có thể dựa trên chiều rộng, chiều cao, hoặc các thuộc tính khác của vùng chứa.cqw
: 1% chiều rộng của vùng chứa.cqh
: 1% chiều cao của vùng chứa.cqi
: 1% kích thước inline của vùng chứa.cqb
: 1% kích thước block của vùng chứa.cqmin
: Giá trị nhỏ hơn củacqi
hoặccqb
.cqmax
: Giá trị lớn hơn củacqi
hoặccqb
.- Bắt đầu với Mobile-First: Thiết kế các component của bạn cho kích thước vùng chứa nhỏ nhất trước, sau đó sử dụng container queries để tăng cường dần bố cục cho các vùng chứa lớn hơn.
- Sử dụng Tên Vùng chứa có ý nghĩa: Nếu bạn lồng các vùng chứa, hãy sử dụng các tên mô tả rõ ràng mục đích của từng vùng chứa.
- Tránh các Truy vấn quá phức tạp: Giữ cho các container query của bạn đơn giản và tập trung. Quá nhiều truy vấn phức tạp có thể làm cho mã của bạn khó hiểu và khó bảo trì.
- Kiểm tra kỹ lưỡng: Kiểm tra các component của bạn ở nhiều kích thước và bối cảnh vùng chứa khác nhau để đảm bảo chúng thích ứng chính xác.
- Cân nhắc về Hiệu suất: Lưu ý đến tác động hiệu suất của container queries, đặc biệt khi sử dụng các truy vấn phức tạp hoặc một số lượng lớn các vùng chứa.
- Duy trì cấu trúc ngữ nghĩa: Đảm bảo rằng cấu trúc HTML cơ bản vẫn giữ được tính ngữ nghĩa và có thể truy cập, bất kể kích thước của vùng chứa.
- Kiểm tra với các công nghệ hỗ trợ: Kiểm tra các component của bạn với trình đọc màn hình và các công nghệ hỗ trợ khác để xác minh rằng nội dung vẫn có thể truy cập và dễ hiểu.
- Cung cấp nội dung thay thế: Nếu kích thước vùng chứa thay đổi đáng kể nội dung, hãy xem xét cung cấp nội dung hoặc cơ chế thay thế để đảm bảo người dùng khuyết tật có thể truy cập thông tin.
Ví dụ:
.container {
container-type: size;
}
Đoạn mã này định nghĩa một phần tử với class container
là một vùng chứa truy vấn, làm cho kích thước của nó khả dụng cho các container query.
Ngoài ra, bạn có thể sử dụng container: inline-size
hoặc container: size
. Thuộc tính viết tắt container
có thể thiết lập cả container-type
và container-name
trong một khai báo duy nhất. Tên của vùng chứa được sử dụng để nhắm mục tiêu đến một vùng chứa cụ thể khi lồng các vùng chứa vào nhau.
Sử dụng Container Queries
Sau khi đã định nghĩa một vùng chứa, bạn có thể sử dụng quy tắc @container
để áp dụng các style dựa trên kích thước hoặc trạng thái của nó.
Quy tắc @container
Quy tắc @container
tương tự như quy tắc @media
, nhưng thay vì nhắm mục tiêu vào khung nhìn, nó nhắm mục tiêu vào một vùng chứa cụ thể. Cú pháp như sau:
@container [container-name] (condition) {
/* Styles to apply when the condition is met */
}
Ví dụ:
.card {
display: flex;
flex-direction: column;
}
@container (min-width: 400px) {
.card {
flex-direction: row;
}
.card__image {
width: 40%;
}
.card__content {
width: 60%;
}
}
Trong ví dụ này, phần tử .card
sẽ chuyển từ bố cục cột sang bố cục hàng khi vùng chứa của nó rộng ít nhất 400px. Các phần tử .card__image
và .card__content
cũng sẽ điều chỉnh chiều rộng của chúng cho phù hợp.
Các đơn vị của Container Query
Container queries giới thiệu các đơn vị mới có tính tương đối với kích thước của vùng chứa:
Các đơn vị này cho phép bạn tạo ra các style thực sự tương đối với kích thước của vùng chứa, làm cho các component của bạn trở nên dễ thích ứng hơn nữa.
Ví dụ:
.element {
font-size: 2cqw;
padding: 1cqh;
}
Trong ví dụ này, kích thước phông chữ của .element
sẽ là 2% chiều rộng của vùng chứa, và padding của nó sẽ là 1% chiều cao của vùng chứa.
Các ví dụ trong thực tế
Hãy cùng khám phá một số ví dụ thực tế về cách sử dụng container queries để tạo ra các component đáp ứng và dễ thích ứng.
Ví dụ 1: Component Thẻ (Card)
Xem xét một component thẻ hiển thị thông tin về một sản phẩm. Component này có thể cần phải điều chỉnh bố cục của nó tùy thuộc vào vị trí nó được đặt trên trang.
HTML:
CSS:
.container {
container-type: inline-size;
width: 100%;
max-width: 800px;
margin: 0 auto;
}
.card {
display: flex;
flex-direction: column;
border: 1px solid #ccc;
padding: 16px;
}
.card__image {
width: 100%;
margin-bottom: 16px;
}
.card__title {
font-size: 1.5rem;
margin-bottom: 8px;
}
@container (min-width: 500px) {
.card {
flex-direction: row;
}
.card__image {
width: 40%;
margin-bottom: 0;
margin-right: 16px;
}
.card__content {
width: 60%;
}
}
Trong ví dụ này, phần tử .container
được định nghĩa là một vùng chứa inline-size. Khi vùng chứa rộng dưới 500px, component thẻ sẽ hiển thị hình ảnh và nội dung theo bố cục cột. Khi vùng chứa rộng 500px trở lên, component thẻ sẽ chuyển sang bố cục hàng, với hình ảnh bên trái và nội dung bên phải. Điều này đảm bảo rằng component thẻ trông đẹp mắt bất kể nó được đặt ở đâu trên trang.
Ví dụ 2: Menu Điều hướng
Một trường hợp sử dụng phổ biến khác cho container queries là điều chỉnh menu điều hướng dựa trên không gian có sẵn.
HTML:
CSS:
.nav-container {
container-type: inline-size;
width: 100%;
background-color: #f0f0f0;
}
.nav {
padding: 16px;
}
.nav__list {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.nav__item {
margin-bottom: 8px;
}
.nav__link {
text-decoration: none;
color: #333;
padding: 8px 16px;
border: 1px solid #ccc;
border-radius: 4px;
}
@container (max-width: 400px) {
.nav__list {
flex-direction: column;
}
.nav__item {
margin-bottom: 16px;
}
.nav__link {
display: block;
width: 100%;
text-align: center;
}
}
Trong ví dụ này, phần tử .nav-container
được định nghĩa là một vùng chứa inline-size. Khi vùng chứa rộng 400px hoặc nhỏ hơn, menu điều hướng sẽ chuyển sang bố cục cột, với mỗi liên kết chiếm toàn bộ chiều rộng của vùng chứa. Khi vùng chứa rộng hơn 400px, menu điều hướng sẽ hiển thị các liên kết theo hàng, có khoảng cách giữa chúng. Điều này cho phép menu điều hướng thích ứng với các kích thước và hướng màn hình khác nhau.
Lồng các Vùng chứa
Container queries có thể được lồng vào nhau, cho phép kiểm soát style một cách phức tạp và chi tiết hơn nữa. Để nhắm mục tiêu đến một vùng chứa cụ thể khi lồng, bạn có thể sử dụng thuộc tính container-name
để đặt tên duy nhất cho các vùng chứa của mình. Sau đó, trong quy tắc @container
, bạn có thể chỉ định tên của vùng chứa bạn muốn nhắm mục tiêu.
Ví dụ:
Content
.outer-container {
container-type: inline-size;
container-name: outer;
}
.inner-container {
container-type: inline-size;
container-name: inner;
}
@container outer (min-width: 500px) {
.inner-container {
background-color: lightblue;
}
}
@container inner (min-width: 300px) {
p {
font-size: 1.2rem;
}
}
Trong ví dụ này, .outer-container
được đặt tên là "outer" và .inner-container
được đặt tên là "inner". Quy tắc @container
đầu tiên nhắm mục tiêu đến vùng chứa "outer" và áp dụng màu nền cho .inner-container
khi vùng chứa "outer" rộng ít nhất 500px. Quy tắc @container
thứ hai nhắm mục tiêu đến vùng chứa "inner" và tăng kích thước phông chữ của phần tử p
khi vùng chứa "inner" rộng ít nhất 300px.
Hỗ trợ trình duyệt
Container queries nhận được sự hỗ trợ tuyệt vời và ngày càng tăng từ các trình duyệt. Hầu hết các trình duyệt hiện đại đều hỗ trợ đầy đủ các tính năng container-type
, container-name
, và @container
. Bạn nên luôn kiểm tra Can I use để biết thông tin tương thích mới nhất.
Đối với các trình duyệt cũ không hỗ trợ container queries, bạn có thể sử dụng polyfill để cung cấp hỗ trợ dự phòng. Tuy nhiên, cần lưu ý rằng polyfill có thể không sao chép hoàn hảo hành vi của container queries gốc, và chúng có thể làm tăng thời gian tải trang.
Các phương pháp hay nhất
Dưới đây là một số phương pháp hay nhất cần ghi nhớ khi làm việc với container queries:
Những lưu ý về khả năng tiếp cận
Mặc dù container queries chủ yếu tập trung vào các điều chỉnh bố cục trực quan, điều quan trọng là phải xem xét khả năng tiếp cận để đảm bảo rằng các component của bạn vẫn có thể sử dụng được cho mọi người.
Ngoài Kích thước: Truy vấn Trạng thái (State Queries)
Mặc dù các container query dựa trên kích thước là phổ biến nhất, tương lai của container queries còn vượt ra ngoài kích thước. Có những thông số kỹ thuật và đề xuất mới nổi cho truy vấn style (style queries) và truy vấn trạng thái (state queries).
Truy vấn Style cho phép bạn áp dụng các style dựa trên các thuộc tính tùy chỉnh được định nghĩa trên vùng chứa. Điều này cho phép tạo style mạnh mẽ dựa trên dữ liệu và cấu hình động.
Truy vấn Trạng thái sẽ cho phép bạn truy vấn trạng thái của một vùng chứa, chẳng hạn như nó có đang được focus, hover, hay có một class cụ thể được áp dụng hay không. Điều này có thể mở ra nhiều khả năng hơn nữa cho các component thích ứng đáp ứng với tương tác của người dùng.
Kết luận
CSS Container Queries là một công cụ mạnh mẽ để tạo ra các web component đáp ứng và dễ thích ứng. Bằng cách cho phép bạn tạo style cho các phần tử dựa trên kích thước hoặc trạng thái của phần tử chứa chúng, container queries cung cấp một phương pháp tiếp cận chi tiết và linh hoạt hơn cho thiết kế đáp ứng so với media queries truyền thống. Khi sự hỗ trợ của trình duyệt tiếp tục được cải thiện, container queries sẵn sàng trở thành một phần thiết yếu trong bộ công cụ của mọi nhà phát triển web. Hãy tận dụng chúng để xây dựng những trải nghiệm web mạnh mẽ, có thể tái sử dụng và thân thiện với người dùng hơn cho khán giả toàn cầu.
Những khả năng được mở ra bởi container queries vượt xa những điều chỉnh bố cục đơn giản. Chúng cho phép tạo ra các component nhận biết ngữ cảnh có thể thích ứng với nhiều tình huống khác nhau, mang lại trải nghiệm người dùng liền mạch và trực quan hơn. Khi bạn khám phá tính năng mạnh mẽ này, hãy xem xét cách nó có thể nâng cao khả năng tái sử dụng, bảo trì và thích ứng của các dự án web của bạn, cuối cùng góp phần vào một trang web toàn diện và có thể truy cập trên toàn cầu.
Bằng cách tận dụng sức mạnh của container queries, bạn có thể tạo ra những trải nghiệm web không chỉ hấp dẫn về mặt hình ảnh mà còn có khả năng thích ứng cao và lấy người dùng làm trung tâm, phục vụ nhu cầu đa dạng của khán giả toàn cầu.