Samenvatting

In dit diepgaande technische artikel onderzoeken we hoe we een thema-selector (light/dark) kunnen implementeren zonder het vervelende flikker-effect dat optreedt bij het laden van een pagina. We analyseren in detail de renderingscyclus van de browser, de diepe oorzaken van FOUC (Flash of Unstyled Content), en stellen een robuuste oplossing voor die is gebaseerd op synchroon vooraf laden in de`<head>`. Deze techniek garandeert een soepele en professionele gebruikerservaring.

Het probleem: Dit vervloekte flikkeren van het thema

Een nachtmerrie voor de Gebruikerservaring

Stel je de scène voor: je hebt uren besteed aan het ontwerpen van een prachtig donker thema voor je webapplicatie. De kleuren zijn perfect in balans, het contrast is optimaal, en je gebruikers zijn dol op deze optie. Maar er is een vervelend probleem: telkens wanneer de pagina wordt herladen, wordt gedurende een fractie van een seconde het standaard lichte thema getoond voordat het donkere thema het overneemt.

Deze visuele 'flits', hoewel kort (soms minder dan 100 ms), wordt onmiddellijk waarneembaar door het menselijk oog en veroorzaakt een onaangename ervaring. Voor gebruikers die het donkere thema hebben gekozen omwille van visueel comfort of toegankelijkheid, kan dit flikkeren zelfs pijnlijk zijn, vooral in een slecht verlichte omgeving.

Het FOUC: Een oud webprobleem

Dit fenomeen is een variant van wat men de "Flash of Unstyled Content" (FOUC) noemt, een klassiek probleem in de webontwikkeling dat teruggaat tot de vroege dagen van CSS. Het FOUC treedt op wanneer de browser tijdelijk HTML-inhoud weergeeft zonder de toegepaste CSS-stijlen, waardoor een flits van onge styled inhoud ontstaat.

In ons specifieke geval praten we niet over volledig ongeformatteerde inhoud, maar eerder over eenFlash van fout thema(FOWT) - de inhoud is gestyled, maar met het verkeerde thema. Dit is bijzonder frustrerend omdat het aantoont dat onze app "vergeet" de voorkeur van de gebruiker bij elke pagina-lading.

Impact op de perceptie van kwaliteit

Dit probleem, hoewel technisch, heeft belangrijke gevolgen op de perceptie van de kwaliteit van uw applicatie:

Geen afwerking: Het knipperen geeft de indruk van een onafgemaakte of slecht geoptimaliseerde toepassing. Gebruikers associëren deze kleine visuele gebreken vaak met een gebrek aan algemene professionaliteit.

Verbreking van coherentie: De toepassing lijkt de voorkeuren van de gebruiker te "vergeten", creërend een gevoel van desynchronisatie tussen de interface en de verwachtingen.

Visuele vermoeidheid: Voor gebruikers die gevoelig zijn voor licht of last hebben van migraine, kan deze felle flits meer zijn dan alleen een esthetisch ongemak.

Ervaren prestatie: Ironisch, zelfs als uw site snel wordt geladen, kan dit knipperen de indruk wekken van een trage of ongevoelige toepassing.

Technische analyse van het probleem

Om te begrijpen hoe dit probleem op te lossen, moet je eerst begrijpen waarom het zich voordoet. Het knipperen treedt op vanwege een tijdsverschuiving tussen drie kritieke gebeurtenissen in de levenscyclus van een webpagina :

  1. De initiële parsing van de HTML: De browser leest en analyseert de structuur van uw pagina

  2. De toepassing van CSS-stijlenDe browser past de CSS-regels toe en berekent de visuele weergave

  3. De uitvoering van JavaScript: Jouw code die het thema verandert wordt uitgevoerd

Het probleem treedt op wanneer gebeurtenis nr. 3 (uitvoering van JavaScript) plaatsvindt nadat de browser al is begonnen of de gebeurtenis nr. 2 (toepassing van stijlen) heeft voltooid. Op dat moment heeft de browser al een beslissing genomen over welk thema moet worden weergegeven, en komt uw code te laat om dat te beïnvloeden vóór de eerste weergave.

Waarom is een klassiek script niet genoeg?

De intuïtieve maar inefficiënte benadering

