cssz-indexpositionMenengah5 mnt baca

Z-Index dan Stacking

Mengatur tumpukan elemen, stacking context yang misterius, dan kenapa z-index: 9999 kadang gagal.

Dasar z-index

z-index mengatur urutan tumpukan elemen yang positioned (bukan static). Nilai lebih besar = lebih di depan.

css
.modal { position: fixed; z-index: 100; }
.dropdown { position: absolute; z-index: 50; }

Tanpa z-index, elemen yang muncul belakangan di HTML menumpuk di depan.

Stacking context: jebakan sesungguhnya

Ini alasan z-index: 9999 kadang "tidak mempan". Elemen dengan properti tertentu (misal position: relative + z-index, opacity < 1, transform, filter) membuat stacking context baru: dunia tumpukan tersendiri.

css
/* Kasus klasik yang membingungkan */
.induk-a { position: relative; z-index: 1; }
.induk-a .anak { position: absolute; z-index: 9999; }

.induk-b { position: relative; z-index: 2; }

Anak dengan z-index 9999 tetap di belakang induk-b! Karena induk-a (z-index 1) kalah dari induk-b (z-index 2), seluruh "dunia" induk-a termasuk anaknya ikut kalah. z-index anak hanya berlaku di dalam stacking context induknya.

Strategi z-index yang sehat

  1. Jangan pakai angka raksasa. Pakai skala kecil: dropdown 10, sticky 20, modal 30, tooltip 40.
  2. Definisikan sebagai variabel CSS di satu tempat.
  3. Kalau z-index tidak mempan, curigai stacking context: cek apakah leluhur punya transform/opacity/filter.
css
:root {
  --z-dropdown: 10;
  --z-sticky: 20;
  --z-modal: 30;
}

Catatan teknis: position: fixed di dalam elemen ber-transform akan mengacu ke elemen itu, bukan viewport! Ini efek samping stacking context yang paling mengejutkan. Hindari transform pada leluhur modal fixed.

Aturan main z-index yang sehat

Tetapkan skala di satu tempat dan patuhi:

css
:root {
  --z-dropdown: 10;
  --z-sticky: 20;
  --z-modal: 30;
  --z-toast: 40;
}

Kenapa skala kecil? Karena angka raksasa (9999) memberi ilusi aman tapi menyembunyikan masalah stacking context. Dengan skala kecil, konflik langsung terlihat dan dipikirkan.

Contoh: modal di atas dropdown

css
.dropdown {
  position: absolute;
  z-index: var(--z-dropdown);
}

.modal-overlay {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  background: rgba(0, 0, 0, 0.5);
}

Modal (30) selalu di atas dropdown (10). Jelas, terdokumentasi, mudah diubah.

Jebakan

z-index pada static tidak berpengaruh. Elemen harus positioned (relative/absolute/fixed/sticky) agar z-index bekerja.

Opacity/transform/filter membuat stacking context. Elemen dengan opacity: 0.99 atau transform: translateX(0) (bahkan yang "tidak ngapa-ngapain") menjadi dunia tumpukan sendiri. Animasi transform pada kartu bisa tiba-tiba mengubah urutan tumpuknya!

Tips profesional

  • Debug z-index dengan DevTools: hover elemen di panel Elements untuk melihat stacking context-nya.
  • Jangan pernah memakai angka di atas 100 tanpa variabel. Skala 10/20/30/40 cukup untuk 99% situs.
  • Modal dan toast sebaiknya di-render di akhir <body> agar terhindar dari stacking context tak terduga milik komponen lain.

Rangkuman

z-index hanya untuk elemen positioned. Pahami stacking context: z-index anak terkungkung di dunia induknya. Pakai skala kecil yang terpusat, bukan angka raksasa acak.

Live Playground
export default function App(): JSX.Element {
  return <h1>Hello world</h1>
}

Edit kode di kiri, preview kanan ter-update otomatis.

Tantangan

Selamatkan Anak 9999

Di playground, anak dengan z-index 9999 tetap tertutup induk B. Ini BUKAN bug.

  1. Jelaskan di komentar kenapa ini terjadi (stacking context).
  2. Perbaiki TANPA mengubah z-index anak: ubah z-index induk A menjadi 3.
  3. Refaktor lebih sehat: ganti semua z-index raksasa menjadi skala kecil (induk A: 1, induk B: 2, anak: 3) dan pastikan hasilnya sama.
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<h1>Halo CSS</h1>
<p>Edit styles.css dan lihat hasilnya.</p>
</body>
</html>