Konteks dan Pertanyaan
Sebuah halaman produk memiliki sticky header dan sebuah card yang berisi tombol menu. Menu tersebut diposisikan secara absolut dan diberi nilai stacking yang sangat besar. Ketika terbuka di dekat bagian atas halaman, header masih digambar (paint) di atasnya. Ketika ukurannya melebihi card, bagian bawahnya menghilang. Rekan tim mengusulkan untuk menambahkan satu angka nol lagi pada nilai stacking tersebut.
Jelaskan mengapa usulan tersebut tidak dapat memperbaiki kedua gejala tersebut. Diagnosis contoh di bawah ini, bedakan urutan paint dari clipping dan geometri penentuan posisi, serta pilih perbaikan yang tahan lama untuk tiga kasus: menu yang dapat tetap berada di dalam komponennya, overlay yang harus keluar dari komponen, dan UI yang semantiknya membutuhkan modal dialog atau popover.
<header class="site-header">Navigation</header>
<main class="page">
<section class="card">
<button type="button">Open menu</button>
<div class="menu">Menu items</div>
</section>
</main>.site-header {
position: sticky;
top: 0;
z-index: 4;
}
.page {
position: relative;
z-index: 1;
}
.card {
position: relative;
overflow: hidden;
transform: translateZ(0);
}
.menu {
position: absolute;
inset-block-start: 100%;
z-index: 999999;
}Asumsikan browser evergreen saat ini. Jawaban harus mempertahankan perilaku scroll, containment, dan aksesibilitas yang disengaja daripada sekadar menghapus CSS sampai tangkapan layar terlihat benar.
Apa yang Dinilai oleh Pewawancara
Sinyal pertama adalah apakah kandidat memahami batas perbandingan (comparison boundary). Stacking context digambar sebagai satu unit atomik dalam konteks induknya. Keturunan (descendants) dapat mengubah urutannya sendiri di dalam unit tersebut, tetapi tidak ada nilai stacking keturunan yang dapat menaikkan seluruh unit di atas sibling dari induknya. Di sini, nilai besar menu menang di dalam .page; .page secara keseluruhan masih berpartisipasi pada level 1, di bawah header pada level 4.
Sinyal kedua adalah klasifikasi. "Di belakang header" adalah masalah urutan paint (paint-order). "Terpotong di tepi card" adalah masalah clipping yang disebabkan oleh overflow: hidden. Kategori ketiga sering kali terlihat serupa: ancestor dengan transform membentuk containing block untuk keturunan yang berposisi absolute dan fixed, sehingga koordinat atau perilaku fixed mungkin relatif terhadap ancestor yang tidak terduga. Satu nilai stacking saja tidak memperbaiki clipping atau geometri.
Sinyal ketiga adalah pengetahuan tentang pembuat konteks (context creators) tanpa mengubah jawaban menjadi daftar hafalan semata. Penyebab umum meliputi elemen berposisi dengan nilai stacking non-auto, fixed atau sticky positioning, opacity di bawah satu, transforms, filters, perspective, clipping atau masking, blending, isolation: isolate, layout atau paint containment, container queries, dan flex atau grid children dengan nilai stacking non-auto. Keterampilan yang berguna adalah menemukan batas relevan pertama pada setiap rantai ancestor.
Sinyal keempat adalah penilaian perbaikan (repair judgment). Menghapus konteks yang tidak disengaja, mengubah level ancestor yang benar, me-reparenting overlay, dan menggunakan browser top layer menyelesaikan kontrak yang berbeda. Portal mengubah silsilah DOM dan dapat menghindari clipping komponen, tetapi tidak secara otomatis masuk ke top layer atau menambahkan perilaku modal, fokus, penutupan (dismissal), atau accessible name.
Sinyal terakhir adalah verifikasi. Pemeriksaan DevTools harus didukung oleh pengujian tumpang tindih (overlap) yang dapat diamati, scrolling, pemeriksaan resize dan zoom, penggunaan keyboard, serta koeksistensi dengan overlay lainnya. Perbaikan dianggap tidak lengkap jika hanya berhasil pada satu tangkapan layar tetapi merusak scroll container, menempel pada sistem koordinat yang salah, atau membiarkan menu biasa menutupi modal sungguhan.
Pertanyaan untuk Diklarifikasi Sebelum Menjawab
- Piksel mana yang salah? Apakah menu digambar di bawah elemen lain, terpotong pada batas tertentu, diposisikan pada koordinat yang salah, atau tertutup oleh elemen top-layer? Masalah-masalah ini memerlukan perbaikan yang berbeda.
- Ancestor mana yang membuat konteks? Periksa rantai menu dan rantai elemen yang menutupi. Perbandingan penentunya adalah tempat rantai tersebut pertama kali menjadi sibling, tidak selalu pada dua elemen yang terlihat langsung.
- Apakah clipping disengaja? Sebuah card mungkin memotong media dengan sudut membulat, dan scroll container mungkin memerlukan perilaku overflow. Menghapusnya secara global dapat memperbaiki menu tetapi merusak tata letak dan scrolling.
- Ke mana overlay harus di-anchor? Me-reparenting ke root overlay bersama berarti koordinatnya harus diukur relatif terhadap root tersebut dan diperbarui saat scroll, resize, zoom, dan perubahan layout terjadi.
- Apakah UI memerlukan semantik top-layer? Modal dialog harus memblokir interaksi di luar dan mengelola fokus. Menu ringan mungkin hanya memerlukan root overlay. Popover yang sesuai dapat menggunakan perilaku popover bawaan platform dan penempatan top-layer.
- Overlay mana saja yang dapat hidup berdampingan? Tentukan apakah menu, tooltip, navigasi sticky, drawer, dan modal dapat saling tumpang tindih. Sekumpulan kecil layer bernama lebih mudah dipahami daripada nilai acak enam digit.
- Kontrak browser dan komponen apa yang berlaku? Primitif platform saat ini mungkin lebih disukai; webview versi lama atau primitif design-system yang sudah ada dapat memengaruhi pilihan implementasi.
Kerangka Jawaban 30 Detik
"Saya akan mengklasifikasikan kegagalan sebelum mengubah nilai apa pun. 999999 menu hanya dibandingkan di dalam stacking context miliknya sendiri. Ancestor-nya .page berada pada level 1, sehingga seluruh subtree tetap berada di bawah sticky header pada level 4. Secara terpisah, overflow: hidden pada card memotong menu, dan transform-nya dapat mengubah containing block menu. Saya akan memeriksa rantai ancestor dan elemen teratas sebenarnya pada titik tumpang tindih. Jika menu dapat tetap lokal, saya akan menghapus konteks yang tidak disengaja atau menaikkan ancestor yang tepat dan menangani clipping secara lokal. Jika harus keluar, saya akan me-render-nya dalam root overlay bersama dan menghitung ulang geometri anchor. Jika interaksinya adalah modal atau popover yang sesuai, saya akan menggunakan browser top layer dan menerapkan kontrak fokus serta penutupan yang diperlukan. Kemudian saya akan menguji scroll, resize, zoom, interaksi keyboard, dan koeksistensi dengan overlay lainnya."
Pembahasan Mendalam Langkah demi Langkah
Mulailah dengan memisahkan keempat mekanisme. Semuanya dapat gagal bersamaan, tetapi tidak dapat saling menggantikan:
PAINT ORDER
Which painted box is above another?
Controlled by the stacking-context tree and paint order inside each context.
CLIPPING
Which pixels are allowed to appear?
Controlled by overflow, clip paths, masks, and paint containment.
GEOMETRY
Which box supplies the coordinate system?
A transform can establish the containing block for absolute and fixed descendants.
TOP LAYER
Is the element outside normal document stacking?
Modal dialogs and shown popovers can be placed in the browser-managed top layer.Bangun context tree untuk contoh kasus daripada membandingkan dua angka yang terlihat. Sticky header membuat konteks pada level 4. .page yang berposisi membuat sibling context pada level 1. Transform pada card membuat konteks lain di dalam .page. Nilai menu yang besar hanya mengurutkannya di dalam subtree tersebut. Secara konseptual, perbandingannya adalah header: 4 melawan page: 1; tidak pernah header: 4 melawan menu: 999999.
Selanjutnya, periksa clipping secara terpisah. Menu adalah keturunan dari .card, sehingga piksel di luar padding box card akan terpotong oleh hidden overflow. Stacking mengubah urutan paint; mereka tidak memperluas area clipping. Menaikkan .page, menghapus transform, atau mengubah nilai menu dapat menempatkannya di atas header tetapi tetap membuat bagian bawahnya terpotong.
Kemudian periksa containing block. Menu tersebut absolute, sehingga card yang berposisi sudah menyediakan koordinatnya. Transform bersifat redundan untuk menu khusus ini, tetapi menjadi penting jika implementasi di masa mendatang mengubah menu menjadi position: fixed: ancestor dengan transform dapat membuat elemen "fixed" tersebut berperilaku relatif terhadap ancestor alih-alih viewport. Inilah mengapa mengubah absolute menjadi fixed bukanlah jalan keluar yang dapat diandalkan.
Gunakan inspeksi browser untuk mengonfirmasi hipotesis daripada menebak-nebak. Periksa computed styles pada setiap ancestor hingga pembuat konteks pertama dan ancestor clipping ditemukan. Bandingkan rantai konteks menu dan header. Pada koordinat tempat keduanya bertumpukan, cari tahu apa yang dianggap paling atas oleh hit testing:
function inspectOverlap(x, y) {
const stack = document.elementsFromPoint(x, y)
return stack.map((element) => ({
element,
position: getComputedStyle(element).position,
zIndex: getComputedStyle(element).zIndex,
overflow: getComputedStyle(element).overflow,
transform: getComputedStyle(element).transform,
}))
}elementsFromPoint() memudahkan untuk melihat tumpukan hit-test; elementFromPoint() hanya mengembalikan elemen teratas yang memenuhi syarat. Hit testing adalah bukti pendukung, bukan debugger stacking-context yang lengkap: elemen dengan pointer-events: none mungkin dilewati, dan clipping atau geometri di luar layar tetap memerlukan inspeksi layout.
Pilih perbaikan terkecil yang sesuai dengan batas kepemilikan (ownership boundary).
Untuk menu lokal, hapus konteks yang tidak disengaja jika tidak memiliki efek visual atau containment yang diperlukan. Jika halaman memang benar-benar membutuhkan konteksnya, bandingkan dan ubah token tingkat halaman karena itulah sibling yang sedang diurutkan terhadap header. Jika clipping card hanya untuk media, tempatkan clipping tersebut pada pembungkus media bertingkat (nested media wrapper) alih-alih pada seluruh card interaktif.
:root {
--layer-content: 0;
--layer-sticky: 20;
--layer-overlay: 40;
}
.page {
position: relative;
z-index: var(--layer-content);
}
.site-header {
z-index: var(--layer-sticky);
}
.card-media {
overflow: hidden;
border-radius: 1rem;
}
.menu {
z-index: var(--layer-overlay);
}Ini hanya berfungsi jika kontrak yang diinginkan mengizinkan subtree halaman tetap berada di bawah header dan menu tidak perlu melintasi batas tersebut. Jika menu harus menutupi header, keputusan layer berada pada common ancestor atau overlay harus dipindahkan ke shared root.
Untuk reusable overlay yang harus keluar dari konteks lokal dan clipping, render di bawah shared overlay root di dekat document root. Pertahankan pemicu (trigger) sebagai pemilik logisnya, jaga hubungan aksesibilitas, dan hitung posisi panel dari ukuran rectangle pemicu. Hitung ulang saat scrolling, resizing, zooming, atau perubahan layout mengubah salah satu rectangle. Penanganan tabrakan (collision handling) harus memperhitungkan tepi viewport dan arah penulisan. Melakukan reparenting tanpa kepemilikan geometri akan menukar bug clipping dengan bug menu yang melayang tidak tentu arah (drifting).
Untuk modal dialog sungguhan, prioritaskan elemen dialog native yang dibuka secara modal atau primitif aksesibel yang sudah mapan yang menyediakan kontrak yang sama. Untuk interaksi non-modal yang sesuai, popover API dapat menempatkan konten yang ditampilkan di top layer. Elemen top-layer dirender di atas konteks dokumen biasa; elemen yang masuk belakangan berada di atas elemen sebelumnya, dan masing-masing memiliki root context sendiri. Overflow, opacity, mask, dan transform pada ancestor tidak akan menarik elemen top-layer kembali ke dalam konteks dokumen ancestor.
Top layer bukanlah "z-index yang lebih kuat" yang universal. Ini adalah layer teratur yang dikelola browser dengan mekanisme masuk dan keluar yang semantik. Modal tetap membutuhkan nama yang berguna, fokus awal yang disengaja, interaksi yang terisolasi, aturan penutupan, dan pemulihan fokus. Menu tetap membutuhkan semantik menu atau disclosure yang benar serta perilaku keyboard. Memilih layer tidak pernah menggantikan pemilihan model interaksi.
Validasi perbaikan menggunakan matriks pengujian:
- Buka di dekat setiap tepi viewport dan di dalam nested scroll containers.
- Scroll halaman dan kontainer terdekat; pastikan panel tetap menempel atau menutup sesuai kebijakan.
- Ubah ukuran, lakukan zoom hingga 200%, dan uji tata letak sempit maupun lebar.
- Navigasi menggunakan keyboard; pastikan urutan, fokus yang terlihat, tombol Escape, dan kembalinya fokus sesuai dengan komponen.
- Buka bersamaan saat navigasi sticky, drawer, tooltip, dan modal sungguhan sedang aktif.
- Periksa hit testing pada piksel yang tertutup dan pastikan tidak ada overlay tak terlihat yang mencegat kontrol yang tidak terkait.
- Uji label terjemahan yang panjang serta tata letak left-to-right dan right-to-left jika didukung.
- Pastikan overflow, containment, compositing, dan perilaku scroll yang disengaja tetap berfungsi.
Contoh Jawaban Berkualitas Tinggi
"Nilai yang besar tersebut dibandingkan pada level yang salah. Menu berada di dalam stacking context .page, yang berpartisipasi pada level 1. Header adalah sibling context pada level 4. Browser menggambar subtree halaman secara atomik di bawah header, sehingga nilai keturunan sebesar 999999 tidak dapat keluar dan bersaing dengan header.
Terdapat juga kegagalan clipping yang terpisah. Overflow: hidden pada card memotong piksel di luar batasnya; z-index mengubah urutan tetapi tidak dapat memperbesar area klip tersebut. Transform pada card membuat konteks lain dan dapat membentuk containing block untuk keturunan absolute dan fixed, sehingga beralih ke fixed positioning mungkin masih menggunakan sistem koordinat yang tidak diharapkan.
Saya akan memeriksa kedua rantai ancestor di DevTools, mengidentifikasi pembuat konteks dan pembuat clipping pertama, serta memeriksa tumpukan hit-test pada koordinat tumpang tindih. Untuk menu lokal, saya hanya akan menghapus konteks yang tidak disengaja, memindahkan clipping ke pembungkus media, atau menyesuaikan layer bernama pada common ancestor yang sebenarnya sedang dibandingkan. Saya tidak akan terus meningkatkan nilai elemen leaf.
Jika menu secara kontrak diizinkan untuk melintasi batas komponen dan header, saya akan me-render-nya dalam root overlay bersama. Artinya, sistem overlay bertanggung jawab atas pengukuran, penanganan tabrakan, dan pembaruan saat scroll, resize, zoom, dan perubahan layout. Portal membantu menangani silsilah DOM dan clipping, tetapi tidak secara otomatis menciptakan perilaku top-layer atau aksesibilitas.
Jika interaksinya adalah modal dialog, saya akan menggunakan perilaku native modal dialog atau primitif aksesibel yang teruji. Jika berupa popover yang sesuai, saya akan mempertimbangkan perilaku platform popover. Keduanya dapat menggunakan browser top layer, tetapi saya tetap akan menerapkan kontrak yang benar untuk accessible name, fokus, keyboard, penutupan, dan pemulihan fokus. Terakhir, saya akan memverifikasi scroll container, tepi viewport, zoom 200%, penggunaan keyboard, tata letak terjemahan, hit testing, dan koeksistensi dengan overlay lainnya."
Kesalahan Umum
- Meningkatkan nilai keturunan tanpa batas → elemen tetap terperangkap di dalam konteks induk yang lebih rendah → Bandingkan konteks sibling pertama dan ubah penentu keputusan layer tersebut.
- Menganggap setiap piksel yang tersembunyi sebagai bug stacking → overflow atau paint containment tetap memotongnya → Periksa ancestor clipping secara terpisah dari urutan paint.
- Mengubah positioning absolute ke fixed → ancestor dengan transform mungkin masih menentukan containing block → Lacak sistem koordinat sebelum memilih mode posisi.
- Menghapus setiap deklarasi transform atau overflow → media membulat, scrolling, containment, atau perilaku rendering menjadi rusak → Hapus hanya pemicu yang tidak disengaja atau batasi pemicu tersebut hanya pada elemen yang membutuhkannya.
- Melakukan portal setiap overlay ke body → masalah anchoring, tabrakan, kepemilikan, dan fokus menggantikan bug awal → Gunakan sistem overlay bersama dengan kontrak geometri dan interaksi yang eksplisit.
- Menganggap portal adalah top layer → konteks dokumen biasa masih dapat menutupinya → Gunakan primitif platform top-layer ketika semantiknya sesuai.
- Menggunakan modal dialog untuk setiap floating panel → fokus dan interaksi luar menjadi terblokir secara tidak perlu → Pilih modal, popover, menu, tooltip, atau inline disclosure berdasarkan perilakunya terlebih dahulu.
- Membuat nilai global yang arbitrer → komponen masuk ke dalam kompetisi peningkatan layer yang tidak terkontrol → Gunakan sekumpulan kecil layer bernama dan jaga nilai lokal tetap lokal.
- Hanya memverifikasi satu tangkapan layar → scroll, zoom, keyboard, atau overlay lain memunculkan kembali kegagalan tersebut → Uji matriks tumpang tindih dan interaksi.
Pertanyaan Lanjutan dan Jawabannya
Apakah z-index yang lebih besar selalu menempatkan elemen di atas elemen yang lebih kecil?
Hanya jika nilai-nilai tersebut berpartisipasi dalam perbandingan stacking context relevan yang sama. Keturunan diurutkan di dalam konteks mereka, dan seluruh konteks tersebut kemudian diurutkan di dalam induknya. Bandingkan rantai konteks, bukan nilai komputasi yang terisolasi.
Properti CSS apa yang umumnya membuat stacking context?
Root, fixed atau sticky positioning, elemen berposisi dengan nilai stacking non-auto, opacity di bawah satu, transforms, filters, perspective, clipping atau masking, blending, isolation, properti containment dan container-query tertentu, serta flex atau grid children dengan nilai stacking non-auto adalah penyebab umum. Daftar pastinya terus berkembang, jadi inspeksi harus mengidentifikasi pemicu terkomputasi daripada hanya mengandalkan ingatan.
Mengapa suatu elemen bisa berada di atas sibling-nya tetapi tetap terlihat terpotong?
Urutan paint menentukan piksel mana yang diizinkan untuk digambar terakhir. Area clipping menentukan piksel mana yang diizinkan untuk ada. Memenangkan urutan paint tidak dapat mengembalikan piksel yang telah dihapus oleh overflow, clip path, mask, atau paint containment milik ancestor.
Akankah position: fixed selalu keluar dari ancestor yang memiliki overflow-hidden?
Tidak. Fixed positioning mengubah perilaku containing-block normal, tetapi transform dan properti terkait pada ancestor dapat menetapkan containing block tersebut. Mekanisme clipping lainnya juga masih dapat berlaku. Telusuri geometri dan clipping daripada menggunakan fixed positioning sebagai solusi pelarian universal.
Apakah me-render melalui portal menyelesaikan seluruh masalah?
Ini dapat meloloskan elemen dari silsilah DOM lokal, konteks, dan clipping jika root target berada di luar elemen-elemen tersebut. Cara ini juga membutuhkan penanganan baru untuk pengukuran, pembaruan scroll, penanganan tabrakan, relasi event, fokus, dan penutupan. Elemen tersebut tetap berada dalam stacking dokumen biasa kecuali primitif itu sendiri masuk ke top layer.
Bagaimana perbedaan top layer dengan token layer global yang bernilai sangat tinggi?
Elemen top-layer digambar dalam layer teratur yang dikelola browser di atas konten dokumen biasa dan membuat root stacking context. Urutan masuk menentukan urutan di dalam layer tersebut. Token global yang tinggi masih berpartisipasi dalam context tree dokumen dan dapat tetap terperangkap oleh ancestor.
Haruskah sebuah tim melarang semua nilai z-index lokal?
Tidak. Nilai lokal berguna untuk mengurutkan bagian-bagian di dalam satu komponen, seperti badge di atas gambar. Gunakan skala bernama yang kecil hanya pada batas bersama seperti navigasi sticky, reusable overlay, dan drawer. Batas perbandingan, bukan besarnya angka, yang menentukan apakah suatu nilai bersifat global.
Bagaimana Anda mencegah regresi kelas masalah ini?
Dokumentasikan beberapa pemilik layer bersama, sentralisasikan penempatan reusable overlay, dan tambahkan skenario interaksi yang menggabungkan konten sticky, scroll container, menu, dan modal. Verifikasi penempatan di tepi layar, scroll, zoom, perilaku keyboard, dan overlay aktif lainnya; snapshot statis saja tidak dapat membuktikan kesesuaian kontrak.