Come Implementare il Dark Mode con Variabili CSS e JavaScript: Tutorial Passo-Passo

Implementare il dark mode in un sito web non richiede librerie esterne né framework pesanti. In questa guida pratica vediamo come costruire un toggle tema completo utilizzando solo variabili CSS e un piccolo script JavaScript, con persistenza della preferenza utente tramite localStorage e rispetto delle impostazioni di sistema.

L’approccio che proponiamo è vanilla, performante e accessibile: zero dipendenze, caricamento istantaneo e nessun flash di contenuto non stilizzato (FOUC).

Perché Implementare il Dark Mode nel 2026

Il dark mode non è più una semplice tendenza estetica. Oggi rappresenta uno standard di usabilità che gli utenti si aspettano. I vantaggi concreti sono:

  • Riduzione dell’affaticamento visivo in condizioni di scarsa illuminazione
  • Risparmio energetico su schermi OLED e AMOLED
  • Miglior accessibilità per utenti con sensibilità alla luce
  • Coerenza con il sistema operativo dell’utente
  • Maggiore engagement: gli utenti restano più a lungo su siti che rispettano le loro preferenze
dark mode toggle screen

Le Tre Strade per il Dark Mode in CSS

Prima di scrivere codice, capiamo le opzioni disponibili nel panorama attuale:

Metodo Pro Contro
prefers-color-scheme Automatico, segue il sistema L’utente non può forzare un tema diverso
Funzione light-dark() Sintassi compatta e moderna Supporto browser più recente
Variabili CSS + attributo data-* Massima flessibilità, toggle manuale Richiede JavaScript

La soluzione che illustriamo combina i tre approcci per ottenere il meglio: rispetto della preferenza di sistema come default, possibilità di override manuale e codice manutenibile.

Step 1: Definire le Variabili CSS

Il fondamento di tutto è una buona architettura di custom properties. Nominale i colori in modo semantico (non per il colore in sé) così potrai cambiarli in base al tema senza riscrivere nulla.

:root {
  color-scheme: light dark;

  --bg-primary: #ffffff;
  --bg-secondary: #f4f4f5;
  --text-primary: #18181b;
  --text-secondary: #52525b;
  --accent: #0066ff;
  --border: #e4e4e7;
}

[data-theme="dark"] {
  --bg-primary: #0a0a0a;
  --bg-secondary: #1a1a1a;
  --text-primary: #fafafa;
  --text-secondary: #a1a1aa;
  --accent: #4d9fff;
  --border: #27272a;
}

body {
  background-color: var(--bg-primary);
  color: var(--text-primary);
  transition: background-color 0.2s ease, color 0.2s ease;
}

Nota due dettagli importanti:

  1. color-scheme: light dark; dice al browser di adattare anche gli elementi nativi (scrollbar, input, form)
  2. L’attributo data-theme sull’elemento html sarà controllato da JavaScript
dark mode toggle screen

Step 2: Il Markup del Toggle

Per garantire accessibilità, usiamo un button con stati ARIA appropriati:

<button 
  id="theme-toggle" 
  type="button"
  aria-label="Cambia tema"
  aria-pressed="false">
  <span class="icon-sun">☀</span>
  <span class="icon-moon">☾</span>
</button>

L’attributo aria-pressed comunica agli screen reader lo stato attuale del pulsante.

Step 3: Lo Script JavaScript

Ecco il cuore della logica. Lo script gestisce tre scenari: preferenza salvata, preferenza di sistema, toggle manuale.

(function() {
  const STORAGE_KEY = 'theme-preference';
  const root = document.documentElement;

  function getPreference() {
    const stored = localStorage.getItem(STORAGE_KEY);
    if (stored) return stored;
    return window.matchMedia('(prefers-color-scheme: dark)').matches 
      ? 'dark' 
      : 'light';
  }

  function applyTheme(theme) {
    root.setAttribute('data-theme', theme);
    const btn = document.getElementById('theme-toggle');
    if (btn) {
      btn.setAttribute('aria-pressed', theme === 'dark');
    }
  }

  // Applica subito per evitare flash
  let currentTheme = getPreference();
  applyTheme(currentTheme);

  document.addEventListener('DOMContentLoaded', function() {
    const toggle = document.getElementById('theme-toggle');
    if (!toggle) return;

    toggle.addEventListener('click', function() {
      currentTheme = currentTheme === 'dark' ? 'light' : 'dark';
      localStorage.setItem(STORAGE_KEY, currentTheme);
      applyTheme(currentTheme);
    });
  });

  // Aggiorna se cambia il tema di sistema
  window.matchMedia('(prefers-color-scheme: dark)')
    .addEventListener('change', function(e) {
      if (!localStorage.getItem(STORAGE_KEY)) {
        currentTheme = e.matches ? 'dark' : 'light';
        applyTheme(currentTheme);
      }
    });
})();

