Pular para o conteúdo principal

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:

ArquivoExportAlimenta
colors.tsthemeColorstheme.colors (toda a paleta)
sizes.tslayoutConfigmaxWidth.content
fonts.tsthemeFontsfontFamily.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

TokenClassePapel
primarybg-primary, text-primaryCor principal da marca
bg-primarybg-bg-primaryFundo principal da página
secondarybg-secondaryCor secundária
bg-secondarybg-bg-secondaryFundo secundário (cards, superfícies elevadas)
textstext-textsTexto corrido
linkstext-linksLinks
titlestext-titlesTítulos
button-bg / button-textbg-button-bg / text-button-textBotão padrão
accenttext-accentDestaque
successtext-successSucesso
moneytext-moneyValores monetários de ganhos — verde "cor de dinheiro", mais amarelado que success
errortext-errorErro
warningtext-warningAviso — também usado pro * de "dado não vem da API"
infotext-infoInformação

Dois tokens globais não são chaves do colors.ts — o tailwind.config.js os deriva:

  • primary-foreground é alias de button-text. Existe só como classe.
  • money cai pra success quando 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
gametitle, subtitle, button-bg, button-text, overlay, icon, online-bg, online-text, online-dot, balloon-bg, balloon-text, balloon-value, balloon-icon
headerbg, 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
sidebarbg, links, titles, icon, button-bg, button-text, cta-bg, cta-text
footerbg-primary, bg-secondary, links, titles, texts, button-bg, button-text, app-button-bg
authbg-primary, bg-inputs, text-inputs, bg-close, icon-close, links, titles, texts, button-bg, button-text, backdrop
paymentsbg-primary, bg-inputs, text-inputs, titles, texts, button-bg, button-text
topbarbg, text, icon-bg, icon-text, cta-bg, cta-text
bottom-notificationbg, border, text, icon-bg, icon-text, cta-bg, cta-text
loaderbar, spinner
ftd-cashbackfirst-bg-from, first-bg-to, first-text-accent, prize-bg-from, prize-bg-to, prize-amount-color, prize-glow-color, prize-coin-color
ftd-offermodal-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):

PropertyOrigem
--auth-bg-inputsauth.bg-inputs
--auth-text-inputsauth.text-inputs
--color-loader-barloader.bar — Tailwind v3 não auto-emite --color-*, daí ser declarada à mão
--font-sansStack 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:

ArquivoO que controla
sizes.tsDimensões de layout (layoutConfig), incl. contentMaxWidthmax-w-content
fonts.tsFamília da brand + fallback com métrica ajustada
font-preloads.tsQuais .woff2 entram em <link rel="preload" as="font">
header.tsChrome do header (headerStyle)
mobile-bottom-nav.tsChrome da barra inferior mobile (mobileBottomNavStyle)
search-input.tsChrome 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.jsthemeColors.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.