Botão de assunto da interface
1) Princípios
1. Sistema> skin. O tema não é apenas uma inversão de fundo, mas um conjunto de tokens (cor, fundo, contraste, sombras, estados, ilustrações, gráficos).
2. System-first. O padrão é System ('preferers-cores-scheme'), com a opção manual Light/Dark/High-Contrast.
3. Contraste padrão. O objetivo é WCAG AA, para texto pequeno/marcas importantes - AAA.
4. Não há flashes. Aplicamos o tema até o render (inline-script) e fazemos as transições com cuidado.
5. Estabilidade da marca. O sotaque e a semântica dos estados são preservados em todos os temas.
2) Modos e cenários
Light - cenários diurnos/formulários de pagamento/leitura longa.
Dark - noite/baixa iluminação/jogos de lave; reduz os destaques.
System - Seguindo o sistema operacional/navegador ('preferers-cor-scheme').
O High-Contrast é um contraste elevado e minimiza as joias.
Seasonal/Promo (opcional) - acima do tema de base para o torneio/ivent (não quebra os tokens).
3) Arquitetura de tokens
Guardemos os tocantes semânticos, não as cores retas: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;
}
Regra: os componentes usam apenas tocas semânticas.
4) Detector e salvação de seleção
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>
Alternador UI: 'Light/Dark/System/High-Contrast'. Se você selecionar «System», não guarde uma cor específica, apenas uma bandeira. Ouça as mudanças do sistema operacional:
js matchMedia('(prefers-color-scheme: dark)'). addEventListener('change', e=>{
if(localStorage. getItem('theme')==='system'){
document. documentElement. setAttribute('data-theme', e. matches? 'dark': 'light');
}
});
5) Transições suaves sem FOUC
Aplique o tema antes do download da CSS (inline-script).
As animações são curtas e apenas 'cor/background/border-cor' (120-200 ms), mas não na primeira renderização:css
@media (prefers-reduced-motion: no-preference){
html. theme-ready { transition: color. 18s, background-color. 18s, border-color. 18s; }
}
Após montar o aplicativo, adicione 'class =' theme-ready '.
6) Componentes e estados
Texto/ícone: contraste ≥ AA; texto secundário não inferior a 4. 5:1 (no dark é fácil «florescer»).
Campos/cartões: fundo '--bg-eleev', limite '--border', sombra '--- shadow'.
CTA: fundo '--accent', texto em contraste ('# fff' ou computável).
Estados (hover/focus/ativo/disabled): altere o brilho/alfa em vez de «transfigure o arco-íris».
Gráficos/sparline: painéis individuais para light/dark; grade/eixos de baixo contorno, mas de leitura.
7) Imagens/mídia/logotipo
Os ícones são monocromáticos através de 'currentColor' (ajustados ao texto).
Logótipos de marcas não invertam; prepare duas versões (light/dark).
Posters/screenshots: overlay leve em dark (8-12%) para leitura de textos.
SVG: Evite preenchimentos «rígidos», use vars 'var (--fg) '/' var (-accent)'.
8) Disponibilidade
Contraste alto: Um pré 'data-theme =' hc '.
Anéis de foco: sempre visíveis ('outline: 2px solid var (-focus); outline-offset: 2px`).
Não dependa da cor. Ícone/texto/pattern para estatais.
Fontes: 'fonte-variant-numérico: tabular-nums;' para somas/coffs.
O tema não quebra o espelho (usemos propriedades lógicas).
9) Performance
Cores - CSS custom properties na raiz → reutilização instantânea sem reutilização de componentes.
Evite «pintar» as imagens com filtros 'invert ()' em contêineres maiores.
Troca preguiçosa de ilustrações pesadas para um tema (se necessário).
Não guarde painéis maiores em JS - o tema é controlado por classe/atributo.
10) Especificidades iGaming
Os cooffs ao vivo à noite são «suave» (AAA para números), realce a variação do coeficiente - discreto, sem parafusos.
Um jogo responsável: lembretes e dicas são legíveis em ambos os temas; sem as cores venenosas à noite.
Caixa: campos/assinaturas/erros sem acertos exaustivos; sucesso/erro são estáveis em matéria.
«Skin» de torneio: Apenas como um accent-override superficial, não rompa os tokens básicos.
11) Snippets UI
Botão (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>
Pré-componentes:
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) Métricas
Theme adition rate: proporção de usuários em Dark/System/HC.
FOUC rate: participação com «surto branco» visível no início (<1%).
Contrast defints: número de bits de contraste de lançamento.
Erro de visibilidade: cliques/repetições devido a erros «imperceptíveis» em vários temas.
Energy impact (mobile): comparação do tempo de sessão em darke vs lite (métrica indireta).
13) Anti-pattern
Inverter tudo 'filter: invert (1)' - quebra marcas e significados.
Alterar as cores diretamente nos componentes sem tokens.
Esconder os anéis de foco em temas escuros.
Texto escuro demais em um fundo escuro (ou claro em uma luz).
Transição longa para página inteira (curvatura).
Cores exclusivas de status em um tema que não estão no outro.
14) QA-folha de cheque
Contraste e lisibilidade
- Todos os textos ≥ AA; marcas críticas/texto pequeno ≥ AAA.
- Erros/êxito/advertência não são apenas cores.
Comportamento
- O sistema respeita 'preferers-cores-scheme' e responde à mudança de sistema operacional.
- Sem FOUC (tema aplicado antes do render).
- A alteração de assunto não remove o status das páginas.
Componentes
- Os cartões/formulários/tabelas usam apenas tokens.
- Os gráficos têm painéis para ambos os temas.
- Os logos/ícones são corretamente visíveis em ambos os temas.
A11y
- Focus-ring visível; O modo High-Contrast está disponível.
- Leva em conta 'preferers-reduced-motion'.
Performance
- Transição ≤ 200 ms; sem reflow global.
- Nenhum filtro ou blend pesado nos contêineres.
15) Documentação em design
Theme tokens: painéis, contrastes, status (hover/focus/ativo/disabled).
Guides: como adicionar um novo sotaque de marca sem um contraste de regressão.
Charts/Media: painéis pré-construídos para light/dark.
Patterns: System-first, High-Contrast, mudança suave sem FOUC.
Do/Don 't: filtro invertido, cores inline, erros invisíveis/foco.
Resumo curto
O botão de trabalho é um tocador semântico + System-first + início sem efeito. Fixe o contraste, centralize as cores, respeite 'preferers-cores-scheme' e reduse-motion, guarde a escolha do usuário e evite efeitos pesados. Então a UI permanece legível e reconhecível em qualquer ambiente, desde um jogo de lave noturno até a bilheteria diurna e telas de torneio.