Tata Letak CSS dan Desain Responsif

  1. 1 Dasar-Dasar
    1. Mode tata letak mana yang harus digunakan?
    2. Prinsip kerja
  2. 2 Flexbox
  3. 3 Petak dan subpetak
    1. Contoh kode: petak dan subpetak
  4. 4 Kueri penampung
    1. Contoh kode: tipografi fluid menggunakan unit kueri penampung
  5. 5 Overlay native, pemosisian anchor, dan konteks tumpukan
  6. 6 Pelacakan overflow dan stabilitas tata letak
  7. 7 Mekanika area pandang dan distribusi trek
  8. 8 Lajur petak (alias masonry)

1 Dasar-Dasar

Andalkan mesin tata letak browser jika memungkinkan untuk mendapatkan performa yang lebih baik. Gunakan ukuran intrinsik, properti logis, dan aspect-ratio sebelum menggunakan dimensi hardcode atau kueri media yang rumit.

1.1 Mode tata letak mana yang akan digunakan?

Ikuti pohon keputusan dari atas ke bawah dan berhenti pada kecocokan pertama. Perhatikan bahwa tata letak dapat disusun bertingkat satu sama lain dan setiap keputusan didasarkan pada kasus penggunaan untuk penampung tersebut.

  1. Apakah itemnya berupa baris ATAU kolom sederhana? Gunakan flexbox — 1D, mengutamakan konten, konten didistribusikan di sepanjang satu sumbu.
  2. Apakah elemen bertingkat perlu sejajar dengan jalur petak induknya? Gunakan subgrid — 2D, mengutamakan hubungan, mewarisi jalur induk sehingga turunan dapat sejajar di seluruh turunan.
  3. Apakah halaman atau struktur komponennya kompleks dengan baris DAN kolom? Gunakan petak — 2D, mengutamakan tata letak, Anda menentukan kerangka dan konten akan mengisinya.
  4. Apakah kontennya berupa prosa panjang yang harus dibagi menjadi kolom-kolom yang seimbang? Gunakan multi-kolom — alur 1D, gaya surat kabar.
  5. Apakah item memiliki tinggi yang bervariasi dan perlu dikemas rapat? Gunakan grid dengan grid-auto-flow: dense sekarang; gunakan masonry native (alias "jalur petak") hanya jika dikirimkan di target Dasar pengukuran Anda (lihat §8).
  6. Apakah elemen perlu mengambang di atas halaman dan tetap tertambat secara spasial ke pemicu, bahkan di seluruh batas DOM atau konteks tumpukan? Gunakan pemosisian anchor — anchor-name pada pemicu, position-anchor pada overlay (lihat §5).

1.2 Prinsip kerja

  • Gunakan properti logis (inline-size, block-size, margin-inline, padding-block, inset-inline-start) untuk dimensi dan spasi tata letak — lihat CSS: Arsitektur Modern untuk cakupan lengkap.
  • Terapkan model mental konten-first vs tata letak-first: flexbox saat item menentukan alur, petak saat Anda menentukan kerangka terlebih dahulu.
  • Gunakan singkatan place-* (place-content, place-items, place-self) untuk meratakan di kedua sumbu dalam satu deklarasi.
  • Gunakan ukuran intrinsik (min-content, max-content, fit-content()) dan jalur fleksibel (fr, minmax()) sebelum width/height tetap — lebih sedikit kueri media, tata letak yang lebih tangguh.
  • JANGAN gunakan overflow: hidden untuk menyamarkan masalah tata letak — perbaiki masalah tersebut di sumbernya. Misalnya, elemen tertentu yang diganti seperti <canvas>, <img>, <svg>, <video>, <iframe> menggunakan display: inline secara default, yang dapat menambahkan celah dalam tata letak tertentu. Daripada memotong celah dengan overflow, hapus celah tersebut dengan menerapkan nilai display-outside blok (misalnya, display: block, grid, flex, dll.).
  • Gunakan aspect-ratio untuk mencadangkan ruang bagi media dan mencegah pergeseran tata letak sebelum aset dimuat.
.sidebar       { inline-size: max-content; }    /* Size to longest unbreakable token. */
.main-content  { inline-size: fit-content; }    /* Grow to available space, no further. */
.media         { aspect-ratio: 16 / 9; inline-size: 100%; block-size: auto; }
body.centered  { display: grid; place-content: center; min-block-size: 100dvb; }

