Sebuah custom theme yang sedang saya kerjakan punya warna aksen yang bisa diatur lewat Customizer. Defaultnya harusnya gelap, #0e0e0e. Saya daftarkan settingnya rapi-rapi, render template, dan yang muncul di halaman justru putih bersih, #ffffff. Tidak ada warning, tidak ada error PHP, tidak ada apa-apa di log. Cuma warna yang salah, dan halaman yang tampak terbalik dari yang saya rancang.
Pemicunya satu baris di page template:
$accent = get_theme_mod( 'accent_color', '#ffffff' );
echo '<div style="border-color:' . esc_attr( $accent ) . '">';Dan registrasi setting-nya, yang menurut saya jadi sumber kebenaran default:
$wp_customize->add_setting( 'accent_color', array(
'default' => '#0e0e0e',
'sanitize_callback' => 'sanitize_hex_color',
) );Belum ada nilai yang pernah disimpan user di Customizer untuk setting ini. Logika saya: kalau belum disimpan, get_theme_mod akan mengambil default yang saya daftarkan, yaitu #0e0e0e. Argumen kedua '#ffffff' saya anggap cuma jaring pengaman ekstra yang tidak akan pernah kepakai. Saya bahkan iseng mengubah 'default' jadi nilai lain untuk membuktikan, dan hasilnya tetap #ffffff. Mengubah default terdaftar sama sekali tidak berpengaruh. Di titik itu saya tahu asumsi saya soal urutan presedennya yang salah, bukan kodenya.
Kenapa ini terjadi
get_theme_mod( $id, $default ) tidak sesederhana "ambil nilai tersimpan, kalau kosong pakai default terdaftar". Argumen kedua bukan fallback pasif. Di dalamnya, nilai yang dikembalikan mengalir lewat filter dinamis theme_mod_{$id} — dalam kasus saya theme_mod_accent_color — dan argumen kedua itulah yang dipakai sebagai nilai awal sebelum filter dijalankan ketika belum ada nilai tersimpan.
Hasilnya: ketika tidak ada nilai yang disimpan user, literal yang saya oper sebagai argumen kedua dipakai apa adanya. '#ffffff' yang saya kira jaring pengaman justru jadi pemenang. Ia mengalahkan 'default' => '#0e0e0e' yang saya daftarkan di add_setting.
Ini bagian yang menipu: argumen fallback get_theme_mod bukan "hanya dipakai kalau benar-benar unset dengan cara yang aman". Sebuah literal non-kosong yang dioper ke argumen kedua akan menang atas default setting kamu. Default terdaftar baru menang kalau argumen kedua dibiarkan kosong sehingga ia yang mengalir lewat filter. Selama saya mengisi argumen kedua dengan nilai non-kosong yang berbeda, default registrasi tidak akan pernah kelihatan — persis seperti yang saya alami. Dua "default" itu hidup di dua tempat berbeda, dan yang ada di pemanggilan template diam-diam menimpa yang ada di registrasi.
Perbaikannya
Perbaikan paling cepat: oper string kosong sebagai argumen kedua, supaya tidak ada literal non-kosong yang menimpa, dan default terdaftar yang mengalir.
$accent = get_theme_mod( 'accent_color', '' );Atau, kalau memang mau menulis literal di template, pastikan literal itu sama persis dengan default terdaftar:
// Harus identik dengan 'default' => '#0e0e0e' di add_setting().
$accent = get_theme_mod( 'accent_color', '#0e0e0e' );Dua cara ini berhasil, tapi keduanya rapuh. Yang pertama bergantung pada registrasi selalu ada dan benar. Yang kedua menyimpan nilai yang sama di dua tempat — sekali kamu ubah satu dan lupa yang lain, mereka mulai berbeda diam-diam, dan kamu balik lagi ke bug yang sama.
Solusi yang saya pakai: satu fungsi registry default yang dipakai oleh registrasi Customizer maupun fallback template, jadi keduanya mustahil melenceng.
function get_theme_defaults() {
return array(
'accent_color' => '#0e0e0e',
);
}
// Saat registrasi setting.
$defaults = get_theme_defaults();
$wp_customize->add_setting( 'accent_color', array(
'default' => $defaults['accent_color'],
'sanitize_callback' => 'sanitize_hex_color',
) );
// Di template.
$defaults = get_theme_defaults();
$accent = get_theme_mod( 'accent_color', $defaults['accent_color'] );Sekarang literal yang dioper ke get_theme_mod selalu sama dengan default terdaftar, karena keduanya membaca dari sumber yang sama. Mau argumen kedua yang menang atau default registrasi yang menang, nilainya identik, jadi tidak ada lagi yang bisa salah. Ganti #0e0e0e di satu tempat, dan registrasi maupun template ikut berubah bersamaan.
Pelajaran
Argumen kedua get_theme_mod bukan jaring pengaman yang lugu. Sebuah literal non-kosong menang atas default yang kamu daftarkan di setting ketika belum ada nilai tersimpan, karena ia mengalir lewat filter theme_mod_{$id}. Begitu kamu punya dua tempat yang mendeklarasikan "default" — registrasi setting dan pemanggilan template — keduanya akan melenceng cepat atau lambat, dan yang menimpa adalah yang ada di template. Simpan satu sumber kebenaran untuk default, dipakai bersama oleh registrasi dan fallback, dan masalah seperti ini tidak akan pernah punya celah untuk muncul.