Evitare il Flash of Unstyled Content

Per impedire che la pagina appaia un istante con il tema sbagliato, inserisci lo script nel head del documento, prima di qualsiasi CSS che dipende dal tema, e senza attributo defer o async. Lo script è minimo e si esegue in millisecondi.

dark mode toggle screen

Step 4: Stilizzare il Pulsante Toggle

Mostra l’icona corretta in base al tema attivo:

#theme-toggle {
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  color: var(--text-primary);
  padding: 0.5rem 0.75rem;
  border-radius: 8px;
  cursor: pointer;
}

.icon-moon { display: none; }
[data-theme="dark"] .icon-sun { display: none; }
[data-theme="dark"] .icon-moon { display: inline; }

Checklist di Accessibilità

Un buon dark mode è anche accessibile. Verifica questi punti prima di pubblicare:

  • Il contrasto WCAG AA è rispettato in entrambi i temi (minimo 4.5:1 per il testo normale)
  • Il pulsante toggle è raggiungibile via tastiera
  • Lo stato è comunicato tramite aria-pressed o aria-label
  • Le transizioni rispettano prefers-reduced-motion
  • Immagini e icone restano visibili e leggibili in dark mode

Per il punto sulle animazioni ridotte, aggiungi:

@media (prefers-reduced-motion: reduce) {
  body {
    transition: none;
  }
}
dark mode toggle screen

Bonus: la Funzione light-dark() Moderna

Se vuoi sperimentare con la sintassi più recente supportata da tutti i browser moderni, puoi semplificare alcune regole:

:root {
  color-scheme: light dark;
  --bg-primary: light-dark(#ffffff, #0a0a0a);
  --text-primary: light-dark(#18181b, #fafafa);
}

Questa funzione restituisce automaticamente il primo o il secondo valore in base allo schema attivo. Resta comunque utile abbinarla all’attributo data-theme per il toggle manuale.

Errori Comuni da Evitare

  1. Usare colori puri come #000000 per lo sfondo dark: causa affaticamento. Meglio un nero leggermente più caldo come #0a0a0a
  2. Dimenticare le immagini: alcune immagini bianche “sparano” troppo in dark mode. Considera filtri o varianti specifiche
  3. Non testare i form: input, select e textarea hanno stili nativi che vanno gestiti
  4. Caricare lo script in fondo alla pagina: causa il flash bianco all’apertura
  5. Nominare le variabili per colore (es. --blu) invece che per funzione (es. --accent)

FAQ sul Dark Mode con CSS e JavaScript

Serve davvero JavaScript per il dark mode?

No, se ti basta seguire la preferenza di sistema puoi usare solo prefers-color-scheme in CSS. JavaScript serve quando vuoi offrire all’utente un toggle manuale e salvarne la scelta.

Posso usare localStorage in modo sicuro?

Sì, per una preferenza di tema è perfetto. È sincrono, persistente e non richiede consenso GDPR se usato esclusivamente per preferenze di interfaccia funzionali al sito.

Come gestisco le immagini in dark mode?

Tre opzioni: usare <picture> con sorgenti diverse via media query, applicare filtri CSS come filter: invert(1) a logo specifici, o servire SVG con colori basati su currentColor.

Il dark mode influisce sulla SEO?

Direttamente no, ma indirettamente sì: migliora tempo di permanenza, riduce la frequenza di rimbalzo e segnala attenzione all’esperienza utente, tutti fattori che Google considera.

Devo usare un framework come Tailwind per il dark mode?

Non è necessario. La soluzione vanilla mostrata in questo articolo è più leggera (meno di 1KB di JavaScript) e non aggiunge dipendenze al progetto.

Conclusione

Implementare un dark mode performante con CSS e JavaScript richiede meno di 50 righe di codice se imposti bene l’architettura. Le variabili CSS semantiche, un attributo data-theme sul root e uno script minimale per il toggle sono tutto ciò che serve.

Questo approccio è scalabile: funziona su un sito statico, su WordPress, su un’applicazione complessa. Senza framework, senza bundle pesanti, senza dipendenze. Solo standard web.

Se hai bisogno di integrare il dark mode in un progetto esistente o stai progettando da zero un sito moderno, il team di Alede Ro Design può aiutarti a strutturare il tuo sistema di temi in modo professionale e accessibile.

Leave a Comment