Untuk calc-size() dan ukuran intrinsik yang mempertimbangkan batasan, lihat Menghitung Dengan Ukuran Intrinsik.

2 Flexbox

Tata letak satu dimensi — item mengalir di sepanjang satu sumbu utama dengan perataan pada sumbu silang. Gunakan untuk navbar, toolbar, baris item, dan distribusi baris atau kolom tunggal apa pun.

Lakukan:

  • Buat konteks dengan display: flex dan tetapkan sumbu utama dengan flex-direction (row default).
  • Gunakan flex-wrap: wrap setiap kali ada kemungkinan overflow — nowrap tanpa overflow: auto/hidden akan tumpah pada area pandang yang sempit.
  • Lapisi flex-wrap: balance setelah flex-wrap: wrap pada baris item ringkas yang digabungkan (seperti tag cloud, pil filter, grup tombol, atau link navigasi) untuk mendistribusikan item secara merata di seluruh baris dan mencegah item tunggal yang tidak terhubung di baris akhir; browser yang tidak mendukung akan kembali ke wrap.
  • Gunakan singkatan flex <grow> <shrink> <basis> (misalnya, flex: 1 1 250px) pada item, bukan menetapkan flex-grow/flex-shrink/flex-basis satu per satu.
  • Gunakan gap (atau bentuk panjang row-gap/column-gap) untuk spasi di antara item, bukan margin turunan.
  • Tambahkan awalan perataan posisi dengan safe (misalnya, align-items: safe center) agar konten yang dapat difokuskan tidak terpotong saat penampung lebih sempit daripada kontennya.
  • Dorong satu item ke ujung sumbu utama dengan margin-inline-start: auto (atau margin-block-start: auto) — ini adalah escape hatch standar.
  • Mengganti perataan sumbu silang per item dengan align-self.
  • Gunakan align-items untuk memusatkan semua item pada sumbu silang; gunakan margin: auto pada satu item untuk memusatkannya pada kedua sumbu secara independen; gunakan align-content hanya jika penampung digabungkan dan memiliki ruang ekstra di seluruh baris.
  • Tetapkan min-inline-size: 0 (atau min-width: 0) pada item flex yang berisi konten panjang yang tidak dapat dipisahkan (URL, kode, string panjang) — item flex tidak akan menyusut di bawah ukuran kontennya secara default, sehingga menyebabkan overflow.

Jangan:

  • Jangan gunakan justify-self pada item flex — properti ini hanya berfungsi pada tata letak petak, blok, dan yang diposisikan secara absolut. Gunakan margin otomatis.
  • Jangan gunakan order atau flex-direction: *-reverse untuk mengurutkan ulang konten interaktif. Properti ini hanya mengubah urutan visual; urutan DOM masih mendorong fokus berurutan, sehingga alur tab keyboard tidak akan cocok dengan yang dilihat pengguna.
  • Jangan bingung antara space-around (setengah celah di ujung) dengan space-evenly (celah yang sama sebelum, di antara, dan setelah).
  • Jangan lupa pembalikan sumbu: saat flex-direction: column, justify-content sejajar pada sumbu blok dan align-items sejajar pada sumbu inline — kebalikan dari default.
  • Jangan mengubah ukuran penampung dan turunannya untuk saling mengisi — ini adalah sumber umum terjadinya overflow dan hasil yang mengejutkan. Berikan ukuran yang pasti pada satu sisi.
  • Jangan tetapkan flex-basis dan width/inline-size pada item yang sama — flex-basis lebih diprioritaskan dalam konteks flex dan width akan diabaikan. Gunakan flex-basis (atau singkatan flex) sebagai satu sumber tepercaya untuk menentukan ukuran item flex.
.card-grid        { display: flex; flex-flow: row wrap; gap: 1rem; }
.card-item        { flex: 1 1 250px; }                  /* grow, shrink, basis */
.card-item-action { margin-inline-start: auto; }        /* Push to main-axis end. */
.toolbar          { display: flex; align-items: safe center; }
.tag-list         { display: flex; flex-wrap: wrap; flex-wrap: balance; gap: 0.5rem; }

3 Petak dan subpetak

Tata letak dua dimensi — tentukan baris DAN kolom secara eksplisit, atau biarkan mesin menurunkannya. Subpetak memungkinkan petak bertingkat mewarisi jalur induknya sehingga turunan kedua sejajar dengan elemen seinduk.

