Tailwind v4 dark mode yang skalabel untuk banyak brand tidak lagi bergantung pada prefix dark: di setiap utility warna. Anda memetakan utility semantic (bg-primary, text-foreground) lewat @theme inline ke CSS custom properties, lalu mengganti nilai variabel saat runtime lewat [data-theme="dark"] atau kelas .dark. Build Tailwind tetap statis; yang bergerak hanya nilai variabel, bukan daftar class baru.
Tailwind CSS v4 menggeser konfigurasi tema ke CSS (theme variables resmi). Tutorial SitePoint (11 Oktober 2026) mempraktikkan pola serupa untuk beberapa brand theme sekaligus. Artikel ini merangkum mental model untuk proyek website klien—bukan migrasi penuh dari v3.
Mental Model Tailwind v4: CSS-First dan Theme Variables
Di v3, palet dan radius sering hidup di tailwind.config.js. Di v4 Anda menambah token lewat direktif @theme pada file CSS entry (@import "tailwindcss";). Variabel namespace --color-* langsung melahirkan utility seperti bg-primary atau border-stroke—bukan hardcode di framework seperti flex.
Theme variable ≠ variabel CSS biasa. Dokumentasi Tailwind menegaskan pakai @theme bila token harus punya utility class; pakai :root bila nilai hanya untuk referensi internal tanpa class. Untuk design system multi klien, tim menaruh palette mentah di :root atau file brand, lalu mendaftarkan semantic role ke Tailwind lewat @theme inline. Keputusan stack CSS tetap terpisah dari cara Anda menyimpan token warna—lihat CSS Modules, Tailwind, atau CSS Biasa untuk Proyek Klien bila tim masih membandingkan utility-first dengan CSS modular.
Palette Mentah vs Token Semantic

