Seni Pra-muatan: Menghilangkan 'Flash' Tema dengan JavaScript dan CSS
Diterbitkan 15 November 2025
Ringkasan
Dalam artikel teknis mendalam ini, kita mengeksplorasi cara mengimplementasikan pemilih tema (light/dark) tanpa efek berkedip yang tidak menyenangkan yang terjadi saat memuat halaman. Kami menganalisis secara detail siklus rendering browser, penyebab mendalam FOUC (Flash of Unstyled Content), dan menawarkan solusi kuat berbasis pre-chargement sinkron dalam`<head>`. Teknik ini menjamin pengalaman pengguna yang lancar dan profesional.
Masalah: Kedipan Tema yang Sial
Sebuah mimpi buruk untuk pengalaman pengguna
Bayangkan adegan ini: Anda telah menghabiskan jam-jam untuk merancang tema gelap yang indah untuk aplikasi web Anda. Warna-warna seimbang sempurna, kontras optimal, dan pengguna Anda menyukai opsi ini. Tapi ada masalah memalukan: setiap kali halaman dimuat ulang, dalam sekejap, tema terang default muncul sebelum tema gelap menggantikannya.
Flash visual ini, meskipun singkat (kadang kurang dari 100ms), langsung terasa oleh mata manusia dan menciptakan pengalaman yang tidak menyenangkan. Untuk pengguna yang telah memilih tema gelap karena alasan kenyamanan visual atau aksesibilitas, kedipan ini bahkan dapat menyakitkan, terutama dalam lingkungan yang tidak terang.
FOUC: Masalah Web Kuno
Fenomena ini adalah varian dari yang disebut "Flash of Unstyled Content" (FOUC), sebuah masalah klasik dalam pengembangan web yang bermula sejak awal era CSS. FOUC terjadi ketika peramban menampilkan sementara konten HTML tanpa gaya CSS yang diterapkan, menciptakan kilat konten yang tidak terdahului.
Dalam kasus spesifik kita, kita tidak berbicara tentang konten yang tidak diberi gaya sepenuhnya, melainkan tentang sebuahKilat tema salah(FOWT) - Konten tersebut telah diberi gaya, tetapi dengan tema yang salah. Ini sangat menyebalkan karena ini menunjukkan bahwa aplikasi kita "melupakan" preferensi pengguna setiap kali halaman dimuat.
Dampak pada Persepsi Kualitas
Masalah ini, meskipun bersifat teknis, memiliki dampak yang signifikan terhadap persepsi kualitas aplikasi Anda :
Kurang halus: Kedipan memberi kesan bahwa aplikasi belum selesai atau tidak dioptimalkan dengan baik. Pengguna sering mengaitkan cacat visual kecil ini dengan kurangnya profesionalisme secara keseluruhan.
Kehilangan koherensiAplikasi sepertinya "melupakan" preferensi pengguna, menciptakan perasaan tidak sinkron antara antarmuka dan harapan.
Kelelahan visualUntuk pengguna yang sensitif terhadap cahaya atau yang mengalami migrain, cahaya kilat ini bisa menjadi lebih dari sekadar gangguan estetika.
Kinerja yang dirasakanIronis, meski situs Anda dimuat dengan cepat, ketipukan ini dapat memberi kesan aplikasi lambat atau kurang responsif.
Analisis Teknis Masalah
Untuk memahami cara menyelesaikan masalah ini, pertama-tama perlu memahami mengapa hal tersebut terjadi. Kedipan terjadi karena adanya keterlambatan waktu antara tiga peristiwa kritikal dalam siklus hidup sebuah halaman web :
-
Parsing awal dari HTMLPeramban membaca dan menganalisis struktur halaman Anda.
-
Penerapan gaya CSSPeramban menerapkan aturan CSS dan menghitung rendering visual
-
Eksekusi JavaScript: Kode Anda yang mengubah tema berjalan
Masalah terjadi ketika peristiwa no.3 (eksekusi JavaScript) tiba setelah browser sudah memulai atau menyelesaikan peristiwa no.2 (penerapan gaya). Saat itu, browser sudah membuat keputusan tentang tema mana yang akan ditampilkan, dan kode Anda terlambat untuk memengaruhinya sebelum rendering pertama.
Mengapa script klasik tidak cukup?
Pendekatan intuitif tetapi tidak efektif
Pendekatan paling natural untuk seorang pengembang akan menempatkan skrip di akhir dari kita.`<body>`yang memeriksa tema favorit pengguna dan menerapkannya. Pendekatan ini mengikuti praktik terbaik tradisional web yang merekomendasikan untuk memuat skrip di akhir halaman agar tidak memblokir rendering.
// À la fin de <body> - L'APPROCHE INSUFFISANTE
document.addEventListener('DOMContentLoaded', () => {
const theme = localStorage.getItem('preferred-theme');
if (theme === 'dark') {
document.documentElement.setAttribute('data-bs-theme', 'dark');
}
});
Pendekatan ini tampak logis pada pandangan pertama. Kami menunggu hingga DOM siap, lalu kami menerapkan tema. Sederhana, bukan? Sayangnya, kesederhanaan ini menyembunyikan cacat fundamental yang terkait dengan timing siklus rendering peramban.
Memahami Acara DOMContentLoaded
Kejadian`DOMContentLoaded`terpicu ketika dokumen HTML awal telah sepenuhnya dimuat dan dianalisis oleh peramban,tanpa menungguakhir pemuatan stylesheets, gambar, dan subframe. Ini adalah poin penting untuk dipahami.
Berikut adalah urutan tipikal peristiwa:
-
Peramban mulai mengunduh HTML
-
Dia menganalisis HTML seiring diterimaannya
-
Dia menemukan tag`<link>`untuk CSS dan mulai mengunduhnya
-
Dia menemukan tag`<script>`dan mengeksekusi mereka (sesuai tipe dan atribut mereka)
-
Dia membangun DOM (Document Object Model)
-
Peristiwa
DOMContentLoadedterpicu -
Dia terus menerapkan gaya dan melakukan layout
-
Pengecatan pertama (tampilan) terjadi
-
Acara`load`terjadi ketika semua resource telah dimuat
Masalah? Di antara langkah 6 (DOMContentLoaded) dan langkah 8 (paint pertama), peramban telah membuat keputusan tentang cara menampilkan halaman. Jika skrip perubahan tema Anda berjalan di langkah 6, sudah terlambat untuk menghindari tampilan pertama dengan gaya default.
Masalah Render Blocking
Sebenarnya, timing-nya masih lebih kompleks. Penyelancar modern menggunakan teknik optimisasi yang canggih untuk meningkatkan performa yang dirasakan. Mereka mencoba membuat first paint (First Contentful Paint) secepat mungkin agar pengguna melihat sesuatu di layar.
CSS secara default bersifat "render-blocking", artinya browser menunggu sampai stylesheet terunduh dan diparse sebelum melakukan first paint. Itu masuk akal: kita tidak ingin menampilkan konten yang belum diberi gaya.
Tapi ini adalah jebakannya: ketika peramban menerapkan gaya CSS ini untuk pertama kalinya, ia melakukannya berdasarkan keadaan saat ini dari DOM. Jika atribut`data-bs-theme`belum didefinisikan pada tag`<html>`, perambar akan menerapkan gaya default (biasanya tema terang).
Kemudian, ketika skrip Anda berjalan dan mengubah atribut ini, peramban harus:
-
Menghitung ulang semua gaya yang dipengaruhi oleh perubahan ini
-
Buat ulang tata letak jika diperlukan
-
Mengulang cat elemen yang terpengaruh
Proses penghitungan ulang dan penarikan ulang ini yang menyebabkan kedipan yang terlihat.
Visualisasi Masalah
Untuk lebih memahami urutan masalah ini, mari kita periksa diagram urutan terperinci:
@startuml
actor Utilisateur
participant Navigateur
participant "Parser HTML" as Parser
participant "CSSOM" as CSS
participant "Script.js (akhir body)" as Script
participant "Mesin render" as Render
Utilisateur -> Navigateur: Demande la page
activate Navigateur
Navigateur -> Parser: Parse le HTML
activate Parser
Parser -> CSS: Découvre et charge les CSS
activate CSS
Parser -> Parser: Construit le DOM
Parser -> Navigateur: DOM Ready (DOMContentLoaded)
deactivate Parser
CSS -> CSS: Parse les feuilles de style
CSS -> Render: CSSOM Ready
deactivate CSS
activate Render
Render -> Render: Calcule les styles avec data-bs-theme=""note right: Tema default (light)
Render -> Navigateur: Premier Paint (Tema Terang)
note right: Pengguna melihat tema terang
deactivate Render
Navigateur -> Script: Menjalankan skrip
activate Script
Script -> Script: Membaca localStorage
Script -> Navigateur: setAttribute('data-bs-theme', 'dark')
deactivate Script
activate Render
Render -> Render: Menghitung ulang gaya dengan data-bs-theme="dark"
note right: Thème sombre
Render -> Navigateur: Repaint (Thème Sombre)
note right: Clignotement visible
deactivate Render
deactivate Navigateur
@enduml
Diagram ini menggambarkan dengan jelas masalahnya: pertama kali paint terjadi sebelum skrip kami berempatan untuk menentukan tema yang tepat. Repaint yang kemudian menghasilkan berkedip yang terlihat.
Upaya Penyelesaian yang Tidak Efektif
Berbagai pendekatan telah dicoba untuk menyelesaikan masalah ini, tetapi kebanyakan memiliki kelemahan masing-masing :
Pendekatan 1: Menyembunyikan konten sampai pemuatan selesai
body {
opacity: 0;
transition: opacity 0.3s;
}
body.loaded {
opacity: 1;
}
Pendekatan ini menyembunyikan semua konten sampai JavaScript menentukan tema yang tepat. Masalah? Ini menunda secara buatan penampilan konten, memberi kesan situs lebih lambat. Selain itu, jika JavaScript dinonaktifkan, pengguna tidak akan melihat apa-apa sama sekali!
Pendekatan 2 : Menggunakan loader/spinner
Serupa dengan pendekatan 1, tetapi dengan spinner pemuat. Ini menyembunyikan masalah tetapi tidak meningkatkan performa aktual dan menambahkan penundaan yang dirasa tidak perlu.
Pendekatan 3 : Default ke tema gelap
Beberapa developer mengatur tema gelap sebagai default dalam CSS. Ini mencegah kedipan bagi pengguna tema gelap, tetapi menciptakan masalah yang berlawanan bagi pengguna tema terang!
Tidak ada pendekatan ini yang memuaskan karena mereka mengatasi gejala alih-alih akar masalah.
Solusi yang Benar : Bertindak Lebih Awal
Kunci untuk menyelesaikan masalah ini adalah menyadari bahwa kita harus menentukan atribut`data-bs-theme` sebelumbahwa peramban belum mulai menerapkan gaya CSS. Ini berarti bahwa skrip kami harus dieksekusi lebih awal dalam siklus hidup halaman, dan ini persis apa yang akan kita jelajahi di bagian berikutnya.
Solusi: Pemuatan Dini (Early Loading)
Prinsip Dasar
Solusi elegan untuk masalah berkedip kita didasarkan pada prinsip yang sederhana tetapi kuat:Mengsinkronkan status aplikasi dengan proses rendering peramban. Sebagai gantinya menunggu hingga halaman dimuat untuk menentukan tema, kita harus menetapkannyasaatpemuatan, bahkan sebelum gaya CSS diterapkan.
Pendekatan ini disebut "Early Loading" atau "Synchronous Preloading" dalam jargon pengembangan web. Ideanya adalah menjalankan logika deteksi tema kita secepat mungkin dalam siklus hidup halaman, idealnya dalam tag`<head>`, bahkan sebelum peramban mulai mengunduh file CSS.
Mengapa <head> adalah tempat yang ideal
Le `<head>`Sebuah elemen dari dokumen HTML diproses secara berurutan oleh peramban, dari atas ke bawah. Setiap elemen diproses dalam urutan tempat ia muncul. Karakteristik ini krusial untuk solusi kita.
Ketika peramban menemui sebuah tag`<script>`dalam`<head>`tanpa atribut-atribut`async` ou defer, il :
-
Menginterupsi parsing HTML
-
Unduh skrip(jika eksternal) atau tempat tidur (jika inline)
-
Jalankan skrip segera
-
Melanjutkan parsing HTML
Perilaku ini, yang sering dianggap sebagai masalah performa (sebab itu rekomendasi biasa adalah meletakkan script di akhir halaman), menjadi sekutu kita dalam kasus spesifik ini. Dengan menempatkan script deteksi tema kita di awal du`<head>`, kami menjamin bahwa dieksekusi sebelum browser bertemu dengan tag`<link>`dari lembar gaya kami.
Arsitektur Solusi dalam Tiga Lapisan
Solusi lengkap kami terdiri dari tiga lapisan yang saling terkait, masing-masing berperan secara spesifik :
Lapisan 1 : Persistensi (localStorage) Lapisan ini bertanggung jawab atas penyimpanan dan pemulihan pilihan pengguna antar sesi
Lapisan 2 : Sinkronisasi Dini (script inline dalam <head>) Lapisan ini mensinkronkan keadaan aplikasi dengan DOM sebelum rendering awal
Lapisan 3 : Gaya Responsif (CSS dengan selektor atribut) Lapisan ini menentukan gaya visual berdasarkan keadaan yang ditentukan oleh lapisan 2.
Mari kita jelajahi setiap lapisan secara detail sekarang
Langkah 1: Menyimpan Pilihan Pengguna
localStorage : Memori Persisten Anda
Le localStorage`adalah API Web Storage yang memungkinkan penyimpanan pasangan kunci-nilai dalam browser secara persisten. Berbeda dengan cookies, data`localStorage:
-
Tidak pernah dikirim ke server secara otomatis
-
Mempunyai kapasitas penyimpanan yang lebih besar (biasanya 5-10 MB)
-
Tidak memiliki tanggal kedaluwarsa (bertahan sampai dihapus secara eksplisit)
-
Dibatasi pada protokol dan domain (Same-Origin Policy)
Untuk kasus penggunaan kita, yang`localStorage`adalah sempurna karena :
-
Kami tidak perlu berbagi informasi ini dengan server
-
Kami ingin preferensi tersebut tetap bertahan selamanya
-
Ukuran penyimpanan yang diperlukan sangat kecil (beberapa byte)
Implementasi Cadangan Tema
Berikut cara kami menyimpan pilihan pengguna saat ia mengubah tema :
// Fonction complète pour changer le thème
function setTheme(newTheme) {
// Validation de l'entrée
if (!['light', 'dark', 'auto'].includes(newTheme)) {
console.error('Thème invalide:', newTheme);
return;
}
try {
// Sauvegarde dans localStorage
localStorage.setItem('preferred-theme', newTheme);
// Application immédiate dans le DOM
document.documentElement.setAttribute('data-bs-theme', newTheme);
// Dispatch d'un événement personnalisé pour notifier d'autres composants
window.dispatchEvent(new CustomEvent('theme-changed', {
detail: { theme: newTheme }
}));
console.log('Thème changé:', newTheme);
} catch (error) {
console.error('Erreur lors de la sauvegarde du thème:', error);
// Fallback : on applique quand même le thème visuellement
document.documentElement.setAttribute('data-bs-theme', newTheme);
}
}
// Exemple d'utilisation avec un bouton
document.getElementById('theme-toggle').addEventListener('click', () => {
const currentTheme = document.documentElement.getAttribute('data-bs-theme') || 'light';
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
setTheme(newTheme);
});
Manajemen Kasus Kesalahan
Penting untuk mengelola kasus di mana`localStorage`Tidak tersedia atau tidak dapat diakses. Beberapa skenario dapat mencegah akses ke`localStorage` :
Navigasi pribadi ketatSafari dalam mode penelusuran pribadi menimbulkan pengecualian`QuotaExceededError`saat percobaan menulis dalam`localStorage`.
Pengaturan privasi: Beberapa peramban atau ekstensi privasi dapat memblokir akses ke`localStorage`.
Keterbatasan domain : Le `localStorage`tidak dapat diakses pada protokol`file://`di beberapa peramban.
Ruang penyimpanan penuhMeskipun jarang, ruang penyimpanan dapat sepenuhnya terisi.
Karena itu kode kita menggunakan sebuah blok`try…catch`Untuk menangani kasus-kasus ini dengan anggun, sambil terus menawarkan fungsionalitas perubahan tema meskipun persistensi tidak tersedia.
Strategi Persistensi Lanjutan
Untuk aplikasi yang lebih canggih, Anda dapat mempertimbangkan strategi tambahan:
Sinkronisasi server (opsional)
async function setTheme(newTheme) {
// Sauvegarde locale immédiate
localStorage.setItem('preferred-theme', newTheme);
document.documentElement.setAttribute('data-bs-theme', newTheme);
// Synchronisation serveur en arrière-plan (si l'utilisateur est connecté)
if (userIsAuthenticated()) {
try {
await fetch('/api/user/preferences', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ theme: newTheme })
});
} catch (error) {
console.warn('Échec de la synchronisation serveur:', error);
// L'échec n'est pas critique car la préférence est déjà sauvegardée localement
}
}
}
Pendekatan ini memungkinkan sinkronisasi preferensi antar perangkat untuk pengguna yang terhubung, sambil tetap menjaga responsivitas lokal yang langsung.
Langkah 2: Skrip Preload di `<head>
Inti Solusi
Ini adalah tempat di mana sihir tersebut benar-benar berfungsi. Kami akan menempatkan sebuah skrip kecil.secara langsunglangsung di dalam kita`<head>`, sebelum semua tag kami`<link>`de lembar gaya. Skrip ini sengaja dibuat minimalis, mandiri, dan dirancang untuk berjalan secepat mungkin.
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mon Site Incroyable</title>
<!-- ==========================================
NOTRE SCRIPT MAGIQUE DE PRÉ-CHARGEMENT
Ce script DOIT être le premier élément
dans le <head> après les meta tags
========================================== -->
<script>
// IIFE pour ne pas polluer le scope global
(function() {
'use strict';
try {
// Lecture de la préférence sauvegardée
const savedTheme = localStorage.getItem('preferred-theme');
// Si une préférence existe, on l'applique immédiatement
if (savedTheme) {
document.documentElement.setAttribute('data-bs-theme', savedTheme);
}
// Optionnel : Détecter la préférence système si aucune sauvegarde
else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.setAttribute('data-bs-theme', 'dark');
}
// Sinon, le thème par défaut du CSS sera utilisé (généralement 'light')
} catch (error) {
// En cas d'erreur (localStorage bloqué, etc.), on log discrètement
// et on laisse le thème par défaut s'appliquer
console.warn('Impossible de charger la préférence de thème:', error);
}
})();
</script>
<!-- FIN DU SCRIPT MAGIQUE -->
<!-- Les feuilles de style sont chargées APRÈS le script -->
<link rel="stylesheet" href="css/bootstrap.min.css">
<link rel="stylesheet" href="css/styles.css">
<!-- Autres ressources du head -->
<link rel="icon" href="favicon.ico">
</head>
<body>
<!-- Contenu de la page -->
</body>
</html>
Anatomi Skrip : Setiap Baris Penting
Mari kita pecah skrip ini baris demi baris untuk memahami setiap keputusan desain:
IIFE (fungsi yang segera dipanggil)
(function() {
// ...
})();
Struktur ini membuat sebuah fungsi yang dieksekusi segera. Mengapa? Untuk mengisolasi variabel kita dalam cakupan lokal dan menghindari pencemaran cakupan global. Meskipun kita hanya menggunakan`const`(yang memiliki scope blok), IIFE adalah praktik yang baik yang membuat niat kita jelas dan melindungi dari konflik nama yang mungkin.
Mode Ketat
'use strict';
Directive ini mengaktifkan mode ketat JavaScript, yang : - Melarang penggunaan variabel yang tidak dideklarasikan - Menghasilkan kesalahan untuk operasi berbahaya - Meningkatkan performa di beberapa mesin JavaScript
Untuk skrip kritis seperti ini, kami ingin keamanan maksimal.
Blok try…catch
try {
// Code principal
} catch (error) {
console.warn('Impossible de charger la préférence de thème:', error);
}
Blok ini sangat penting. Ia menjamin bahwa jika terjadi sesuatu yang salah (localStorage terblokir, kesalahan sintaks improbable, dsb.), skrip kita tidak akan menghambat pemuatan seluruh halaman. Penggunaan`console.warn`daripada`console.error`mengindikasikan bahwa ini bukan masalah kritis
Pembacaan localStorage
const savedTheme = localStorage.getItem('preferred-theme');
Baris ini dapat menimbulkan pengecualian dalam beberapa konteks (privatisasi Safari yang ketat). Itu sebabnya ia berada dalam blok try…catch.
Aplikasi bersyarat
if (savedTheme) {
document.documentElement.setAttribute('data-bs-theme', savedTheme);
}
Kami menerapkan tema hanya jika kami menemukan tema yang telah disimpan. Jika tidak, kita membiarkan CSS menggunakan tema default-nya. Pendekatan ini lebih robust daripada nilai default yang di-hardcode dalam JavaScript.
Deteksi Preferensi Sistem (Bonus)
Penambahan yang opsional namun elegan adalah mendeteksi preferensi tema sistem operasi pengguna jika mereka belum membuat pilihan eksplisit dalam aplikasi Anda :
else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.setAttribute('data-bs-theme', 'dark');
}
Fitur ini menggunakan Media Query`prefers-color-scheme`untuk menanyakan sistem. Pada macOS, Windows 10+, iOS, dan Android modern, permintaan ini mengembalikan preferensi sistem pengguna.
Keuntungan : - Pengalaman yang dipersonalisasi sejak pertama kali berkunjung - Konsistensi dengan lingkungan sistem pengguna - Tidak perlu penyimpanan untuk kunjungan pertama
Pertimbangan: - Tidak semua browser mendukung fitur ini (tapi dukungannya sangat baik sejak 2020) - Verifikasi`window.matchMedia`memastikan kompatibilitas - Pengguna selalu dapat mengganti pilihan ini
Kinerja : Mengapa skrip ini cepat
Skrip pre-load kami dirancang untuk sangat cepat:
Ukuran minimumKira-kira 300 byte yang tidak dipersempit, 200 byte yang dipersempit. Itu dianggap tidak signifikan dibandingkan dengan gambar atau pustaka JavaScript apa pun.
secara langsungTidak ada permintaan HTTP tambahan. Skrip ada di dalam HTML, sehingga ia langsung tersedia.
Operasi sinkron sederhana: Membaca sebuah kunci dalam localStorage (operasi sangat cepat) dan modifikasi sebuah atribut DOM (operasi bawaan peramban).
Tidak ada ketergantungan: Tidak ada framework, tidak ada perpustakaan, hanya JavaScript vanilla. Tidak ada waktu pemuatan, tidak ada parsing dependensi.
Eksekusi tunggal: Skrip ini dijalankan hanya sekali saat dimuat. Tidak ada listener peristiwa, tidak ada perulangan, tidak ada perhitungan kompleks.
Dalam praktiknya, pada perangkat modern, skrip ini berjalan dalam kurang dari 1 milidetik, waktu yang tidak terasa yang tidak memiliki dampak apa pun pada kinerja pemuatan halaman.
Penempatan optimal di `<head>
Urutan elemen di dalam`<head>`penting. Berikut urutan yang disarankan :
<head>
<!-- 1. Métadonnées critiques -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 2. Notre script de pré-chargement (IMMÉDIATEMENT après les meta) -->
<script>
(function() { /* notre code */ })();
</script>
<!-- 3. Titre de la page -->
<title>Mon Site</title>
<!-- 4. Feuilles de style -->
<link rel="stylesheet" href="styles.css">
<!-- 5. Autres ressources (fonts, favicons, etc.) -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="icon" href="favicon.ico">
<!-- 6. Autres scripts avec defer ou async -->
<script src="app.js" defer></script>
</head>
Perintah ini memastikan bahwa :
-
Charset ditentukan sebelum apa pun pemrosesan teks
-
Skrip kita dieksekusi sebelum CSS dimuat
-
CSS kemudian dimuat dan langsung menerapkan tema yang tepat
-
Sumber daya non-kritis lainnya dimuat terakhir
Langkah 3: Kekuatan Selektor Atribut CSS
Sistem Tema Bootstrap 5
Bootstrap 5 telah memperkenalkan sistem tema yang elegan berbasis custom properties CSS (variabel CSS) dan selector atribut. Sistem ini menggunakan atribut`data-bs-theme`pada elemen`<html>`untuk menentukan kumpulan variabel warna yang akan diterapkan.
Keindahan sistem ini terletak pada kesederhanaannya: alih-alih memuat berbagai stylesheet atau mengalihkan kelas pada ribuan elemen, kita hanya mengubah satu atribut pada satu elemen, dan CSS melakukan sisa dengan menggunakan kaskad.
Struktur CSS untuk sebuah Sistem Tema
Berikut adalah struktur CSS lengkap untuk mengimplementasikan sistem tema yang kuat :
/**
* SYSTÈME DE THÈME COMPLET
* Utilise les Custom Properties CSS pour une maintenance facile
*/
/* ============================================
THÈME PAR DÉFAUT (LIGHT)
Défini sur :root pour être le fallback
============================================ */
:root {
/* Couleurs de base */
--color-primary: #0d6efd;
--color-secondary: #6c757d;
--color-success: #198754;
--color-danger: #dc3545;
--color-warning: #ffc107;
--color-info: #0dcaf0;
/* Couleurs de fond */
--bg-primary: #ffffff;
--bg-secondary: #f8f9fa;
--bg-tertiary: #e9ecef;
/* Couleurs de texte */
--text-primary: #212529;
--text-secondary: #6c757d;
--text-tertiary: #adb5bd;
/* Couleurs de bordure */
--border-color: #dee2e6;
--border-color-subtle: #e9ecef;
/* Couleurs d'ombre */
--shadow-sm: rgba(0, 0, 0, 0.075);
--shadow-md: rgba(0, 0, 0, 0.15);
--shadow-lg: rgba(0, 0, 0, 0.25