Memilih fitur petak:

  • Apakah Anda tahu persis berapa banyak kolom yang Anda butuhkan?
    • Ya — gunakan jalur eksplisit (grid-template-columns: 200px 1fr, repeat(3, 1fr), dll.)
    • Apakah kolom yang berbeda memerlukan ukuran yang berbeda (sidebar + utama, header yang mencakup semuanya)? → gunakan grid-template-areas untuk region yang diberi nama dan dapat dibaca
    • Apakah semua kolom seragam atau diposisikan murni berdasarkan nomor baris? → gunakan repeat(N, ...) atau garis bernama
    • Tidak (responsif, jumlah item tidak diketahui) — gunakan repeat(auto-fit, minmax(min, 1fr))
    • Haruskah item di baris terakhir direntangkan untuk mengisi ruang yang tersisa? → auto-fit
    • Haruskah trek baris terakhir yang kosong mempertahankan ukuran minimumnya (mempertahankan slot hantu kolom)? → auto-fill
  • Apakah Anda perlu menempatkan item di lokasi tertentu?
    • Ya — gunakan grid-column: <start> / <end> atau grid-area: <name>
    • Tidak (hanya mencakup beberapa jalur, posisi alur tidak penting) — gunakan grid-column: span <n>
  • Apakah elemen turunan perlu mewarisi ukuran jalur petak induk (perataan tepi tidak rata di seluruh elemen seinduk)?
    • Ya — gunakan subgrid pada sumbu yang terpengaruh
    • Apakah jumlah anak per sel bervariasi? → subgrid hanya satu sumbu; gunakan grid-auto-rows/grid-auto-columns untuk sumbu lainnya
    • Apakah jumlah anak sudah diperbaiki? → subgrid pada kedua sumbu tidak masalah
    • Tidak — petak standar, tidak memerlukan subpetak

Lakukan:

  • Buat konteks dengan display: grid.
  • Gunakan grid-template-areas untuk tata letak tingkat halaman yang kompleks — nama area mendokumentasikan dirinya sendiri dan deklarasi dapat disejajarkan dalam baris dan kolom agar mudah dibaca secara sekilas.
  • Gunakan repeat(auto-fit, minmax(200px, 1fr)) untuk petak kartu responsif yang merentangkan trek yang terisi untuk mengisi baris, atau auto-fill untuk mempertahankan trek berulang yang kosong pada ukuran minimumnya.
  • Gunakan fr untuk distribusi jalur proporsional dan minmax(min, max) untuk jalur yang fleksibel, tetapi terbatas.
  • Posisikan item dengan grid-column: span <n> untuk menentukan ukuran di seluruh trek, grid-column: <start> / <end> untuk menempatkan di baris tertentu, atau grid-area: <name> untuk region bernama.
  • Gunakan subgrid (grid-template-columns: subgrid atau grid-template-rows: subgrid) untuk mengatasi masalah "tepi tidak rata" dalam daftar kartu — elemen internal seperti judul, metadata, dan CTA sejajar di seluruh elemen seinduk.
  • Pasangkan deklarasi subpetak dengan deklarasi grid-template-rows/-columns eksplisit sebelumnya sebagai penggantian cascade yang sama untuk browser lama.

Jangan:

  • Jangan mengharapkan ukuran jalur auto-fit/auto-fill berasal dari konten item — ukuran tersebut berasal dari argumen ukuran repeat().
  • Jangan gunakan grid-auto-flow: dense pada konten interaktif. Properti ini mengemas item secara efisien, tetapi menyusun ulang item secara visual, sehingga mengganggu alur tab keyboard urutan DOM.
  • Jangan terapkan subgrid ke kedua sumbu jika jumlah turunan bervariasi. Ekstra ditempatkan di jalur terakhir; gunakan grid-auto-rows/grid-auto-columns untuk sumbu implisit.
  • Jangan bingung antara justify-items/align-items (menyejajarkan konten item dalam jalurnya) dengan justify-content/align-content (menyejajarkan jalur petak dalam penampung). Menggunakan yang salah tidak akan berpengaruh apa pun.
  • Jangan gunakan repeat(auto-fit/auto-fill, ...) tanpa inline-size yang pasti pada penampung — di dalam display: inline-grid atau item flex tanpa ukuran, penampung tidak memiliki lebar untuk dibagi, sehingga jumlah jalur tidak dapat diprediksi.

3.1 Contoh kode: petak dan subpetak

