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.
.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.
/* 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
- Jangan pakai angka raksasa. Pakai skala kecil: dropdown 10, sticky 20, modal 30, tooltip 40.
- Definisikan sebagai variabel CSS di satu tempat.
- Kalau z-index tidak mempan, curigai stacking context: cek apakah leluhur punya transform/opacity/filter.
:root {
--z-dropdown: 10;
--z-sticky: 20;
--z-modal: 30;
}Catatan teknis:
position: fixeddi 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:
: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
.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.
export default function App(): JSX.Element { return <h1>Hello world</h1> }
Tantangan
Selamatkan Anak 9999
Di playground, anak dengan z-index 9999 tetap tertutup induk B. Ini BUKAN bug.
- Jelaskan di komentar kenapa ini terjadi (stacking context).
- Perbaiki TANPA mengubah z-index anak: ubah z-index induk A menjadi 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>