De meest natuurlijke aanpak voor een ontwikkelaar zou zijn om een script aan het einde van onze`<body>`die het voorkeursthema van de gebruiker controleert en toepast. Deze aanpak volgt de beste traditionele praktijken van het web die aanbevelen om scripts aan het einde van de pagina te laden om het renderen niet te blokkeren.

// À 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');
  }
});

Deze aanpak lijkt logisch bij eerste gezicht. We wachten tot de DOM klaar is, waarna we het thema toepassen. Simpel, toch? Helaas verbergt deze eenvoud een fundamenteel gebrek dat verband houdt met de timing van het renderingscyclus van de browser.

Begrijpen van de DOMContentLoaded-gebeurtenis

Het evenement`DOMContentLoaded`activeert wanneer het initiële HTML-document volledig is geladen en geparsed door de browser,onmiddellijkhet einde van het laden van de stijlbladen, afbeeldingen en subframes. Dit is een belangrijk punt om te begrijpen.

Dit is de typische volgorde van de gebeurtenissen :

  1. De browser begint met het downloaden van de HTML

  2. Hij analyseert de HTML naarmate deze wordt ontvangen.

  3. Hij ontdekt de tags`<link>`voor de CSS en begint ze te downloaden

  4. Hij ontdekt de tags`<script>`en voert ze uit (naar hun type en attributen)

  5. Hij bouwt het DOM (Document Object Model)

  6. Het evenement DOMContentLoaded wordt geactiveerd

  7. Hij blijft stijlen toepassen en de lay-out maken

  8. De eerste paint (weergave) vindt plaats

  9. Het evenement`load`Wordt geactiveerd wanneer alle resources zijn geladen

Het probleem? Tussen stap 6 (DOMContentLoaded) en stap 8 (first paint) heeft de browser al beslissingen genomen over hoe de pagina wordt weergegeven. Als uw script voor themawijziging wordt uitgevoerd op stap 6, is het al te laat om een eerste weergave met de standaardstijlen te vermijden.

Het probleem van Render Blocking

In werkelijkheid is de timing nog complexer. Moderne browsers gebruiken geavanceerde optimalisatietechnieken om de waargenomen prestaties te verbeteren. Ze proberen de eerste paint (First Contentful Paint) zo snel mogelijk uit te voeren zodat de gebruiker iets op het scherm ziet.

De CSS zijn standaard "render-blocking", wat betekent dat de browser wacht totdat hij de stylesheets heeft gedownload en geparseerd voordat hij de eerste paint uitvoert. Dat is logisch: we willen geen onge-stijlde inhoud weergeven.

Maar hier komt het struikelblok: wanneer de browser deze CSS-stijlen voor het eerst toepast, doet hij dat op basis van de huidige staat van het DOM. Als het attribuut`data-bs-theme`is nog niet gedefinieerd op de tag`<html>`, de browser zal de standaardstijlen toepassen (doorgaans het lichte thema).

Daarna, wanneer je script wordt uitgevoerd en dit attribuut verandert, moet de browser:

  1. Herbereken alle stijlen die door deze wijziging worden beïnvloed

  2. Herzien de lay-out indien nodig

  3. Schilder de getroffen elementen opnieuw

Dit proces van herbereking en opschildering is wat het zichtbare flikkeren veroorzaakt.

Visualisatie van het probleem

Om deze problematische sequentie beter te begrijpen, laten we een gedetailleerd sequentiediagram onderzoeken:

@startuml
actor Utilisateur
participant Navigateur
participant "Parser HTML" as Parser
participant "CSSOM" as CSS
participant "Script.js (einde van de body)" as Script
participant "Render Engine" as Render

Utilisateur -> Navigateur: Vraag de pagina
activate Navigateur

Navigateur -> Parser: Parse het HTML
activate Parser
Parser -> CSS: Ontdek en laad de CSS
activate CSS
Parser -> Parser: Bouwt de DOM
Parser -> Navigateur: DOM klaar (DOMContentLoaded)
deactivate Parser

CSS -> CSS: Parseer de stijlbladen
CSS -> Render: CSSOM Klaar
deactivate CSS

activate Render
Render -> Render: Bereken de stijlen met data-bs-theme=note right: Standaardthema (light)\nRender -> Navigateur: Eerste Paint (Licht thema)\nnote right: De gebruiker ziet het licht thema\ndeactivate Render\n\nNavigateur -> Script: Voer het script uit\nactivate Script\nScript -> Script: Leest localStorage\nScript -> Navigateur: setAttribute('data-bs-theme', 'dark')\ndeactivate Script\n\nactivate Render\nRender -> Render: Herbereken de stijlen met data-bs-theme="dark"
note right: Donker thema
Render -> Navigateur: Herverf (Donker Thema)
note right: Zichtbaar flikkeren
deactivate Render