Palette mentah menyimpan angka warna (--brand-blue-500, --neutral-900); token semantic menyimpan peran UI (--app-background, --app-primary, --app-muted). Komponen seharusnya memanggil peran, bukan nama hue.
Blok HTML pertama menunjukkan markup tanpa semantic yang memaksa duplikasi variant. Blok kedua mempertahankan class tombol saat tema berganti.
<button class="bg-blue-600 text-white dark:bg-blue-400 dark:text-neutral-900">
Simpan
</button>
<button class="bg-primary text-primary-foreground">
Simpan
</button>
SitePoint memakai kosakata surface, on-surface, stroke. Proyek Anda boleh memakai background/foreground asalkan tim konsisten. Jangan men-copy naming library UI lain tanpa menyesuaikan desain klien.
@theme inline: Memetakan Utility ke var() Dinamis
Bila theme variable Tailwind merujuk variabel lain, dokumentasi resmi meminta modifier inline. Tanpa itu, utility bisa menyelesaikan var() di scope yang salah sehingga warna anak pun tidak ikut tema subtree. Blok berikut mengikat utility Tailwind ke variabel aplikasi yang bisa Anda override per tema:
:root {
--app-background: #ffffff;
--app-foreground: #111827;
--app-primary: #2563eb;
--app-primary-foreground: #ffffff;
}
@theme inline {
--color-background: var(--app-background);
--color-foreground: var(--app-foreground);
--color-primary: var(--app-primary);
--color-primary-foreground: var(--app-primary-foreground);
}
Utility yang dihasilkan memakai referensi langsung ke --app-*. Saat Anda mengoverride --app-primary di [data-theme="ocean"], tombol bg-primary ikut berubah tanpa rebuild. Modifier inline bukan optimasi kecepatan dan tidak wajib untuk setiap token—hanya untuk rantai referensi var().
Selanjutnya, untuk latihan cascade variabel di luar Tailwind, tim bisa memakai playground di tutorial CSS Playground & Simulator Layout. Konsepnya sama dengan yang Anda andalkan untuk theme switch.
Dark Mode, Multi Tema, dan Selector data-theme
[data-theme="dark"] {
--app-background: #0f172a;
--app-foreground: #f8fafc;
--app-primary: #60a5fa;
--app-primary-foreground: #0f172a;
}
[data-theme="ocean"] {
--app-primary: #0ea5e9;
--app-background: #ecfeff;
}
Untuk mode gelap, override semantic variables di CSS seperti di atas. Nilai --app-* berubah tanpa menyentuh markup komponen. HTML cukup class="bg-background text-foreground".
Multi-brand agency men-deploy satu library komponen dengan file CSS brand terpisah yang hanya mengisi ulang --app-*. Untuk mode sistem, Tailwind mendokumentasikan pola window.matchMedia("(prefers-color-scheme: dark)") bersama preferensi tersimpan (dark mode Tailwind). Tiga opsi umum: light, dark, dan system; script menulis data-theme atau kelas .dark pada document.documentElement sesuai keputusan user.
Custom Variant dark: untuk Class atau data-theme
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
Bila sebagian UI tetap memakai utility dark:, override variant di CSS entry agar selector mengikuti atribut data-theme proyek Anda. Variant ini coexist dengan semantic variables: warna bulk lewat token. Tweak seperti dark:opacity-80 tetap lewat variant.
SitePoint menekankan menghilangkan semua dark: dari markup bukan syarat v4. Itulah pilihan arsitektur bila variabel sudah menutupi seluruh permukaan warna.
Kapan dark: Lebih Tepat daripada Token Baru
Semantic token unggul untuk background, teks, border, dan warna form—terutama jika lebih dari dua tema. Walau begitu, beberapa perbedaan tidak layak jadi token global. Contohnya visibility gambar dekoratif, mix-blend-mode, shadow elevation berbeda, atau padding section marketing khusus dark mode.
Contoh sah tetap memakai variant seperti pada markup gambar di bawah. Logo dual-tone pun sering butuh asset berbeda, bukan sekadar --app-primary. Memaksa setiap perbedaan visual masuk token membuat design system over-dynamic dan sulit diaudit.
<img class="opacity-90 dark:opacity-100 dark:invert-0 invert" src="diagram.svg" alt="Diagram alur checkout" />
Theme Switcher, FOUC, dan SSR
JavaScript yang men-set tema setelah paint pertama menimbulkan flash light→dark. Mitigasi umum: snippet inline di <head> (sebelum CSS) yang membaca localStorage atau cookie, lalu menulis data-theme pada elemen root. Server juga bisa merender atribut tema dari cookie pada layout WordPress atau Next.js.
Build Tailwind tidak perlu Anda jalankan ulang saat user men-toggle tema. Nilai variabel berubah di browser; class utility baru tidak muncul dinamis. String seperti bg-${name}-500 tetap gagal kecuali safelist/build strategy.
Akhirnya, breakpoint, spacing, dan radius boleh tetap statis di @theme bila tidak ikut switch runtime. Jangan buat seluruh design token dapat di-toggle tanpa kebutuhan produk.
WordPress dan Website Klien
Theme WordPress custom umumnya mengompilasi Tailwind sekali lewat PostCSS/Vite, lalu meng-enqueue file CSS hasil build seperti biasa. Preferensi dark/light tinggal di browser atau cookie. PHP meneruskan atribut awal ke <html> bila Anda ingin SSR konsisten.
Warna brand dari CMS sebaiknya masuk variabel (--app-primary: … setelah validasi), bukan generate class Tailwind dari input user. Untuk gradient kompleks lihat CSS Gradient untuk Website. Pisahkan token warna solid dari dekorasi gradient supaya kontras tetap terukur.
Aksesibilitas, Kontras, dan color-scheme
Mengganti background gelap belum menjamin kontras WCAG. Tim perlu mengecek setiap pasangan primary/primary-foreground per tema, termasuk hover, focus ring, dan disabled. Tailwind menyediakan utility scheme-light, scheme-dark, dan scheme-light-dark agar kontrol native browser selaras dengan tema.
Transisi warna pada background-color boleh dipakai. Hormati prefers-reduced-motion dan hindari animasi saat apply tema pertama kali jika memperparah flash. Token semantic tidak menggantikan pengujian aksesibilitas manual atau automated.
Ringkas v3→v4 dan Langkah Verifikasi
Perpindahan mental: config JS → CSS @theme, plus pemisahan palette vs semantic. Kompilasi CSS lalu periksa utility seperti .bg-primary memuat var(--app-primary) atau fallback yang Anda definisikan. SitePoint menyarankan inspeksi output agar token custom tidak bentrok dengan namespace default.
Stakeholder yang membandingkan utility-first dengan framework komponen bisa membaca Font Pairing & Variable Font Simulator yang juga menyinggung uji dark mode di staging. Checklist sebelum production: uji tiga tema (light, dark, satu brand), toggle system mode, reload hard tanpa flash, kontras tombol utama, dan pastikan tidak ada string class dinamis di template PHP/JSX.