Dark Mode Done Right: Panduan Implementasi yang Konsisten

Dark mode sering dianggap fitur sepele: "tinggal balik warna hitam-putih, kan?" Kenyataannya jauh dari sederhana. Dark mode yang buruk justru membuat mata lebih cepat lelah, teks jadi sulit dibaca karena efek "halation" (silau) pada teks terang di atas latar sangat gelap, ikon dan ilustrasi terlihat aneh karena warnanya tidak disesuaikan, dan shadow yang dirancang untuk latar terang jadi tidak terlihat sama sekali di latar gelap. Survei pengguna aplikasi mobile secara konsisten menunjukkan permintaan dark mode ada di jajaran teratas fitur yang diminta, bukan cuma karena tren estetika, tapi karena manfaat nyata: mengurangi kelelahan mata di lingkungan minim cahaya, menghemat baterai signifikan pada layar OLED, dan membantu pengguna dengan sensitivitas cahaya tertentu. Artikel ini membahas cara mengimplementasikan dark mode yang benar-benar baik — bukan sekadar invert warna — dari sisi arsitektur CSS, sistem warna, hingga aksesibilitas.
Kenapa Dark Mode Bukan Sekadar "Invert Warna"
Kesalahan paling umum adalah menganggap dark mode cukup dibuat dengan filter invert() atau sekadar menukar putih menjadi hitam dan hitam menjadi putih. Pendekatan naif ini menimbulkan beberapa masalah nyata:
- Kontras berlebihan menyebabkan halation. Teks putih murni (
#FFFFFF) di atas latar hitam murni (#000000) menciptakan kontras yang secara teknis "sempurna" menurut rasio WCAG, tapi secara persepsi visual justru melelahkan mata karena efek silau/blur optik, terutama bagi orang dengan astigmatisme. - Warna brand tidak selalu bekerja di kedua mode. Warna biru cerah yang kontras baik di latar putih bisa jadi terlalu redup atau justru terlalu menyala (neon) di latar gelap.
- Shadow kehilangan fungsinya.
box-shadowhitam transparan yang dirancang untuk menciptakan ilusi elevasi di latar terang nyaris tak terlihat di atas latar yang sudah gelap. - Gambar dan ilustrasi bisa "berbenturan" dengan latar. Foto produk dengan latar putih akan terlihat seperti kotak terang yang mengganggu di tengah antarmuka gelap.
Dark mode yang baik adalah desain ulang sistem warna secara sadar, bukan transformasi matematis satu arah dari palet terang yang sudah ada.
Strategi Implementasi Teknis
Ada tiga pendekatan utama untuk mendeteksi dan menerapkan tema di CSS modern, dan pendekatan terbaik biasanya mengombinasikan ketiganya.
1. Mengikuti Preferensi Sistem Operasi
Media query prefers-color-scheme membaca pengaturan tema di level OS pengguna (Windows, macOS, iOS, Android semuanya mendukung ini):
:root {
--bg-primary: #FFFFFF;
--text-primary: #111827;
}
@media (prefers-color-scheme: dark) {
:root {
--bg-primary: #121212;
--text-primary: #E5E7EB;
}
}
Pendekatan ini bagus sebagai default, tapi tidak memberi pengguna kontrol eksplisit untuk override — sebagian pengguna ingin aplikasi tetap terang meski OS mereka disetel gelap, atau sebaliknya.
2. Toggle Manual dengan Atribut Data
Untuk memberi kontrol eksplisit, pola yang paling fleksibel adalah menggunakan atribut data-theme pada elemen root, dikombinasikan dengan prefers-color-scheme sebagai fallback ketika pengguna belum memilih apa pun:
:root {
--bg-primary: #FFFFFF;
--text-primary: #111827;
--surface-1: #F9FAFB;
--border-color: #E5E7EB;
}
/* Ikuti sistem jika user belum memilih manual */
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--bg-primary: #121212;
--text-primary: #E5E7EB;
--surface-1: #1E1E1E;
--border-color: #2E2E2E;
}
}
/* Override eksplisit ketika user memilih dark secara manual */
:root[data-theme="dark"] {
--bg-primary: #121212;
--text-primary: #E5E7EB;
--surface-1: #1E1E1E;
--border-color: #2E2E2E;
}
body {
background-color: var(--bg-primary);
color: var(--text-primary);
}
Pola :root:not([data-theme="light"]) di dalam media query ini penting: ia memastikan preferensi sistem hanya berlaku ketika pengguna belum memilih tema secara eksplisit. Begitu pengguna klik toggle dan memilih "light" secara manual, atribut data-theme="light" akan mengalahkan preferensi OS.
3. Menyimpan Pilihan Pengguna
Pilihan tema manual sebaiknya disimpan di localStorage agar persisten antar sesi, dan diterapkan sesegera mungkin (idealnya lewat inline script kecil di <head>) untuk menghindari "flash of wrong theme" saat halaman pertama kali dimuat:
<script>
(function () {
var saved = localStorage.getItem('theme');
var theme = saved || 'system';
if (theme !== 'system') {
document.documentElement.setAttribute('data-theme', theme);
}
})();
</script>
<script>
function setTheme(theme) {
if (theme === 'system') {
document.documentElement.removeAttribute('data-theme');
localStorage.removeItem('theme');
} else {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
}
}
</script>
Script ini sengaja diletakkan sebagai inline script yang dijalankan sinkron sebelum konten body dirender, bukan lewat file JavaScript eksternal yang dimuat belakangan — inilah kunci untuk menghindari kedipan tema yang salah (flash of unstyled/wrong theme) sepersekian detik saat halaman dibuka.
Sistem Warna untuk Dark Mode
Jangan Gunakan Hitam Murni
Alih-alih #000000, gunakan abu-abu sangat gelap seperti #121212 (rekomendasi Material Design) sebagai warna latar dasar. Ini mengurangi efek halation dan membuat elemen dengan shadow/elevation lebih mudah dibedakan dari latar.
Elevation Lewat Kecerahan, Bukan Hanya Shadow
Di latar terang, elevasi (rasa "mengambang") sebuah kartu biasanya dikomunikasikan lewat box-shadow. Di latar gelap, shadow kurang efektif karena kontrasnya rendah. Solusinya: permukaan yang "lebih tinggi" (misalnya modal, dropdown, kartu) diberi warna latar yang sedikit lebih terang dibanding latar dasarnya. Semakin tinggi elevasinya, semakin terang permukaannya:
--dark-bg-base: #121212; /* elevation 0 */
--dark-surface-1: #1E1E1E; /* elevation 1 - kartu */
--dark-surface-2: #232323; /* elevation 2 - dropdown */
--dark-surface-3: #2C2C2C; /* elevation 3 - modal */
Turunkan Saturasi Warna Aksen
Warna solid dengan saturasi tinggi (misalnya merah #FF0000 atau biru #0000FF) cenderung terlihat "menyala" berlebihan (vibrating) di atas latar gelap karena kontras luminance-nya sangat tinggi. Praktik umum adalah menaikkan lightness dan sedikit menurunkan saturasi warna aksen untuk mode gelap:
| Token | Light Mode | Dark Mode |
|---|---|---|
| color-primary | #2563EB | #5B8DEF |
| color-danger | #DC2626 | #F87171 |
| color-success | #16A34A | #4ADE80 |
Aksesibilitas Kontras di Dark Mode
Standar WCAG 2.1 level AA mensyaratkan rasio kontras minimum 4.5:1 untuk teks normal dan 3:1 untuk teks besar (di atas 18pt atau 14pt bold), berlaku sama di light maupun dark mode. Yang sering terlewat adalah bahwa kontras berlebihan (misalnya di atas 15:1 dengan teks putih murni di latar hitam murni) juga bukan ideal secara persepsi kenyamanan baca, meski secara teknis lolos audit otomatis. Rekomendasi praktis:
- Gunakan teks abu-abu terang (misalnya
#E5E7EB) alih-alih putih murni untuk teks body di dark mode. - Selalu uji rasio kontras dengan tools seperti WebAIM Contrast Checker atau axe DevTools, jangan mengandalkan penilaian visual semata.
- Perhatikan kontras pada state disabled dan placeholder — elemen ini sering terlewat dari audit karena dianggap "memang harus redup", padahal tetap perlu memenuhi rasio minimum 3:1 untuk elemen non-teks seperti border input.
Gambar, Ikon, dan Shadow di Dark Mode
Ikon
Gunakan ikon berbasis SVG dengan currentColor alih-alih warna hardcoded, sehingga ikon otomatis mengikuti warna teks di sekitarnya tanpa perlu file ikon terpisah untuk tiap tema:
.icon {
fill: currentColor;
color: var(--text-primary);
}
Gambar dan Foto
Foto produk dengan latar putih solid sebaiknya diberi border tipis atau sedikit padding dengan latar netral saat ditampilkan di dark mode, agar tidak menciptakan kotak terang yang mengganggu. Untuk ilustrasi vektor/SVG, sediakan varian warna khusus dark mode jika ilustrasi tersebut mengandalkan warna terang sebagai elemen dominan.
Shadow
Alih-alih box-shadow hitam transparan yang nyaris tak terlihat, gunakan kombinasi border tipis semi-transparan dan shadow yang justru menggunakan warna gelap pekat dengan opacity lebih tinggi, atau andalkan pendekatan elevation-by-lightness yang sudah dibahas sebelumnya sebagai penanda hierarki visual utama.
Menangani Media: Gambar, Video, dan Diagram di Dark Mode
Konten media sering jadi titik lemah implementasi dark mode yang terburu-buru. Beberapa strategi praktis yang layak dipertimbangkan tergantung jenis kontennya:
Meredupkan Gambar Secara Halus
Untuk foto dengan latar terang yang tidak bisa diganti asetnya (misalnya foto produk e-commerce), teknik umum adalah menurunkan sedikit brightness dan menambah sedikit kontras agar tidak terasa terlalu menyala dibanding elemen UI di sekitarnya, tanpa mengubah warna aslinya secara signifikan:
[data-theme="dark"] img:not(.no-dim) {
filter: brightness(0.9) contrast(1.05);
}
Teknik ini sebaiknya dipakai secukupnya dan tidak untuk semua gambar — foto yang memang jadi fokus utama (misalnya foto profil besar di halaman detail) sebaiknya dikecualikan lewat class seperti .no-dim di atas, agar tidak terlihat kusam tanpa alasan jelas.
Diagram, Grafik, dan Ilustrasi Teknis
Diagram berbasis SVG dengan warna teks hitam solid akan tidak terbaca di dark mode jika latar SVG-nya transparan. Solusi paling robust adalah membuat diagram memakai currentColor atau CSS custom properties untuk warna teks dan garis, sehingga otomatis mengikuti tema — sama seperti pendekatan ikon yang dibahas sebelumnya. Untuk grafik data (chart) yang dirender lewat library seperti Chart.js atau D3, palet warna kategori juga perlu disesuaikan: warna-warna pastel yang kontras baik di latar putih sering kali "hilang" ketenggelam di latar gelap, sehingga dibutuhkan palet kategori terpisah yang sudah diuji kontrasnya khusus untuk dark mode.
Video dan Embed Pihak Ketiga
Player video (YouTube embed, dsb) biasanya sudah memiliki tema gelapnya sendiri dan sulit dikustomisasi penuh dari luar. Praktik yang aman adalah membungkus embed tersebut dengan latar gelap netral dan sedikit padding, alih-alih membiarkan kotak putih pemutar video "meledak" kontras di tengah halaman gelap.
Studi Kasus dari Implementasi Nyata
Mempelajari bagaimana produk-produk besar menyelesaikan tantangan dark mode memberi banyak insight praktis:
- GitHub menyediakan bukan hanya dua, tapi beberapa varian tema (light, dark, dark dimmed, dark high contrast) — mengakui bahwa "gelap" bagi satu pengguna belum tentu ideal bagi pengguna lain yang butuh kontras lebih tinggi karena gangguan penglihatan. Sistem token warna mereka bahkan diterbitkan sebagai package open-source terpisah (Primer) yang bisa dipelajari langsung strukturnya.
- YouTube menerapkan dark mode dengan sangat konsisten hingga ke pemutar video itu sendiri, termasuk area letterbox di sekitar video yang otomatis menyesuaikan supaya tidak ada kontras tajam antara video dan UI di sekitarnya saat menonton di ruangan gelap.
- Twitter/X menyediakan tiga opsi: default (biru gelap), dim (abu-abu gelap sedang), dan lights out (hitam pekat murni) — mengakomodasi preferensi berbeda antara pengguna yang menginginkan kontras maksimal untuk hemat baterai OLED versus yang menginginkan kenyamanan mata dengan kontras lebih lembut.
- Aplikasi produktivitas seperti Notion dan Linear dikenal dengan transisi tema yang sangat mulus (smooth), memakai transisi CSS pada properti warna saat toggle diklik, sehingga pergantian tema tidak terasa seperti "flash" mendadak, melainkan seperti lampu yang diredupkan secara halus.
Pola umum dari studi kasus ini: produk-produk matang jarang berhenti di "satu dark mode untuk semua orang" — mereka mengenali bahwa preferensi kontras dan kecerahan itu personal, dan menyediakan variasi dalam batas yang tetap terkelola oleh design system yang konsisten.
Testing Dark Mode Secara Menyeluruh
Beberapa langkah pengujian yang sering terlewat tim yang terburu-buru merilis fitur dark mode:
- Uji transisi antar tema secara langsung, bukan cuma reload halaman dengan tema berbeda — pastikan tidak ada elemen yang "tertinggal" warna lama saat toggle diklik.
- Uji semua state komponen — hover, focus, active, disabled, error — di kedua tema, bukan cuma tampilan default.
- Uji dengan screen reader dan keyboard navigation untuk memastikan indikator fokus tetap terlihat jelas di dark mode (outline fokus yang biru gelap misalnya bisa nyaris tak terlihat di latar gelap).
- Uji pada perangkat OLED asli, bukan cuma simulator, karena reproduksi warna hitam pekat di layar OLED berbeda signifikan dari layar LCD.
- Uji kombinasi dengan mode kontras tinggi OS (misalnya Windows High Contrast Mode) untuk memastikan tidak ada konflik antara custom dark mode dan pengaturan aksesibilitas sistem.
Performa dan SEO: Pertimbangan yang Sering Terlewat
Selain aspek visual, implementasi dark mode juga punya implikasi teknis pada performa dan pengalaman multi-tab yang jarang dibahas:
- Hindari mengganti tema lewat penggantian file CSS terpisah (misalnya
dark.cssvslight.cssyang dimuat bergantian lewat JavaScript) karena ini memicu flash konten tanpa gaya (FOUC) saat file baru dimuat. Pendekatan CSS custom properties dengan satu stylesheet jauh lebih andal karena tidak ada request jaringan tambahan saat berganti tema. - Sinkronkan preferensi tema antar tab/window terbuka memakai event
storagepadalocalStorage, agar pengguna yang mengubah tema di satu tab tidak mendapati tab lain masih memakai tema lama sampai di-refresh manual:
window.addEventListener('storage', (event) => {
if (event.key === 'theme') {
applyTheme(event.newValue || 'system');
}
});
- Perbarui meta tag
theme-colorsecara dinamis agar UI chrome browser mobile (status bar, address bar) ikut menyesuaikan warna latar aplikasi, bukan tetap putih meski konten halaman sudah gelap:
<meta name="theme-color" content="#FFFFFF" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#121212" media="(prefers-color-scheme: dark)">
- Pertimbangkan dampak pada Core Web Vitals. Skrip inline penentu tema di
<head>yang dibahas sebelumnya sengaja dibuat sangat kecil dan sinkron karena ia render-blocking secara sengaja — trade-off yang diterima demi menghindari flash tema salah, tapi pastikan skripnya benar-benar minimal (hanya bacalocalStoragedan set atribut) agar tidak menambah Time to First Byte secara signifikan.
Kesalahan Umum dalam Implementasi Dark Mode
- Hardcode warna di komponen alih-alih memakai token/variabel. Ini membuat sebagian komponen "lupa" berganti tema saat toggle diklik.
- Tidak menyediakan opsi "ikuti sistem". Sebagian pengguna mengatur perangkat mereka otomatis berganti tema sesuai waktu (misalnya gelap di malam hari) dan mengharapkan aplikasi mengikuti tanpa perlu setting terpisah.
- Lupa menyesuaikan warna meta theme-color di mobile browser, sehingga status bar browser tetap terang meski konten halaman sudah gelap.
- Flash tema yang salah saat halaman dimuat karena skrip penentu tema dijalankan terlalu telat (setelah body dirender).
- Menerapkan dark mode hanya di permukaan (surface level) tapi lupa elemen sekunder seperti scrollbar custom, placeholder input, atau warna selection text.
Kesimpulan
Dark mode yang baik adalah hasil desain sistem warna yang disengaja, bukan transformasi otomatis dari palet terang yang sudah ada. Ia membutuhkan pertimbangan matang soal elevation, saturasi warna aksen, kontras yang nyaman (bukan sekadar lolos audit), serta penanganan teknis yang rapi lewat CSS custom properties, prefers-color-scheme, dan penyimpanan preferensi pengguna yang persisten tanpa flash tema yang salah. Ketika dikerjakan dengan benar, dark mode bukan cuma fitur estetika tambahan, melainkan peningkatan nyata terhadap kenyamanan visual, daya tahan baterai, dan aksesibilitas — sekaligus bukti bahwa tim produk memperhatikan detail pengalaman pengguna secara menyeluruh, bukan cuma di permukaan.

Ditulis oleh
@nopal
Ruang Iklan Tersedia
Hubungi Admin untuk menempatkan banner iklan atau Adsense Anda di sini.
Bagaimana pendapat Anda tentang artikel ini?
Komentar Pembaca (0)
Belum ada komentar. Jadilah yang pertama!