deactivate Navigateur
@enduml

Dit diagram illustreert duidelijk het probleem: de eerste paint vindt plaats voordat ons script de kans heeft gehad om het juiste thema in te stellen. Het daaropvolgende repaint veroorzaakt het zichtbare knipperen.

De ondoeltreffende oplossingspogingen

Verschillende benaderingen zijn geprobeerd om dit probleem op te lossen, maar de meeste hebben hun eigen nadelen :

Aanpak 1: verberg de inhoud tot het laden

body {
  opacity: 0;
  transition: opacity 0.3s;
}

body.loaded {
  opacity: 1;
}

Deze aanpak verbergt alle inhoud totdat JavaScript het juiste thema heeft ingesteld. Het probleem? Dit vertraagt de weergave van de inhoud kunstmatig, waardoor de indruk ontstaat dat de site langzamer is. Bovendien, als JavaScript is uitgeschakeld, ziet de gebruiker helemaal niets!

Benadering 2: Gebruik een loader/spinner

Gelijk aan benadering 1, maar met een laadspinner. Dit verbloemt het probleem, maar verbetert de werkelijke prestaties niet en voegt een onnodige waargenomen vertraging toe.

Benadering 3: Standaard donker thema

Sommige ontwikkelaars definiëren het donkere thema als standaard in de CSS. Dit voorkomt flikkeren voor gebruikers van het donkere thema, maar creëert het tegenovergestelde probleem voor gebruikers van het lichte thema!

Geen van deze benaderingen is bevredigend omdat ze het symptoom behandelen in plaats van de hoofdoorzaak van het probleem.

De echte oplossing: eerder handelen

De sleutel om dit probleem op te lossen is te beseffen dat we het attribuut moeten definiëren`data-bs-theme` voordat de browser niet begint met het toepassen van de CSS-stijlen. Dit betekent dat ons script eerder moet worden uitgevoerd in de levenscyclus van de pagina, en dat is precies wat we in de volgende sectie gaan verkennen.

De oplossing: vroeg laden (Early Loading)

Het fundamentele principe

De elegante oplossing voor ons flikkeringprobleem is gebaseerd op een eenvoudige maar krachtige principe :synchroniseer de staat van de applicatie met het renderproces van de browser. In plaats van te wachten tot de pagina is geladen om het thema in te stellen, moeten we het instellentijdenshet laden, zelfs voordat de CSS-stijlen zijn toegepast.

Deze aanpak staat bekend als "Early Loading" of "Synchronous Preloading" in de jargon van webontwikkeling. Het idee is om ons thema-detectielogica zo vroeg mogelijk in de levenscyclus van de pagina uit te voeren, idealiter in de tag`<head>`, zelfs voordat de browser begint met het downloaden van de CSS-bestanden.

Waarom is <head> de ideale plek

Le `<head>`Een HTML-document wordt sequentieel door de browser verwerkt, van boven naar beneden. Elk element wordt verwerkt in de volgorde waarin het voorkomt. Deze eigenschap is cruciaal voor onze oplossing.

Wanneer de browser een tag tegenkomt`<script>`in de`<head>`zonder de attributen`async` ou defer, il :

  1. Onderbreek het parseren van de HTML

  2. Download het script(indien extern) of het bed (indien inline)

  3. Voer onmiddellijk het script uit

  4. Hervat het parseren van de HTML

Dit gedrag, vaak beschouwd als een performanceprobleem (waarom de gebruikelijke aanbeveling is om scripts aan het einde van de pagina te plaatsen), wordt in dit specifieke geval onze bondgenoot. Door ons thema-detectiescript aan het begin van de`<head>`, We garanderen dat het wordt uitgevoerd voordat de browser de tags tegenkomt.`<link>`van onze stijlbladen.

Architectuur van de Oplossing in Drie Lagen

Onze volledige oplossing bestaat uit drie onderling afhankelijke lagen, elk met een specifieke functie:

Laag 1 : Persistentie (localStorage) Deze laag is verantwoordelijk voor het opslaan en ophalen van de keuze van de gebruiker tussen de sessies.

