Pulsante argomento interfaccia
1) Principi
1. Sistema> skin. Il tema non è solo un'inversione di sfondo, ma un insieme di token (colore, sfondo, contrasto, ombre, stati, illustrazioni, grafici).
2. System-first. Il valore predefinito è System ('preferers-color-scheme') con la possibilità di selezionare manualmente Light/Dark/High-Contrast.
3. Contrasto predefinito. L'obiettivo è WCAG AA, per testo piccolo/etichette importanti - AAA.
4. Niente flash. Applichiamo il tema fino allo script inline, mentre le transizioni vengono eseguite con attenzione.
5. Stabilità del marchio. L'accento e la semantica degli stati vengono mantenuti in tutti i temi.
2) Modalità e script
Light - script diurni/moduli di pagamento/lettura lunga.
Dark - serata/bassa illuminazione/partite live; riduce i riflessi.
Sistema - Seguiamo il sistema operativo/browser («preferers-color-scheme»).
High-Contrast - aumento del contrasto e minimizzazione dei gioielli.
Seasonal/Promo (opzionale) è sopra il tema base del torneo/ivent (non rompe i token).
3) Architettura dei token
Memorizziamo i token semantici, non i colori retti:css
:root {
/ semantics/
--bg: hsl(0 0% 99%);
--bg-elev: hsl(0 0% 100%);
--fg: hsl(220 15% 15%);
--muted: hsl(220 10% 45%);
--accent: hsl (260 95% 60%) ;/brand accent/
--success: hsl(152 55% 40%);
--warning: hsl(36 85% 45%);
--danger: hsl(0 75% 50%);
--border: hsl(220 10% 90%);
--focus: hsl(260 95% 60% /.6);
--shadow: 0 6px 24px hsl(220 20% 10% /.08);
/ typography/radii/
--radius: 12px;
--lh: 1. 4;
}
:root[data-theme="dark"]{
--bg: hsl(220 18% 10%);
--bg-elev: hsl(220 18% 14%);
--fg: hsl(0 0% 96%);
--muted: hsl(220 10% 70%);
--accent: hsl (260 95% 65%) ;/slightly lighter in the dark/
--border: hsl(220 14% 22%);
--shadow: 0 8px 28px hsl(220 50% 2% /.6);
}
:root[data-theme="hc"]{
/ high-contrast: simple pairs, high Lc/
--bg: #000; --bg-elev:#000; --fg:#fff; --muted:#fff;
--accent:#00E; --success:#0F0; --warning:#FF0; --danger:#F00;
--border:#fff; --focus:#0FF;
}
Regola: i componenti utilizzano solo token semantici.
4) Rilevatore e salvataggio delle selezioni
html
<script>
(function(){
const saved = localStorage. getItem('theme'); // 'light' 'dark' 'hc' 'system' null const sys = window. matchMedia('(prefers-color-scheme: dark)'). matches? 'dark': 'light';
const theme = saved && saved!=='system'? saved: sys;
document. documentElement. setAttribute('data-theme', theme);
})();
</script>
Interruttore UI: 'Light/Dark/System/High-Contrast'. Se si seleziona'System ', non salvare un colore specifico, solo un flag. Ascolti i cambiamenti del sistema operativo:
js matchMedia('(prefers-color-scheme: dark)'). addEventListener('change', e=>{
if(localStorage. getItem('theme')==='system'){
document. documentElement. setAttribute('data-theme', e. matches? 'dark': 'light');
}
});
5) Transizioni fluide senza FOUC
Applicare l'argomento prima di scaricare lo script CSS (inline).
Le animazioni sono brevi e solo «color/background/border-color» (120-200 mc), ma non al primo rendering:css
@media (prefers-reduced-motion: no-preference){
html. theme-ready { transition: color. 18s, background-color. 18s, border-color. 18s; }
}
Dopo aver montato l'applicazione, aggiungete "class =" theme-ready ".
6) Componenti e stati
Testo/icone - Contrasto dell'AA; testo secondario non inferiore a 4. 5:1 (in dark è facile «sfumare»).
Campi/carte: sfondo --bg-elev, limite --border, ombra ---shadow.
CTA: sfondo --accent, testo a contrasto ('# fff' o calcolato).
Stati (hover/focus/active/disabled) - Modificare la luminosità/alfa anziché «trasfusionare l'arcobaleno».
Grafici/sparcline: pannelli separati per light/dark; griglia/assi a basso contrasto, ma leggibili.
7) Immagini/media/logo
Le icone sono monocromatiche attraverso «currentColor» (adattate al testo).
Non invertire i loghi dei marchi; Preparare due versioni (light/dark).
Poster/screenshot: overlay leggero in dark (8-12%) per la leggibilità dei testi.
SVG: evita i riempimenti «rigidi», usa il vars'var (--fg) '/' var (--accent) '.
8) Disponibilità
Contrasto elevato: preset separato dì data-theme =» hc».
Anelli attivi: sempre visibili ('outline: 2px solid var (--focus); outline-offset: 2px`).
Non si fidi del colore. Icone/testo/pattern per gli stati.
Caratteri: 'font-variant-numeric: tabula-nums;' per le somme/koff.
RTL - Il tema non rompe il mirroring (usiamo le proprietà logiche).
9) Performance
Colori - CSS custom properties alla radice per riutilizzare istantaneamente senza riutilizzare i componenti.
Evitare di ritrasmettere le immagini con filtri'invert () 'sui contenitori di grandi dimensioni.
Sostituzione pigra di immagini pesanti per un argomento (se necessario).
Non salvare le tavolozze più grandi in JS - l'argomento è controllato da una classe/attributo.
10) Specificità del iGaming
I live cooff di notte sono il contrasto «morbido» (AAA per i numeri), l'evidenziazione della variazione del coefficiente è discreta, senza misurazioni.
Gioco responsabile: avvisi e suggerimenti sono leggibili su entrambi i temi; Niente fiori velenosi di notte.
Cassa: campi/firme/errori senza accenti scintillanti; successo/errore sono stabili per argomento.
Skin tornei: solo come accent-override di superficie, non rompere i token di base.
11) Snippet UI
Pulsante di scelta (HTML/JS):html
<label class="theme-switch">
<span> Topic </span>
<select id="theme">
<option value = "system "> System </option>
<option value = "light "> Bright </option>
<option value = "dark "> Dark </option>
<option value = "hc"> High contrast </option>
</select>
</label>
<script>
const sel = document. getElementById('theme');
sel. value = localStorage. getItem('theme') 'system';
sel. addEventListener('change', e=>{
const v = e. target. value;
localStorage. setItem('theme', v);
if(v==='system'){
const sys = matchMedia('(prefers-color-scheme: dark)'). matches? 'dark':'light';
document. documentElement. setAttribute('data-theme', sys);
} else {
document. documentElement. setAttribute('data-theme', v);
}
});
</script>
Preset componenti:
css
.btn{height:44px; padding:0 16px; border-radius:var(--radius); display:inline-flex; align-items:center; gap:8px}
.btn--primary{background:var(--accent); color:#fff}
.card{background:var(--bg-elev); border:1px solid var(--border); box-shadow:var(--shadow); border-radius:var(--radius)}
.text-muted{color:var(--muted)}
React hook (persist + system):
tsx import { useEffect, useState } from 'react';
type Theme = 'light' 'dark' 'hc' 'system';
export function useTheme(){
const [theme, setTheme] = useState<Theme>(()=>localStorage. getItem('theme') as Theme 'system');
useEffect(()=>{
const apply = (t:Theme)=>{
const v = t==='system'? (matchMedia('(prefers-color-scheme: dark)'). matches? 'dark':'light'): t;
document. documentElement. setAttribute('data-theme', v);
};
apply(theme);
const mql = matchMedia('(prefers-color-scheme: dark)');
const on = ()=> theme==='system' && apply('system');
mql. addEventListener('change', on);
return ()=> mql. removeEventListener('change', on);
},[theme]);
useEffect(()=> localStorage. setItem('theme', theme),[theme]);
return { theme, setTheme };
}
12) Metriche
Theme adoption rate: percentuale di utenti su Dark/System/HC.
FUC rate - Quota con «picco bianco» visibile all'avvio (<1%).
Contrast defetts - Numero di contrasti di rilascio.
Errore visibility: click/ripetizioni a causa di errori «invisibili» in argomenti diversi.
Energy impact (mobile) - Confronta l'ora della sessione in dark vs lite (metrica indiretta).
13) Anti-pattern
Inverti tutto «filter: invert (1)» - Rompe il marchio e i significati.
Cambia i colori direttamente nei componenti senza token.
Nascondi gli anelli focali in un argomento oscuro.
Testo troppo scuro su sfondo scuro (o chiaro su sfondo chiaro).
Transizione lunga per l'intera pagina (sottoforma).
Colori di stato esclusivi in un argomento che non sono nell'altro.
14) Foglio di assegno QA
Contrasto e leggibilità
- Tutti i testi dell'AA; etichette critiche/testo piccolo AAA.
- Gli errori/i successi/gli avvisi non sono solo un colore.
Comportamento
- Sistema rispetta «preferers-color-scheme» e risponde al cambio di sistema operativo.
- Nessun FOUC (oggetto applicabile al render).
- Il cambio di argomento non ripristina lo stato delle pagine.
Componenti
- Le schede/moduli/tabelle utilizzano solo token.
- I grafici hanno palette per entrambi i temi.
- I loghi/icone sono visibili correttamente in entrambi i temi.
A11y
- Focus-ring visibile; La modalità High-Contrast è disponibile.
- Considerato'preferers-reduced-motion '.
Performance
- Transizione da 200 ms; nessun reflow globale.
- Nessun filtro/bland pesante nei contenitori.
15) Documentazione in progettazione
Theme tokens: tavolozza, contrasti, stato (hover/focus/active/disabled).
Guides - Come aggiungere un nuovo accento brando senza regressione del contrasto.
Charts/Media - Pannelli preconfigurati per light/dark.
Patterns: System-first, High-Contrast, failover senza FOUC.
Do/Don't: inversione filtro, colori inline, errori invisibili/fuoco.
Breve riepilogo
L'interruttore di lavoro del tema è un tocco semantico + System-first + avvio senza misura. Fissa il contrasto, centralizza i colori, rispetta «preferers-color-scheme» e reduce-motion, conserva la scelta dell'utente ed evita gli effetti pesanti. La UI rimane quindi leggibile e riconoscibile in qualsiasi ambiente, dalla partita di lave notturna alla biglietteria diurna e agli schermi del torneo.