D
P
0
← Semua artikel Read in English

Layout, Overflow & Cascade CSS

Blok Gambar Jadi 528px di Kolom 335px Setelah Ganti Foto ke Landscape? `min-height` Bikin `aspect-ratio` Menghitung Lebar, Bukan Tinggi

· · 5 menit baca
Blok Gambar Jadi 528px di Kolom 335px Setelah Ganti Foto ke Landscape? `min-height` Bikin `aspect-ratio` Menghitung Lebar, Bukan Tinggi

Yang berubah hari itu cuma satu berkas gambar. Bukan CSS, bukan template tema, bukan build. Saya menukar satu foto di sebuah blok gambar dari orientasi portrait ke landscape karena arah art direction-nya memang minta begitu, dan halaman itu langsung terlihat salah: kotak gambarnya menggantung jauh keluar dari sel grid-nya, menabrak kolom di sebelahnya, lalu tepinya terpotong rapi di batas section.

Saya ukur kotaknya. Lebarnya 528px, di dalam kolom yang lebarnya 335px. Menggantung 193px keluar dari batas yang seharusnya mengikat dia. Aturan CSS yang mengatur blok itu sudah berdiri hampir setahun dan tidak pernah sekali pun bikin masalah.

Tapi bukan itu yang paling banyak memakan waktu saya. Sweep responsif yang selalu saya jalankan tiap kali menyentuh layout tetap hijau. document.documentElement.scrollWidth sama persis dengan clientWidth, halaman tidak bisa digeser ke samping satu piksel pun, dan laporannya menyebut nol overflow horizontal. Jadi saya duduk dengan tangkapan layar yang jelas-jelas rusak di satu sisi, dan alat ukur yang bilang semuanya beres di sisi lain.

Sweep-nya tidak salah, dia cuma menjawab pertanyaan yang lain

Salah satu leluhur blok itu memakai overflow-x: clip. Itu penjelasan lengkapnya. Kelebihan lebar diserap di situ sebelum dokumen sempat tumbuh, jadi scrollWidth memang tidak pernah bertambah dan halaman memang tidak akan pernah bisa di-scroll ke samping. Cacatnya tidak hilang, dia cuma jadi tak terlihat oleh satu-satunya metrik yang sedang saya percaya.

Ini bukan pertama kalinya saya kena di situs yang sama. Sebelumnya, sweep di lebar 320px melaporkan nol overflow selama banyak sesi berturut-turut, sampai saya iseng mengukur elemen terhadap kontainernya sendiri, bukan terhadap dokumen. Hasilnya lima nama mitra di kartu daftar kolaborasi meleset 24 sampai 59px keluar dari kartunya masing-masing, dan dua di antaranya sudah terpotong di tengah kata oleh overflow-x: clip milik section-nya. Pelajarannya sama persis, dan saya jelas belum benar-benar menelannya: metrik yang hijau hanya bercerita tentang sepasang kotak yang dia bandingkan, tidak lebih.

Overflow dokumen dan overflow elemen itu dua pemeriksaan yang berbeda. Kalau kamu cuma menjalankan yang pertama, setiap leluhur dengan overflow-x: clip atau hidden adalah titik buta yang kamu setujui sendiri.

Pelakunya: min-height yang menempel di elemen ber-aspect-ratio

Setelah percaya pada mata dan berhenti percaya pada sweep, saya buka computed styles blok itu. Isinya dua deklarasi yang kelihatannya tidak berhubungan:

.blok-gambar {
  aspect-ratio: var(--rasio-berkas);
  min-height: 320px;
}

Keduanya masuk akal sendiri-sendiri. aspect-ratio memesan kotak sesuai rasio berkasnya, min-height dulu ditambahkan supaya placeholder tidak kolaps jadi setipis garis sebelum gambarnya termuat. Digabung, keduanya jadi sesuatu yang lain.

aspect-ratio menghitung dimensi yang tidak definit dari dimensi yang definit. min-height memberi kotak itu tinggi yang definit. Jadi arah hitungnya berbalik: yang dihitung dari rasio bukan lagi tinggi dari lebar kolom, melainkan lebar dari tinggi 320px itu. Sejak saat itu min-height berhenti jadi aturan tinggi dan berubah jadi deklarasi lebar yang menyamar.

