D
P
0

WordPress & PHP

Layout Shortcode di `display: grid` Meledak di Live Site? `wpautop` Menyelipkan `<p>` Kosong Jadi Grid Item Hantu

1 Juli 2026·4 menit baca
Layout Shortcode di `display: grid` Meledak di Live Site? `wpautop` Menyelipkan `<p>` Kosong Jadi Grid Item Hantu

Salah satu shortcode saya merender deretan kartu di dalam container display: grid: tiga kolom, gap rapi, satu kartu per sel. Di preview editor semuanya tampak benar. Begitu halamannya tayang di live site, grid-nya meledak. Beberapa kartu tiba-tiba menyempit jadi kolom kurus, ada gap kosong nyasar di tengah deretan, dan item-item wrapping ke posisi yang tidak masuk akal. Template sama, konten sama, hasil beda total.

Refleks pertama saya menyalahkan CSS. Saya cek grid-template-columns, cek media query, cek apakah ada stylesheet lain yang menimpa. Semuanya bersih. Baru ketika saya buka DevTools dan meng-inspect container grid-nya, pelakunya kelihatan. Di antara kartu-kartu saya bertebaran tag <p> kosong dan <br> liar:

<div class="card-grid">
  <p></p>
  <div class="card">...</div>
  <p><br /></p>
  <div class="card">...</div>
  <p></p>
  <div class="card">...</div>
</div>

Tag-tag itu tidak pernah saya tulis. Tidak ada di template, tidak ada di editor. Mereka muncul begitu saja di HTML final yang diterima browser.

Kenapa ini terjadi

Pelakunya wpautop, filter bawaan WordPress yang jalan di atas the_content. Tugasnya kedengaran jinak: mengubah baris kosong jadi paragraf <p>...</p> dan newline tunggal jadi <br />, supaya penulis tidak perlu mengetik tag paragraf manual. Untuk prosa itu berguna. Masalahnya, wpautop tidak bisa membedakan prosa dan markup. Dia memproses seluruh string konten, termasuk area output shortcode saya.

Template shortcode saya menghasilkan markup yang dipisah newline antar tag, kebiasaan normal supaya source-nya enak dibaca. Di mata wpautop, newline-newline itu terlihat seperti pergantian paragraf, jadi dia dengan patuh membungkus potongan-potongan itu dengan <p> dan menyelipkan <br /> di sela-selanya.

Di layout biasa, <p> kosong paling banter menambah margin aneh. Di dalam display: grid, akibatnya jauh lebih brutal: setiap child langsung dari grid container adalah grid item. Setiap <p> kosong hasil injeksi itu jadi grid item sungguhan, sel hantu yang menempati track dan menggeser semua kartu setelahnya. Itulah kenapa kartu menyempit dan wrapping-nya kacau: grid-nya menghitung anak-anak yang tidak pernah saya buat. Dan itu juga alasan preview editor terlihat aman, jalur render preview tidak melewati rantai filter the_content yang sama dengan halaman live.

Perbaikannya

Saya memperbaikinya di dua lapisan.

Lapisan pertama: berhenti memberi wpautop bahan untuk ditebak. Callback shortcode saya rombak supaya membangun markup sebagai satu string, tanpa newline nyasar antar tag:

function render_card_grid($atts, $content = null) {
    $cards = get_card_items();
 
    $html = '<div class="card-grid">';
    foreach ($cards as $card) {
        $html .= '<div class="card">';
        $html .= '<h3>' . esc_html($card['title']) . '</h3>';
        $html .= '<p>' . esc_html($card['excerpt']) . '</p>';
        $html .= '</div>';
    }
    $html .= '</div>';
 
    return $html;
}
add_shortcode('card_grid', 'render_card_grid');

Untuk varian shortcode yang membungkus konten dalam (enclosing), kontennya sudah keburu diproses sebelum sampai ke callback. Jadi artefaknya saya bersihkan dulu sebelum dipakai:

$content = shortcode_unautop($content);
$content = preg_replace('#</?p>|<br\s*/?>#', '', $content);

shortcode_unautop() membalikkan pembungkusan paragraf di sekitar tag shortcode, dan preg_replace menyapu sisa <p> dan <br> yang masih lolos. Satu catatan: strip seagresif ini hanya pantas untuk area yang memang murni markup layout.

Lapisan kedua kebalikannya. Di bagian yang memang butuh paragraf, misalnya teks excerpt di dalam kartu, saya tulis tag <p> eksplisit sendiri di template, seperti di contoh di atas. Kalau markup-nya sudah eksplisit dan rapat, wpautop tidak punya apa-apa lagi untuk ditebak.

Ada satu trik klasik lagi yang layak dipasang: urutan filter. Secara default wpautop jalan di prioritas 10 pada the_content, sedangkan do_shortcode jalan di 11. Artinya wpautop memproses konten sebelum shortcode dirender, saat tag shortcode dan newline di sekitarnya masih mentah. Geser dia ke belakang:

remove_filter('the_content', 'wpautop');
add_filter('the_content', 'wpautop', 12);

Dengan prioritas 12, wpautop baru jalan setelah do_shortcode selesai. Output shortcode yang sudah berupa markup block-level rapat dia biarkan lewat, dan dia hanya memformat prosa asli di sekitarnya. Setelah kedua lapisan ini masuk, grid kembali normal: tiga kolom rapi, tanpa sel hantu.

Pelajaran

Kalau layout grid atau flex punya anak misterius, jangan mulai dari CSS. Inspect DOM live dan cari <p> serta <br> hasil injeksi; jumlah grid item yang tidak sesuai hampir selalu berarti ada child yang tidak kamu buat. Ingat bahwa wpautop memperlakukan output shortcode sebagai prosa kecuali kamu mengontrol whitespace-nya, newline antar tag adalah undangan terbuka untuk paragraf hantu. Dan markup eksplisit selalu mengalahkan paragraphing implisit: di tempat yang butuh paragraf, tulis <p> sendiri. Sejak insiden ini, setiap kali sebuah layout menghitung lebih banyak anak daripada yang saya render, pertanyaan pertama saya bukan lagi soal CSS, tapi: siapa yang menyunting HTML saya di tengah jalan?