Laag 2 : Vroege synchronisatie (Inline script in <head>) Deze laag synchroniseert de staat van de applicatie met het DOM voordat de initiële rendering plaatsvindt.

Laag 3: Reactieve stijlen (CSS met attribuutselectors) Deze laag definieert de visuele stijlen gebaseerd op de staat gedefinieerd door laag 2.

Laten we nu elke laag in detail verkennen.

Stap 1: Bewaar de keuze van de gebruiker

localStorage : Uw permanente geheugen

Le localStorage`is een Web Storage API die het opslaan van sleutel-waardeparen in de browser op een permanente manier mogelijk maakt. In tegenstelling tot cookies zijn de gegevens van`localStorage:

  • Ze worden nooit automatisch naar de server verzonden

  • Hebben een grotere opslagcapaciteit (meestal 5-10MB)

  • Ze hebben geen vervaldatum (blijft behouden tot expliciete verwijdering)

  • Zijn beperkt tot het protocol en het domein (Same-Origin Policy)

Voor onze gebruikssituatie, de`localStorage`is perfect omdat :

  1. We hoeven deze informatie niet te delen met de server

  2. We willen dat de voorkeur onbeperkt blijft

  3. De benodigde opslagruimte is minimaal (enkele bytes)

Implementatie van de thema-backup

Dit is hoe we de keuze van de gebruiker opslaan wanneer hij van thema verandert:

// 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);
});

Foutafhandeling

Het is cruciaal om de gevallen te beheren waar het`localStorage`is niet beschikbaar of toegankelijk. Verschillende scenario’s kunnen toegang tot het verhinderen`localStorage`:

Strikte privénavigatie: Safari in private browsing mode raises an exception`QuotaExceededError`tijdens pogingen om in het`localStorage`.

Privacy-instellingen: Sommige browsers of privacy-extensions kunnen de toegang tot de`localStorage`.

Domeinbeperkingen : Le `localStorage`is niet toegankelijk via het protocol`file://`in bepaalde browsers.

Opslagruimte vol: Hoewel zeldzaam, kan de opslagruimte volledig gevuld zijn.

Daarom gebruikt onze code een blok`try…​catch`om deze gevallen netjes af te handelen, door de functionaliteit om van thema te wisselen aan te blijven bieden, zelfs als persistentie niet beschikbaar is.

Geavanceerde strategieën van persistentie

Voor meer gevorderde toepassingen kunt u extra strategieën overwegen :

Server synchronisatie (optioneel)

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
    }
  }
}

Deze aanpak maakt het mogelijk om de voorkeuren tussen apparaten te synchroniseren voor aangemelde gebruikers, terwijl onmiddellijke lokale responsiviteit behouden blijft.

