Container query membuat satu komponen dapat menyesuaikan tampilannya mengikuti lebar wadahnya, bukan lebar jendela browser. Topik ini membahas properti container-type, aturan @container beserta penamaan kontainernya, satuan cqw dan cqi, perbedaannya dengan media query, serta dukungan browser yang berstatus Baseline sejak 2023. Setelah membacanya, kartu yang sama dapat ditempatkan di sidebar maupun di konten utama tanpa menyalin satu baris gaya pun.
Keterbatasan media query pada aras komponen#
Media query menguji lebar viewport, sehingga seluruh komponen pada halaman menerima keputusan yang sama pada lebar yang sama. Keadaan itu menjadi membebani ketika komponen yang sama dipakai di dua tempat dengan ruang yang berbeda, misalnya kartu di dalam sidebar dan kartu yang identik di dalam kolom utama. Sidebar selebar 240 piksel membutuhkan kartu bertumpuk, sedangkan kolom utama selebar 800 piksel memuat kartu yang sama dengan susunan mendatar.
Dengan media query, kedua kartu itu tetap berubah bersamaan ketika viewport menyentuh breakpoint, karena keduanya diuji terhadap ukuran yang sama. Solusi lama menyalin komponen menjadi dua versi kelas, misalnya kartu-samping dan kartu-lebar, kemudian memelihara keduanya secara terpisah. Setiap penambahan fitur kartu harus ditulis dua kali, dan ketidaksesuaian tampilan antar versinya mudah muncul tanpa disadari.
Container query membalik arah pengujian tersebut. Browser menguji lebar kontainer terdekat yang telah ditetapkan, lalu menerapkan gaya pada isinya sesuai hasil ujian itu. Komponen menjadi mandiri karena penyesuaiannya melekat pada komponen, dan bukan pada halaman tempat komponen itu diletakkan.
container-type dan konteks containment#
Sebuah elemen menjadi kontainer query setelah diberi properti container-type dengan nilai inline-size. Nilai ini menyatakan bahwa pengujian dilakukan pada arah inline, yaitu arah penulisan teks yang pada tulisan horizontal berarti lebar. Elemen yang telah ditetapkan menjadi konteks containment, yaitu lingkup terpisah yang menghitung layout dan gaya keturunannya secara mandiri, sehingga perubahan isinya tidak merambat keluar lingkup tersebut.
.sidebar {
container-type: inline-size;
}Deklarasi di atas menjadikan elemen .sidebar kontainer bagi seluruh keturunannya. Penempatannya biasanya pada wadah tata letak, misalnya elemen aside, sel grid, atau pembungkus kartu, dan bukan pada kartu itu sendiri. Alasannya, aturan @container hanya boleh menata elemen di dalam kontainernya, sehingga elemen yang menjadi kontainer tidak dapat menata dirinya sendiri melalui query miliknya.
Aturan @container dan penamaan kontainer#
@container (min-width: 400px) {
.kartu {
display: flex;
flex-direction: row;
gap: 12px;
}
}Aturan @container menampung deklarasi yang hanya berlaku bila kontainernya memenuhi kondisi lebar yang ditulis. Struktur kondisinya sama dengan media query, tetapi min-width pada konteks ini berarti lebar kontainer dan bukan lebar viewport. Browser mengevaluasi kondisi itu terhadap kontainer leluhur terdekat dari setiap elemen yang ditata, sehingga aturan yang sama dapat menghasilkan keputusan berbeda untuk dua kartu yang berada di dua kontainer berbeda.
Kondisi pada @container juga menerima satuan selain piksel, misalnya em yang mengikuti ukuran font kontainernya, sehingga ambang perubahan dapat ditulis dalam satuan yang sama dengan tipografinya. Blok @container boleh ditulis bersarang ketika komponen di dalam komponen masing-masing memiliki kontainernya sendiri. Pada keadaan itu, kondisi blok terluar diuji terlebih dahulu, dan deklarasi di dalamnya baru diuji ulang terhadap kontainer yang lebih dekat.
Kontainer juga dapat diberi nama melalui properti container-name, dan aturan @container kemudian menyebut nama itu sebelum kondisinya, misalnya @container sidebar (min-width: 400px). Penamaan menegaskan kontainer mana yang diuji ketika halaman memiliki beberapa tingkat kontainer sekaligus. Tanpa nama, pengujian selalu memilih kontainer terdekat, yang pada umumnya sudah sesuai untuk komponen tunggal.
Satuan container query#
Satuan cqw dan cqi masing-masing menyatakan satu persen lebar dan ukuran inline kontainer, sedangkan cqh menyatakan satu persen tingginya. Pada tulisan horizontal, cqi bernilai sama dengan cqw, tetapi cqi tetap dipilih karena mengikuti arah penulisan. Satuan ini dihitung ulang setiap kali ukuran kontainernya berubah, termasuk ketika perubahan itu berasal dari grid induk dan bukan dari viewport.
@container (min-width: 300px) {
.kartu h2 {
font-size: calc(1rem + 1.5cqi);
}
}Pada contoh tersebut, judul kartu membesar mengikuti lebar kartunya, dan dua kartu dengan lebar berbeda menghasilkan ukuran judul yang berbeda pula. Perilaku ini tidak dapat dicapai dengan satuan vw karena vw selalu menghitung dari jendela browser. Apabila elemen tidak berada di dalam kontainer apa pun, satuan cq menghitung dari ukuran viewport terkecil sebagai cadangannya, sehingga nilainya tetap terhitung tanpa error.
Satuan cq juga lazim dipakai di luar blok @container, misalnya pada padding atau lebar elemen yang seharusnya proporsional terhadap wadahnya. Perhitungannya tetap dilakukan terhadap kontainer leluhur terdekat, sehingga satu deklarasi dapat dipakai kartu mana pun tanpa menyetel ukurannya per tempat. Kebiasaan ini menjaga skala komponen tetap seragam meskipun wadah-wadahnya berbeda lebar.
Perbedaan dengan media query#
Perbedaan mendasar keduanya terletak pada acuan pengujian. Media query menguji viewport sehingga hasilnya sama untuk seluruh halaman, sedangkan container query menguji kontainer sehingga hasilnya dapat berbeda antar wadah pada halaman yang sama. Tabel berikut merangkum perbedaan yang paling memengaruhi keputusan harian.
| Aspek | Media query | Container query |
|---|---|---|
| Acuan pengujian | viewport | kontainer terdekat |
| Cakupan gaya | seluruh halaman | keturunan kontainer |
| Komponen sama di dua wadah | berubah bersamaan | menyesuaikan masing-masing |
| Satuan pendamping | vw dan vh | cq dan cqi |
| Kegunaan utama | struktur halaman | penyesuaian komponen |
Keduanya bukan pengganti, melainkan pelengkap. Media query tetap menjadi alat yang tepat untuk mengubah kerangka halaman, misalnya memindahkan area navigasi dan konten pada layar sempit. Container query mengambil alih penyesuaian yang semula dipaksakan ke media query, yaitu respons komponen terhadap ruang yang benar-benar dihuninya. Dukungan browser untuk container query telah lengkap pada browser utama sejak 2023 dan tercatat berstatus Baseline pada tahun itu, sehingga penggunaannya aman untuk proyek baru.
Pola nyata: kartu di sidebar dan konten utama#
Pola yang paling umum adalah satu kartu yang dipasang di dua wadah berbeda lebar. Kartu pada wadah sempit menyusun gambar di atas teksnya, sedangkan kartu yang sama pada wadah lebar menyusun keduanya berdampingan. Kedua keadaan itu ditangani satu aturan @container tanpa duplikasi kelas maupun salinan komponen.
Demo berikut menaruh kartu yang sama pada dua kontainer dengan lebar berbeda di dalam satu halaman. Amati bahwa lebar halaman untuk keduanya identik, sehingga yang membedakan susunannya hanyalah lebar kontainernya. Persempit jendela pratinjau hingga kontainer lebar menyusut melewati 400 piksel, lalu kartu di dalamnya ikut berubah menjadi bertumpuk, sedangkan kartu pada kontainer sempit tidak berubah karena lebar kontainernya memang tetap.
Kode sumber demo
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Kartu pada dua kontainer</title>
<link rel="stylesheet" href="index.css">
</head>
<body>
<p class="label">Kontainer sempit, lebar 240 piksel</p>
<div class="kontainer sempit">
<article class="kartu">
<div class="gambar"></div>
<div class="isi">
<h2>Jadwal piket</h2>
<p>Piket kelas dimulai Senin dengan tiga siswa per hari.</p>
</div>
</article>
</div>
<p class="label">Kontainer lebar, lebar hingga 520 piksel</p>
<div class="kontainer lebar">
<article class="kartu">
<div class="gambar"></div>
<div class="isi">
<h2>Jadwal piket</h2>
<p>Piket kelas dimulai Senin dengan tiga siswa per hari.</p>
</div>
</article>
</div>
<style>
/* Demo container query: kartu menyesuaikan lebar kontainernya */
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 16px;
font-family: sans-serif;
background: #f1f5f9;
color: #1e293b;
}
.label {
margin: 4px 0 6px;
font-size: 13px;
font-weight: bold;
color: #334155;
}
.kontainer {
container-type: inline-size;
margin-bottom: 16px;
}
.sempit {
width: 240px;
}
.lebar {
width: min(520px, 100%);
}
.kartu {
display: flex;
flex-direction: column;
gap: 10px;
background: #ffffff;
border: 1px solid #cbd5e1;
border-radius: 8px;
padding: 12px;
}
.gambar {
height: 72px;
border-radius: 6px;
background: linear-gradient(135deg, #93c5fd, #3b82f6);
}
.isi h2 {
margin: 0 0 4px;
font-size: 15px;
}
.isi p {
margin: 0;
font-size: 13px;
line-height: 1.5;
}
@container (min-width: 400px) {
.kartu {
flex-direction: row;
align-items: center;
}
.gambar {
width: 120px;
height: 64px;
flex-shrink: 0;
}
}
</style>
<style>
/* Demo container query: kartu menyesuaikan lebar kontainernya */
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 16px;
font-family: sans-serif;
background: #f1f5f9;
color: #1e293b;
}
.label {
margin: 4px 0 6px;
font-size: 13px;
font-weight: bold;
color: #334155;
}
.kontainer {
container-type: inline-size;
margin-bottom: 16px;
}
.sempit {
width: 240px;
}
.lebar {
width: min(520px, 100%);
}
.kartu {
display: flex;
flex-direction: column;
gap: 10px;
background: #ffffff;
border: 1px solid #cbd5e1;
border-radius: 8px;
padding: 12px;
}
.gambar {
height: 72px;
border-radius: 6px;
background: linear-gradient(135deg, #93c5fd, #3b82f6);
}
.isi h2 {
margin: 0 0 4px;
font-size: 15px;
}
.isi p {
margin: 0;
font-size: 13px;
line-height: 1.5;
}
@container (min-width: 400px) {
.kartu {
flex-direction: row;
align-items: center;
}
.gambar {
width: 120px;
height: 64px;
flex-shrink: 0;
}
}
</style></body>
</html>Dengan media query, perubahan pada demo hanya dapat dinyatakan terhadap lebar halaman, sehingga kedua kartu berubah bersamaan. Aturan @container menghapus ketergantungan itu, dan komponen menjadi dapat dipindahkan ke wadah mana pun tanpa penyesuaian tambahan. Sifat inilah yang membuat container query sering disebut responsivitas pada aras komponen.
Ringkasan#
- Properti
container-type: inline-sizemenjadikan elemen konteks containment sekaligus kontainer bagi query keturunannya. - Aturan
@containermenguji lebar kontainer terdekat, dan kontainer bernama diuji melalui@container nama (kondisi). - Satuan
cqw,cqi, dancqhmenyatakan persen ukuran kontainer sehingga ukuran elemen mengikuti wadahnya. - Media query menguji viewport untuk struktur halaman, sedangkan container query menyesuaikan komponen terhadap wadahnya, dengan dukungan Baseline sejak 2023.
Referensi#
- @container — MDN Web Docs
— Dokumentasi aturan
@containerbeserta kondisi, penamaan kontainer, dan contohnya. - container-type — MDN Web Docs — Dokumentasi properti penentu kontainer query beserta dampak containment yang menyertainya.
- CSS container queries — MDN Web Docs — Panduan konsep container query dari penetapan kontainer hingga satuan cq.