Dark mode NgodingSantai semula tampak seperti satu tombol yang mengganti warna. Masalah sebenarnya lebih luas: tema harus diterapkan sebelum stylesheet selesai dirender agar tidak berkedip, semua komponen harus memakai token yang sama, ikon harus menjelaskan aksi berikutnya, dan pilihan tetap bertahan ketika penyimpanan browser tersedia.

Batas implementasi
  • Dua pilihan eksplisit: terang dan gelap.
  • Preferensi sistem menjadi nilai awal ketika pengguna belum memilih.
  • localStorage dibungkus try/catch karena penyimpanan dapat dibatasi.
  • Kontrol berupa button nyata dengan label yang berubah.
Diagram alur pemeriksaan komponen antarmuka
Audit tema menggunakan pendekatan yang sama dengan audit responsive: temukan gejala pada komponen, perbaiki sumber tokennya, lalu uji seluruh matriks halaman.

Gunakan token semantik, bukan warna per komponen

Stylesheet mendefinisikan warna berdasarkan fungsi: latar halaman, surface, teks utama, teks sekunder, garis, aksen, status, dan bayangan. Komponen tidak perlu mengetahui hex tema. Hal ini memperbaiki bug lama ketika tombol atau tab kategori memiliki background gelap tetapi teksnya tetap memakai warna muted terang.

:root {
  --bg: #f8f9ff;
  --surface: #fff;
  --text: #182033;
  --muted: #5d6679;
  --line: #dfe3f0;
  --accent: #5b4df5;
}

[data-theme="dark"] {
  --bg: #0d1020;
  --surface: #15192b;
  --text: #f3f4ff;
  --muted: #aab2ca;
  --line: #2d3552;
  --accent: #8b80ff;
  color-scheme: dark;
}

Terapkan tema di head

theme.js dimuat sebelum stylesheet utama dan sebelum body. Script membaca pilihan tersimpan; jika tidak ada, ia membaca prefers-color-scheme. Atribut data-theme pada elemen HTML ditetapkan sedini mungkin sehingga browser tidak sempat menggambar tema terang lalu beralih gelap.

const stored = localStorage.getItem("ns-theme");
const preferred = matchMedia("(prefers-color-scheme: dark)").matches
  ? "dark" : "light";
document.documentElement.dataset.theme = stored || preferred;

Dalam source produksi, akses storage dibungkus penanganan error. Contoh ringkas di atas menunjukkan urutan keputusan, bukan seluruh proteksi.

Label tombol menjelaskan aksi

Jika tema saat ini gelap, label yang berguna adalah “Aktifkan mode terang”, bukan hanya “Tema” atau ikon bulan tanpa makna. JavaScript mengganti SVG dan aria-label setelah state berubah. Icon mempunyai aria-hidden karena nama aksesibel berasal dari tombol.

Jaga tombol primer tetap terbaca

Kelas umum .btn memakai surface dan text. Kelas .btn-primary kemudian menimpa background serta menetapkan putih secara eksplisit. Urutan ini mencegah aturan umum yang muncul belakangan mengubah warna teks tombol primer pada dark mode.

.btn { background: var(--surface); color: var(--text); }
.btn-primary,
.btn-primary:visited {
  background: linear-gradient(135deg, var(--action), var(--action-strong));
  color: #fff !important;
}

Audit semua state, bukan satu screenshot

Komponen diperiksa pada keadaan default, hover, focus-visible, active, disabled, dan aria-current. Navbar kategori sebelumnya tampak aktif pada halaman Tentang karena background aturan aktif tidak selaras dengan status sebenarnya. Script navigasi kini menentukan bagian aktif dari pathname dan membangun menu yang sama pada seluruh halaman.

KomponenLightDarkState tambahan
Button primaryTeks putihTeks putihhover, focus, disabled
Button ghostSurface terangSurface gelapvisited tidak berubah
DropdownSurface dan shadowSurface dan borderhover, keyboard
FormBackground situscolor-scheme darkerror, autofill, focus

Respons terhadap perubahan sistem

Ketika pengguna sudah memilih manual, perubahan preferensi sistem tidak mengambil alih pilihannya. Bila belum ada pilihan tersimpan, situs boleh mengikuti sistem. Tombol tetap menyediakan jalan keluar. Kebijakan ini perlu ditetapkan eksplisit agar event media query tidak menimpa keputusan pengguna.

Uji kegagalan storage dan JavaScript

Dalam mode privat tertentu atau lingkungan file lokal, storage bisa melempar error. Situs tetap memakai tema berdasarkan sistem dan tombol bekerja untuk sesi aktif. Tanpa JavaScript, atribut awal light membuat halaman tetap terbaca; fitur tema diperlakukan sebagai enhancement.

Checklist regresi

  • Tema dipilih sebelum body dirender dan tidak berkedip jelas.
  • Label tombol menyebut aksi yang akan terjadi.
  • Text, muted text, border, card, dialog, code, dan form terbaca di dua tema.
  • Focus-visible tidak hilang pada surface atau aksen.
  • Reload dan navigasi ke artikel mempertahankan pilihan.
  • Storage error tidak menghentikan script navigasi lain.

Referensi primer

Uji kontras tanpa mengandalkan persepsi satu monitor

Token teks dan background diperiksa dengan alat contrast ratio pada pasangan yang benar-benar muncul. Muted text pada surface, link aksen di callout, badge, placeholder form, serta focus ring diuji terpisah. Warna yang tampak nyaman di monitor desktop bisa kehilangan kontras pada ponsel dengan brightness rendah.

Kontrol native mengikuti color-scheme

color-scheme: dark membantu browser menggambar form, scrollbar, dan kontrol native sesuai tema. Namun input tetap mempunyai background, border, dan text token eksplisit agar konsisten. Autofill serta placeholder diperiksa karena keduanya dapat memakai warna dari user-agent stylesheet.

Hindari state yang tersebar

Atribut pada elemen HTML adalah state render utama. Storage hanya menyimpan preferensi untuk kunjungan berikutnya; ikon dan label diturunkan dari atribut yang aktif. Dengan satu sumber state, tombol tidak dapat menampilkan matahari sementara halaman sebenarnya masih memakai tema terang.

Periksa pasangan token, bukan hanya tampilan umum

Buka pemeriksa kontras dan gunakan preset terang (#182033 pada #ffffff), kemudian preset gelap (#f3f4ff pada #15192b). Keduanya adalah contoh pasangan token situs ini. Periksa juga teks muted, tombol, hover, fokus, dan latar kartu; satu pasangan lulus tidak mewakili semua komponen.

Uji rumus dengan hitam pada putih: hasilnya 21:1. Dua warna sama menghasilkan 1:1. Rasio yang ditampilkan dibulatkan, tetapi penentuan lulus memakai nilai tanpa pembulatan. Alat hanya menghitung warna opak; latar transparan perlu dikompositkan dahulu terhadap warna di belakangnya.

Catat hasil kedua tema pada checklist. Jangan mengubah tanggal publikasi asli ketika memperbarui palet; gunakan tanggal pembaruan untuk perubahan isi yang bermakna.

Tangkapan browser alat cek-kontras di Lab Frontend NgodingSantai
Tangkapan pengujian lokal Microsoft Edge, viewport 1280 × 900, tema terang, 26 September 2026. Ini contoh alat yang dapat dicoba, bukan bukti hasil produksi historis.

Artikel terkait