Stap 2: Het Preload Script in het `<head>

Het hart van de oplossing

Hier gebeurt de magie echt. We gaan een klein scriptinlinerechtstreeks in onze`<head>`, voor al onze tags`<link>`van stijlbladen. Dit script is opzettelijk minimalistisch, autonoom, en ontworpen om zo snel mogelijk uit te voeren.

<!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>

Anatomie van het script: Elke regel telt

Laten we dit script regel voor regel ontleden om elke ontwerpbeslissing te begrijpen:

IIFE (Onmiddellijk uitgevoerde functie-expressie)

(function() {
  // ...
})();

Deze structuur maakt een functie die onmiddellijk wordt uitgevoerd. Waarom? Om onze variabelen te isoleren in een lokale scope en te voorkomen dat we de globale scope vervuilen. Zelfs als we alleen`const`(die een blok-scope heeft), de IIFE is een goede praktijk die onze intenties duidelijk maakt en beschermt tegen mogelijke naamconflicten.

De Strikte Modus

'use strict';

Deze richtlijn activeert de strikte modus van JavaScript, die: - Verbiedt het gebruik van niet-geclareerde variabelen - Genereert fouten voor gevaarlijke bewerkingen - Verbeter de prestaties in bepaalde JavaScript-motoren

Voor een critisch script als dit willen we maximale veiligheid.

Het Blok try…​catch

try {
  // Code principal
} catch (error) {
  console.warn('Impossible de charger la préférence de thème:', error);
}

Dit blok is absoluut essentieel. Het garandeert dat als er iets mis gaat (localStorage geblokkeerd, onwaarschijnlijke syntaxis‑fout, enz.), ons script het laden van de hele pagina niet zal blokkeren. Het gebruik van`console.warn`liever dan`console.error`Wijst erop dat het een niet-kritiek probleem is.

Het lezen van localStorage

const savedTheme = localStorage.getItem('preferred-theme');

Deze regel kan een uitzondering veroorzaken in sommige contexten (strikte privé‑modus van Safari). Daarom staat deze in een try…​catch-blok.

De voorwaardelijke toepassing

if (savedTheme) {
  document.documentElement.setAttribute('data-bs-theme', savedTheme);
}

We passen het thema alleen toe als we er één hebben gevonden en opgeslagen. Anders laten we CSS zijn standaardthema gebruiken. Deze aanpak is robuuster dan een standaardwaarde die hard gecodeerd is in de JavaScript.

Detectie van de systeemvoorkeur (Bonus)

Een optionele maar elegante verbetering is het detecteren van de themavoorkeur van het besturingssysteem van de gebruiker als hij of zij nog geen expliciete keuze heeft gemaakt in uw toepassing:

else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
  document.documentElement.setAttribute('data-bs-theme', 'dark');
}

Deze functie maakt gebruik van de Media Query`prefers-color-scheme`voor het systeem te bevragen. Op macOS, Windows 10+, iOS, en moderne Android retourneert deze query de systeemvoorkeur van de gebruiker.

Voordelen : - Persoonlijke ervaring vanaf het eerste bezoek - Consistentie met de systeemomgeving van de gebruiker - Geen opslag nodig voor het eerste bezoek

Overwegingen : - Niet alle browsers ondersteunen deze functie (maar de ondersteuning is sinds 2020 uitstekend) - De controle`window.matchMedia`zorgt voor compatibiliteit - De gebruiker kan deze keuze altijd overschrijven

Prestatie: Waarom dit script snel is

Ons preload-script is ontworpen om extreem snel te zijn:

Minimale grootteOngeveer 300 bytes niet-geminificeerd, 200 bytes geminificeerd. Het is verwaarloosbaar vergeleken met elke afbeelding of JavaScript-bibliotheek.

InlineGeen extra HTTP-verzoek. Het script staat in de HTML, dus het is onmiddellijk beschikbaar.

Eenvoudige synchrone operaties: Het lezen van een sleutel in localStorage (ultra-snelle bewerking) en wijziging van een DOM-attribuut (native bewerking van de browser).

Geen afhankelijkheidGeen framework, geen bibliotheek, alleen vanilla JavaScript. Geen opstarttijd, geen parsing van afhankelijkheden.

Unieke uitvoering: Dit script wordt één keer uitgevoerd bij het laden. Geen event‑listeners, geen lussen, geen complexe berekeningen.

In de praktijk, op moderne hardware, voert dit script uit in minder dan 1 milliseconde, een onmerkbaar tijdstip dat geen invloed heeft op de laadprestaties van de pagina.

Optimale plaatsing in de `<head>

De volgorde van de elementen in de`<head>`is belangrijk. Hier is de aanbevolen volgorde :

<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>

Deze order garandeert dat:

  1. De charset wordt gedefinieerd voordat er enige tekstverwerking plaatsvindt

  2. Ons script wordt uitgevoerd voordat de CSS wordt geladen

  3. De CSS worden vervolgens geladen en passen direct het juiste thema toe

  4. Andere niet‑kritische bronnen worden als laatste geladen

Stap 3: De kracht van CSS-attribuutselectors

Het thema-systeem van Bootstrap 5

Bootstrap 5 heeft een elegant systeem voor themabeheer geïntroduceerd gebaseerd op CSS custom properties (CSS-variabelen) en attribuutselectors. Dit systeem maakt gebruik van het attribuut`data-bs-theme`op het element`<html>`om te bepalen welke set kleurvariabelen moet worden toegepast.

De schoonheid van dit systeem zit in zijn eenvoud: in plaats van verschillende stylesheets te laden of klassen te toggelen op duizenden elementen, veranderen we eenvoudigweg een attribuut op één element, en doet de CSS de rest dankzij de cascade.

CSS-structuur voor een themasysteem

Hier is een volledige CSS-structuur voor het implementeren van een robuust thema-systeem:

/**
 * 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

Gerelateerde artikelen