Shell halaman: <main class="page-layout"> berisi <header>, <aside>, <section class="card-grid"> dengan <div class="card"> turunan, dan <footer>.

/* Align grid-template-areas in rows and columns for readability. */
.page-layout {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-areas:
    "header  header  header"
    "sidebar main    main"
    "footer  footer  footer";
  gap: 1.5rem;
}

header  { grid-area: header; }
aside   { grid-area: sidebar; }
footer  { grid-area: footer; }

.card-grid {
  grid-area: main;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  grid-template-rows: auto 1fr; /* title block, body block */
  gap: 1rem;
}

.card {
  grid-row: span 2;
  display: grid;
  /* Same-cascade fallback: ignored when subgrid is supported. */
  grid-template-rows: auto 1fr;
  grid-template-rows: subgrid;
}

4 Kueri container

Membuat kueri ukuran (atau gaya yang dihitung) penampung ancestor, bukan area pandang. Model mental: kueri penampung = konteks komponen; kueri media = tata letak halaman global dan preferensi pengguna (prefers-color-scheme, prefers-reduced-motion).

Lakukan:

  • Buat konteks penampungan dengan container-type: inline-size (kueri khusus lebar) atau container-type: size (kedua sumbu) pada wrapper sebelum turunannya dapat dikueri.
  • Beri nama penampung dengan container-name (atau singkatan container: container: card / inline-size) jika konteks bertingkat dapat bertabrakan.
  • Sertakan unit kueri penampung dalam menghitung jenis dan spasi fluid: cqi/cqb (inline/blok logis), cqw/cqh (fisik), cqmin/cqmax.
  • Berikan block-size yang pasti pada penampung setiap kali container-type: size digunakan — tanpa itu, turunan akan diciutkan karena pembatasan ukuran memaksa penampung mengabaikan kontennya.

Jangan:

  • Jangan gunakan block-size sebagai nilai container-type — nilai ini tidak valid. Gunakan size untuk kedua sumbu.
  • Jangan berharap ukuran intrinsik turunan akan memengaruhi penampung setelah mendeklarasikan container-type. Penampung dihitung seolah-olah tidak memiliki turunan setelah penampungan aktif.
  • Jangan mengandalkan unit kueri penampung di dalam turunan ancestor yang tidak memenuhi syarat; unit tersebut akan kembali ke area pandang kecil (svw/svh).

4.1 Contoh kode: tipografi fluid menggunakan unit kueri penampung

.card-wrapper {
  container: card / inline-size; /* shorthand for container-name / container-type */
}

@container card (inline-size > 400px) {
  .content {
    display: flex;
    gap: 2rem;
  }
}

.title {
  /* Fluid type bound to the container width, not the viewport. */
  font-size: clamp(1rem, 4cqi, 2rem);
}

Untuk pola gaya responsif berbasis komponen, lihat Penataan Gaya Berdasarkan Ukuran dan Penskalaan Fleksibel.

5 Overlay native, pemosisian anchor, dan konteks tumpukan

Kapan harus menggunakan setiap primitif overlay:

  • Gunakan popover untuk UI non-modal sementara (flyout, toast, tooltip) — berada di lapisan atas, tidak memerlukan pengelolaan z-index.
  • Gunakan <dialog> dengan .showModal() untuk interaksi modal yang memerlukan penguncian fokus dan latar belakang inert.
  • Jangan gabungkan popover dan .showModal() pada elemen yang sama — keduanya adalah status runtime yang sama-sama eksklusif.

Pemosisian anchor (tata letak spasial overlay):

  • Gunakan position-area (atau anchor() pada inset) dan anchor-size() untuk memosisikan dan mengukur overlay sesuai pemicunya.
  • Gunakan position-try-fallbacks: flip-block (atau flip-inline) untuk mengizinkan browser mengubah posisi saat overlay melebihi area pandang.
  • Jangan mencampur kata kunci fisik dan logis dalam satu nilai position-area — pilih satu sistem koordinat.
  • Deteksi fitur dengan @supports (anchor-name: --x) dan berikan penggantian posisi absolut.

Untuk mengetahui detail implementasi lengkap, strategi polyfill, dan referensi nilai popover, lihat Dialog Deklaratif dan Kontrol Popover serta Tooltip yang Memperhitungkan Posisi. Untuk pemosisian anchor yang diterapkan ke dropdown, flyout, dan indikator tab, lihat Dropdown yang Memperhatikan Area Tampilan dan Garis Bawah Tab Pemosisian Anchor.

