Przełącznik tematyczny interfejsu
1) Zasady
1. System> skóra. Tematem jest nie tylko inwersja tła, ale zestaw żetonów (kolor, tło, kontrast, cienie, stany, ilustracje, wykresy).
2. System pierwszy. Domyślnie - System ('preferuje kolor-schemat') z możliwością ręcznego wybrania Light/Dark/High-Contrast.
3. Domyślny kontrast. Cel - WCAG AA, dla małych tekstów/ważnych etykiet - AAA.
4. Żadnych ognisk. Stosujemy motyw przed renderowaniem (skrypt inline) i wykonujemy przejścia ostrożnie.
5. Stabilność marki. Akcenty i semantyka statusów są zachowane we wszystkich tematach.
2) Tryby i scenariusze
Lekkie skrypty/formularze płatności/długie odczyty.
Ciemność - wieczór/niskie światło/mecze na żywo; zmniejsza odblaski.
System - postępuj zgodnie z systemem operacyjnym/przeglądarką („preferuje kolor-schemat”).
Wysoki kontrast - zwiększony kontrast i zminimalizowana biżuteria (w tym zmniejszyć ruch).
Sezonowe/Promo (opcjonalne) - nad podstawowym tematem turnieju/wydarzenia (nie łamie żetonów).
3) Architektura tokenów
Przechowujemy tokeny semantyczne, nie bezpośrednie kolory: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;
}
Zasada: Elementy używają tylko tokenów semantycznych.
4) Detektor i zapisz wybór
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>
Przełącznik interfejsu użytkownika: 'Light/Dark/System/High-Contrast'. Jeśli wybierzesz 'System', nie przechowuj określonego koloru, tylko flagi. Posłuchaj zmian w systemie operacyjnym:
js matchMedia('(prefers-color-scheme: dark)'). addEventListener('change', e=>{
if(localStorage. getItem('theme')==='system'){
document. documentElement. setAttribute('data-theme', e. matches? 'dark': 'light');
}
});
5) Płynne przejścia bez FOUC
Zastosuj motyw przed załadowaniem CSS (skrypt inline).
Animacje tematyczne są krótkie i tylko 'kolor/tło/kolor granicy' (120-200 ms), ale nie w pierwszym renderze:css
@media (prefers-reduced-motion: no-preference){
html. theme-ready { transition: color. 18s, background-color. 18s, border-color. 18s; }
}
Po zamontowaniu aplikacji dodaj 'class =' theme-ready ''.
6) Składniki i stany
Tekst/ikony: kontrast ≥ AA; tekst drugorzędny nie niższy niż 4. 5:1 (łatwo „zanika” w ciemności).
Pola/karty: tło '--bg-elev', granica '--border', cień '--shadow'.
CTA: tło '--accent', tekst kontrastu ('# fff' lub obliczony).
Stany (hover/focus/active/disabled): Zmień jasność/alfa, a nie „blask tęczy”.
Grafika/sparkliny: oddzielne palety do światła/ciemności; siatka/osie są niski kontrast, ale czytelne.
7) Obrazy/Media/Logos
Ikony monochromatyczne - za pomocą ' Color' (dostosuj się do tekstu).
Nie odwracaj logo marki; Przygotować dwie wersje (jasne/ciemne).
Plakaty/zrzuty ekranu: łatwa nakładka w ciemności (8-12%) dla czytelności tekstów.
SVG: unikać „twardych” wypełnień, używać var 'var (--fg) '/' var (--accent)'.
8) Przystępność cenowa
Wysoki kontrast: oddzielny wstępny 'data-theme =' hc'.
Pierścienie ostrości: zawsze widoczne ('zarys: 2px lite var (--focus); outline-offset: 2px ').
Nie polegaj na kolorze. Ikona/tekst/wzór dla statusów.
Czcionki: „font-variant-numeric: tabular-nums;” dla sum/czynników.
RTL: motyw nie łamie lustra (używamy właściwości logicznych).
9) Wydajność
Kolory - właściwości niestandardowe CSS u źródła → natychmiastowe ponowne użycie bez zmiany nazwy komponentu.
Unikaj powielania obrazów z filtrami 'inwertuj ()' na dużych pojemnikach.
Leniwe zastąpienie ciężkich ilustracji tematu (w razie potrzeby).
Nie przechowywać dużych palet w JS - motyw jest kontrolowany przez klasę/atrybut.
10) Specyfika iGaming
Czynniki żywe w nocy: „miękki” kontrast (AAA dla liczb), podkreślając zmianę czynnika jest dyskretny, bez migotania.
Odpowiedzialna gra: przypomnienia i zapytania są czytelne w obu tematach; bez „trujących” kwiatów w nocy.
Skrzynka: pola/podpisy/błędy bez męczących świecących akcentów; sukces/błąd są stabilne w temacie.
Turniej „skórki”: tylko jako powierzchowny akcent nadrzędny, nie łamać podstawowych żetonów.
11) Snippets UI
Przełącznik (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>
Ustawienia wstępne komponentów:
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)}
Hak reakcji (utrzymać + 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) Metryka
Wskaźnik adopcji tematu: udział użytkowników na Dark/System/HC.
Wskaźnik FOUC: odsetek z widocznym „białym wybuchem” na początku (<1%).
Wady kontrastu: liczba błędów kontrastu przez zwolnienie.
Widoczność błędów: kliknięcia/powtórzenia z powodu „niewidocznych” błędów w różnych tematach.
Wpływ energii (mobilny): porównanie czasu sesji w ciemnym vs lait (metryka pośrednia).
13) Anty-wzory
Odwróć wszystkie 'filtr: inwertuj (1)' - łamie markę i znaczenia.
Zmień kolory bezpośrednio w elementach bez żetonów.
Ukryj pierścienie ostrości w ciemnym motywie.
Tekst jest zbyt ciemny na ciemnym tle (lub światło na tle światła).
Długie przejście na całą stronę (spowolnienia).
„Ekskluzywne” kolory statusu w jednym temacie, które nie są w innym.
14) Lista kontrolna QA
Kontrast i czytelność
- Wszystkie AA ≥ teksty; etykiety krytyczne/mały tekst ≥ AAA.
- Błędy/sukces/ostrzeżenia są nie tylko zauważalne w kolorze.
Zachowanie
- System szanuje „preferuje kolorystykę” i reaguje na zmiany systemu operacyjnego.
- Brak FOUC (motyw ma zastosowanie przed utylizacją).
- Przełączanie tematu nie zresetuje stanu strony.
Składniki
- Karty/formularze/tabele używają tylko żetonów.
- Grafika posiada palety dla obu tematów.
- Logo/ikony są prawidłowo widoczne w obu tematach.
A11y
- Widoczny pierścień ostrości; Tryb High-Contrast jest dostępny.
- Należy rozważyć „zredukowany ruch”.
Wydajność
- Przejście ≤ 200 ms; bez globalnych refleksów.
- Brak ciężkich filtrów/mieszanek na pojemnikach.
15) Dokumentacja w systemie projektowym
Żetony tematyczne: palety, kontrasty, stan (hover/focus/active/disabled).
Przewodniki: Jak dodać nowy akcent brendo bez regresji kontrastu.
Wykresy/Media: wstępnie zdefiniowane palety dla światła/ciemności.
Wzory: System-first, High-Contrast, gładkie przełączanie bez FOUC.
Nie rób/Nie: inwersja filtra, kolory inline, niewidoczne błędy/ostrość.
Krótkie podsumowanie
Roboczy przełącznik tematyczny to tokeny semantyczne + System-first + start migotania. Uchwycić kontrast, scentralizować kolory, szacunek „preferuje kolor-schemat” i zmniejszyć ruch, przechowywać wybór użytkownika i unikać ciężkich efektów. Następnie interfejs użytkownika pozostaje czytelny i rozpoznawalny w każdym ustawieniu - od meczu na żywo do kasy dziennej i ekranów turniejowych.