Dimensi mana yang akhirnya mengikat sepenuhnya ditentukan oleh rasio berkas fotonya:

Angka 528 bukan kebetulan dan bukan hasil layout yang bingung. Itu hasil perkalian yang benar dari aturan yang salah. CSS-nya bekerja persis seperti yang ditulis, cuma yang ditulis bukan yang dimaksud.

Perbaikannya: hapus, bukan disetel ulang

Godaan pertamanya jelas, turunkan saja min-height sampai kasus landscape itu muat lagi. Saya tidak melakukannya, karena itu cuma menggeser ambangnya. Selama tinggi tetap definit, lebar tetap dihitung dari tinggi, dan berkas berikutnya yang rasionya sedikit lebih lebar akan meledak lagi dengan cara yang persis sama. Yang perlu dicabut bukan angkanya, tapi arah hitungnya.

Jadi min-height-nya saya hapus. Alasan aslinya sudah tidak berlaku: aspect-ratio sendiri sudah memesan kotak, jadi placeholder tidak akan kolaps walau gambarnya belum termuat. Begitu rasionya ada, min-height yang dulu dipasang sebagai penahan kolaps hampir selalu jadi mubazir.

Aturan praktis yang saya pakai sekarang: kalau sebuah elemen punya aspect-ratio, perlakukan min-height atau height apa pun yang menempel padanya sebagai deklarasi lebar. Baca ulang tiap kali arah art direction berganti orientasi, karena orientasi berkas adalah input yang menentukan sisi mana yang mengikat.

Verifikasi yang benar: bandingkan elemen dengan kontainernya

Karena sweep dokumen sudah terbukti buta untuk kasus ini, verifikasinya saya ganti. Untuk tiap kandidat, ukur selisih tepi kanannya terhadap kontainernya sendiri:

function selisihKanan(el, kontainer) {
  return Math.round(
    el.getBoundingClientRect().right - kontainer.getBoundingClientRect().right
  );
}

Apa pun yang di atas nol berarti elemen itu keluar dari kotak yang seharusnya mengikatnya. Kalau di atasnya ada leluhur yang meng-clip, angka itu tidak akan pernah muncul sebagai scrollbar, dia akan muncul sebagai konten yang terpotong.

Leluhur yang meng-clip juga layak dicari lebih dulu, karena di situlah pemeriksaan dokumen kehilangan penglihatannya:

let n = el.parentElement;
while (n) {
  const ox = getComputedStyle(n).overflowX;
  if (ox === 'clip' || ox === 'hidden') console.log(n, ox);
  n = n.parentElement;
}

Satu catatan yang membuat saya lama tidak menemukan cacat ini: gambar tidak punya teks. Sweep yang menyusuri simpul teks untuk mencari baris yang terpotong akan melewati blok gambar sepenuhnya, sebersih apa pun laporannya nanti terbaca.

Aturan yang tidur setahun bukan aturan yang benar

min-height: 320px itu salah sejak hari pertama dia ditulis. Dia cuma belum pernah bertemu berkas yang memicunya. Selama setahun setiap foto yang masuk kebetulan portrait, dan setiap kali itu aturannya menghasilkan lebar yang lebih kecil dari kolom, jadi tidak ada bukti apa pun bahwa dia bermasalah. Kode yang tenang bukan kode yang sudah diverifikasi, kadang dia cuma kode yang belum ketemu datanya.

Yang saya bawa dari kasus ini ada dua, dan keduanya soal apa yang kita anggap sudah diperiksa. Pertama, aspect-ratio bekerja dua arah, jadi tiap deklarasi tinggi yang menempel padanya diam-diam adalah deklarasi lebar. Kedua, satu sweep hijau tidak menutup seluruh kelas cacat, dan overflow-x: clip di mana pun di rantai leluhur adalah izin resmi untuk gagal tanpa suara. Setelah dua kali kena di situs yang sama, saya berhenti menanyakan apakah halamannya bisa di-scroll ke samping, dan mulai menanyakan apakah tiap kotak masih muat di dalam kotak induknya.