Theming — Deep Dive
Como o tema de uma brand é declarado, como ele chega no Tailwind, e por que cores hardcoded simplesmente não funcionam neste projeto.
O mecanismo
O tema não é CSS. É um objeto TypeScript que o tailwind.config.js lê em tempo
de build e usa pra montar a paleta do Tailwind:
app/config/theme/colors.ts ← themeColors (o que o fork edita)
│
│ resolveBrandFile(projectRoot, brandKey, "app/config/theme/colors.ts")
│ → overrides/<brand>/app/config/theme/colors.ts, se existir
▼
tailwind.config.js → theme.colors → utility classes (bg-*, text-*, border-*, …)
A brand ativa vem de ORIGIN_DOMAIN (env ou .dev.vars), traduzida em brand key
por toBrandOverrideKey(). Ou seja: trocar ORIGIN_DOMAIN troca a paleta
inteira do build, sem tocar em nenhum componente.
Três arquivos de app/config/theme/ são lidos pelo tailwind.config.js:
| Arquivo | Export | Alimenta |
|---|---|---|
colors.ts | themeColors | theme.colors (toda a paleta) |
sizes.ts | layoutConfig | maxWidth.content |
fonts.ts | themeFonts | fontFamily.sans + a custom property --font-sans |
:::danger A paleta é SUBSTITUÍDA, não estendida
O tailwind.config.js define theme.colors, não theme.extend.colors. Isso
descarta a paleta inteira do Tailwind. As únicas cores que existem no projeto são
os tokens da brand mais estas quatro:
transparent current white (#ffffff) black (#000000)
Consequência direta: bg-red-500, text-zinc-400, border-slate-200 e
companhia não compilam pra nada. Não é convenção de estilo — a classe não
existe. É exatamente por isso que a regra abaixo é absoluta.
:::
Cores hardcoded: proibidas
Nunca use cor hardcoded em componente. Sempre um token do tema.
Exceções — as únicas quatro, porque são as únicas que existem:
black, white, transparent, current.
Antes de commitar:
# não deve retornar nada de app/ nem de overrides/
grep -rnE '\b(bg|text|border|from|via|to|ring|fill|stroke)-(red|green|blue|yellow|orange|purple|pink|gray|grey|zinc|slate|neutral|stone|amber|lime|emerald|teal|cyan|sky|indigo|violet|fuchsia|rose)-[0-9]{2,3}\b' app/ overrides/
Cor nova que nenhum token cobre? Adicione um token — não uma cor literal. E propague pra todos os overrides (ver o final desta página).
Nomes de classe: como o token vira utility
Chave escalar no topo → o nome é a própria chave:
<div className="bg-bg-primary text-texts" />
<span className="text-primary" />
Chave aninhada → <seção>-<chave>:
// header: { bg, links } → header-bg, header-links
<header className="bg-header-bg text-header-links" />
// auth: { "button-bg", "button-text" }
<button className="bg-auth-button-bg text-auth-button-text" />
// sidebar: { titles }
<span className="text-sidebar-titles" />
Cada token gera o conjunto completo de utilities de cor do Tailwind —
bg-, text-, border-, from-/via-/to-, ring-, fill-, stroke-, etc.
:::caution text-primary existe, mas não significa "cor do texto principal"
primary é chave escalar, então text-primary compila — e quer dizer "texto
na cor primária da marca" (usado ~350 vezes no base, legitimamente).
A cor do texto corrido é texts (text-texts); títulos são titles;
links são links. Não existe token chamado text-primary nem
text-secondary — e também não existe namespace default-. Se você viu
default-primary ou default-text-primary em algum lugar, é referência morta.
:::
Uma pegadinha de nomenclatura: a seção bottomNotification é declarada em
camelCase no colors.ts, mas exposta ao Tailwind como
bottom-notification. Classes: bg-bottom-notification-bg,
text-bottom-notification-text, etc.
Referência de tokens
Fonte de verdade: app/config/theme/colors.ts (valores) e tailwind.config.js
(o que de fato é exposto). Se divergir do que está aqui, o código ganha.
Globais
| Token | Classe | Papel |
|---|---|---|
primary | bg-primary, text-primary | Cor principal da marca |
bg-primary | bg-bg-primary | Fundo principal da página |
secondary | bg-secondary | Cor secundária |
bg-secondary | bg-bg-secondary | Fundo secundário (cards, superfícies elevadas) |
texts | text-texts | Texto corrido |
links | text-links | Links |
titles | text-titles | Títulos |
button-bg / button-text | bg-button-bg / text-button-text | Botão padrão |
accent | text-accent | Destaque |
success | text-success | Sucesso |
money | text-money | Valores monetários de ganhos — verde "cor de dinheiro", mais amarelado que success |
error | text-error | Erro |
warning | text-warning | Aviso — também usado pro * de "dado não vem da API" |
info | text-info | Informação |
Dois tokens globais não são chaves do colors.ts — o tailwind.config.js os
deriva:
primary-foregroundé alias debutton-text. Existe só como classe.moneycai prasuccessquando a brand não declarou (themeColors.money ?? themeColors.success). O fallback existe porque override é substituição de arquivo inteira: uma brand que ainda não propagou o token não pode quebrar o build.
Seções
Onze seções são expostas ao Tailwind. Os tokens de cada uma:
| Seção (prefixo) | Tokens |
|---|---|
game | title, subtitle, button-bg, button-text, overlay, icon, online-bg, online-text, online-dot, balloon-bg, balloon-text, balloon-value, balloon-icon |
header | bg, links, texts, active-icon, promo-icon, nav-link, nav-link-active, register-bg, register-text, login-bg, login-text, login-border, register-border, deposit-bg, deposit-text, deposit-border, balance-bg, balance-text, avatar-bg, top-bg, top-text, main-bg, glow |
sidebar | bg, links, titles, icon, button-bg, button-text, cta-bg, cta-text |
footer | bg-primary, bg-secondary, links, titles, texts, button-bg, button-text, app-button-bg |
auth | bg-primary, bg-inputs, text-inputs, bg-close, icon-close, links, titles, texts, button-bg, button-text, backdrop |
payments | bg-primary, bg-inputs, text-inputs, titles, texts, button-bg, button-text |
topbar | bg, text, icon-bg, icon-text, cta-bg, cta-text |
bottom-notification | bg, border, text, icon-bg, icon-text, cta-bg, cta-text |
loader | bar, spinner |
ftd-cashback | first-bg-from, first-bg-to, first-text-accent, prize-bg-from, prize-bg-to, prize-amount-color, prize-glow-color, prize-coin-color |
ftd-offer | modal-bg-from, modal-bg-to, image-bg-from, image-bg-to, text-accent, countdown-bg, countdown-border, countdown-text, thumb-border, thumb-badge-bg, thumb-badge-text, e os tokens template-* e toast-* do template unificado de modal |
manifest não é Tailwind
colors.ts tem uma décima segunda seção, manifest (theme-color,
background-color), que não é exposta ao Tailwind — alimenta o manifest do
PWA. Não existe bg-manifest-theme-color.
Valores não-hex são permitidos onde há transparência
O docblock do colors.ts pede hex, mas alguns tokens de glow/borda usam rgba()
de propósito (ex: ftd-cashback.prize-glow-color, ftd-offer.countdown-border).
Tailwind aceita — só não tente aplicar modificador de opacidade (/50) num token
que já carrega alpha.
Custom properties
Alguns tokens também saem como CSS custom property, pra CSS puro que não passa pelo Tailwind (barra de boot, placeholders, gradientes animados):
| Property | Origem |
|---|---|
--auth-bg-inputs | auth.bg-inputs |
--auth-text-inputs | auth.text-inputs |
--color-loader-bar | loader.bar — Tailwind v3 não auto-emite --color-*, daí ser declarada à mão |
--font-sans | Stack completa de fonts.ts |
auth.bg-inputs também alimenta o boxShadow.input (o hack de
inset que mata o amarelo do autofill do Chrome).
Além das cores
Outros arquivos de app/config/theme/, todos overrideable por brand:
| Arquivo | O que controla |
|---|---|
sizes.ts | Dimensões de layout (layoutConfig), incl. contentMaxWidth → max-w-content |
fonts.ts | Família da brand + fallback com métrica ajustada |
font-preloads.ts | Quais .woff2 entram em <link rel="preload" as="font"> |
header.ts | Chrome do header (headerStyle) |
mobile-bottom-nav.ts | Chrome da barra inferior mobile (mobileBottomNavStyle) |
search-input.ts | Chrome das superfícies de busca (searchInputStyle) |
Override por brand
Espelhe o path dentro de overrides/<brand-key>/:
overrides/donald-bet-br/app/config/theme/colors.ts
:::danger Override é substituição do arquivo inteiro Não há deep merge. O arquivo da brand substitui o do base por completo.
Portanto: ao adicionar um token novo ao colors.ts do base, propague pra todos
os overrides que existem. Quem não receber vai resolver o token pra undefined
— e aí ou o build quebra (o tailwind.config.js lê themeColors.auth["bg-inputs"]
e themeColors.loader.bar diretamente) ou a classe some silenciosamente do CSS
gerado.
O fallback de money (?? themeColors.success) existe exatamente por causa
dessa armadilha — mas é exceção pontual, não a regra.
:::
Pra listar as brands que precisam receber a propagação (só as versionadas — a árvore local pode ter sobras não rastreadas):
git ls-files overrides/ | awk -F/ 'NF>2 {print $2}' | sort -u
Detalhes do mecanismo de resolução em Pontos de Customização Rápida.