Dark Mode
Sistem dark theme project ini: class-based (.dark), token-driven, dengan deteksi system preference. Demo interaktif, strategi implementasi, dan panduan per-komponen.
Live Preview
Toggle untuk melihat transisi light ↔ dark dalam konteks terisolasi.
Implementasi
Project ini menggunakan class-based dark mode — class .dark
ditambahkan ke <html>, bukan @media (prefers-color-scheme).
Class pada <html>
Tambah atau hapus class .dark di elemen <html>.
Seluruh tree CSS merespons secara otomatis.
// tambah dark mode
document.documentElement.classList.add('dark');
// hapus dark mode
document.documentElement.classList.remove('dark');
// toggle
document.documentElement.classList.toggle('dark');
Deteksi system preference
Baca preferensi OS pengguna via matchMedia.
Gunakan ini sebagai default sebelum user pernah memilih manual.
const mq = window.matchMedia('(prefers-color-scheme: dark)');
// cek saat ini
if (mq.matches) { ... }
// listen perubahan real-time (user ganti OS theme)
mq.addEventListener('change', e => {
if (e.matches) document.documentElement.classList.add('dark');
else document.documentElement.classList.remove('dark');
});
Persist ke localStorage
Simpan pilihan user agar tidak kembali ke default saat halaman di-refresh. Baca dari localStorage sebelum paint pertama untuk menghindari flash.
// Letakkan di <head>, sebelum CSS lain, inline —
// supaya tidak ada FOUC (flash of unstyled content)
const saved = localStorage.getItem('theme');
const prefersDark = window.matchMedia(
'(prefers-color-scheme: dark)'
).matches;
if (saved === 'dark' || (!saved && prefersDark)) {
document.documentElement.classList.add('dark');
}
CSS: override dengan .dark
Pattern di base.css dan _base.css project ini.
Semua token dark sudah tersedia di token.css.
/* light (default) */
.my-card {
background: var(--color-bg-b1);
color: var(--color-primary);
}
/* dark override */
.dark .my-card {
background: var(--color-bg-d3);
color: var(--color-primary-dark);
}
Token Swap Reference
Pasangan token light ↔ dark yang paling sering digunakan. Semua sudah terdefinisi di token.css.
Hint: pasangan background gelap mengikuti pola d2→d3→d4 untuk page→surface→border, bukan d1 (pure black) karena terlalu kontras.
Alpine.js: Three-Mode Toggle
Snippet siap pakai untuk toggle System / Light / Dark dengan Alpine + localStorage. Ini pola yang digunakan demo di atas.
// Alpine.data() — daftarkan di alpine:init
Alpine.data('themeToggle', () => ({
mode: localStorage.getItem('theme') || 'system',
systemPrefersDark: window.matchMedia(
'(prefers-color-scheme: dark)'
).matches,
get isDark() {
return this.mode === 'dark' ||
(this.mode === 'system' && this.systemPrefersDark);
},
setMode(val) {
this.mode = val;
if (val === 'system') {
localStorage.removeItem('theme');
} else {
localStorage.setItem('theme', val);
}
document.documentElement.classList.toggle('dark', this.isDark);
},
init() {
// apply on load
document.documentElement.classList.toggle('dark', this.isDark);
// react to OS theme change
window.matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', e => {
this.systemPrefersDark = e.matches;
if (this.mode === 'system') {
document.documentElement.classList.toggle(
'dark', e.matches
);
}
});
},
}));