6 Pelacakan overflow dan stabilitas tata letak

Kelola pergeseran tata letak, scrollbar, dan pemangkasan secara terprediksi.

Lakukan:

  • Gunakan overflow: auto agar scrollbar hanya muncul saat konten benar-benar meluap.
  • Gunakan overflow: clip untuk memotong konten tanpa membuat penampung scroll; pilih spillover dengan overflow-clip-margin.
  • Gunakan scrollbar-gutter: stable untuk mencadangkan ruang bagi scrollbar dan mencegah pergeseran tata letak saat konten bertambah.
  • Gunakan overscroll-behavior: contain (atau none) pada region scroll bertingkat (seperti sidebar, modal, dan daftar) untuk menghentikan rantai scroll agar tidak muncul ke halaman, atau overscroll-behavior: chain pada wrapper scroll perantara (seperti trek geser untuk menampilkan) saat scroll berlebih harus disebarkan ke scroller ancestor tanpa memicu pantulan rubber-band lokal.
  • Gunakan triad -webkit-line-clamp + display: -webkit-box + -webkit-box-orient: vertical untuk pemotongan multibaris — meskipun memiliki awalan, pola ini sepenuhnya ditentukan dan tidak digunakan lagi. Deklarasikan singkatan line-clamp tanpa awalan di sampingnya; browser yang belum mendukungnya akan mengabaikan properti tersebut tanpa menimbulkan masalah. Jangan:

  • Jangan gunakan overflow: scroll jika auto sudah cukup — scroll memaksa scrollbar muncul meskipun tidak ada yang perlu di-scroll.

  • Jangan gunakan overflow: hidden jika Anda hanya ingin memotong — hidden membuat penampung scroll yang dapat di-scroll secara terprogram.

.scrollable-list {
  max-block-size: 400px;
  overflow-y: auto;
  scrollbar-gutter: stable;       /* Reserve scrollbar space. */
  overscroll-behavior: contain;   /* No scroll chaining into the page. */
}

.snippet {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  line-clamp: 3;                  /* Ignored where unsupported. */
  overflow: clip;
}

Untuk mengetahui overflow: clip dan overflow-clip-margin secara mendalam, lihat Kontrol Pemangkasan Overflow. Untuk pengalihan scroll bertingkat dengan overscroll-behavior: chain di UI geser untuk menampilkan, lihat Geser untuk menampilkan. Untuk warna, ukuran, dan tema scrollbar, lihat Menyesuaikan warna atau ketebalan scrollbar, Mode gelap, dan Menyesuaikan scrollbar dengan preferensi kontras tinggi.

7 Mekanisme area pandang dan distribusi jalur

  • Gunakan dvh/dvw untuk penampung tata letak seluler yang harus memperhitungkan pergeseran UI browser (penciutan/perluasan kolom URL).
  • Jangan gunakan 100vw untuk tata letak lebar penuh — ini mengabaikan lebar scrollbar dan menyebabkan overflow horizontal. Sebagai gantinya, gunakan 100%, 100dvw, atau 100svw.

Untuk titik entri desain responsif (unit area pandang dinamis, menyediakan ruang untuk media, tipografi fluid), lihat Desain responsif.

8 Jalur petak (alias masonry)

Spesifikasi ini sedang dalam pengembangan dengan nama "grid lanes" (misalnya, display: grid-lanes), sedangkan prototipe eksperimental sebelumnya menggunakan grid-template-rows: masonry.

Lakukan:

  • Gunakan petak dengan grid-auto-flow: dense untuk pengemasan yang rapat saat ini, dengan menerima bahwa urutan DOM mungkin tidak cocok dengan urutan visual.
  • Gunakan multi-kolom (columns: 3; column-gap: 1rem) untuk alur seperti masonry yang padat konten jika itemnya adalah fragmen dokumen, bukan kartu dengan bobot yang sama.
  • Perlakukan grid-template-rows: masonry hanya sebagai progressive enhancement — deteksi fitur dengan @supports.

Jangan:

  • Jangan kirimkan grid-template-rows: masonry sebagai persyaratan ketat hingga target Dasar Pengukuran Anda tercapai.
.gallery       { columns: 3 200px; column-gap: 1rem; }
.gallery > *   { break-inside: avoid; margin-block-end: 1rem; }

@supports (grid-template-rows: masonry) {
  .gallery {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    grid-template-rows: masonry;
    gap: 1rem;
    columns: unset;
  }
}