Pular para o conteúdo principal

N2-06 · Sobreposições responsáveis

Contexto

O pipeline do Aplica DS é sofisticado — mas não é onisciente. Haverá casos onde o comportamento gerado não é exatamente o que uma marca precisa. O tom neutro ficou frio demais. O cinza gerado não combina com a identidade quente da marca.

Overrides existem para esses casos. Mas eles têm um custo — não em tokens, mas em manutenibilidade — e um protocolo que deve ser seguido.


Conceito

O que são overrides

Overrides são configurações no *.config.mjs que substituem valores gerados pelo pipeline padrão. Você está dizendo ao engine: "Para este caso específico, ignore o valor calculado e use este."

O engine aceita overrides em dois pontos:

1. Grayscale — a escala de cinza fixa Por padrão, o grayscale é um cinza neutro. Marcas com identidade quente (como marcas de moda, alimentação, lifestyle) frequentemente precisam de cinzas levemente amarelados ou rosados para manter a coerência visual.

overrides: {
grayscale: {
light: {
surface: {
'5': '#faf8f5', // off-white com tom quente
'10': '#f5f2ee',
'140': '#1a1814' // quase-preto com tom quente
}
},
dark: { /* mesma estrutura */ }
}
}

2. Neutrals — os cinzas derivados da cor de marca Por padrão, os neutrals são calculados com 10% de croma da cor de marca. Às vezes esse cálculo não produz o resultado visual desejado.

overrides: {
neutrals: {
brand_principal: {
light: { surface: { '5': '#fdf4f9' } } // apenas o nível que precisa
}
}
}

3. Interaction states — substituição de estados de interação específicos A partir da v3.20, o leaf de override de interação aceita cinco campos. Cada um afeta uma parte diferente do visual do estado:

CampoO que substituiChecagem de contraste
backgroundA surface do estado diretamente pelo hex declarado. txtOn é auto-computado com WCAG AA e border é derivado a partir desse hex.txtOn auto sempre passa AA
colorA cor-fonte do estado, que passa pelo pipeline de dilução completo. A surface é diluída — não é o hex bruto.derivado pela estratégia do tema
txtOnO texto/ícone sobre a surface. Só é aplicado se o valor passar WCAG AA (4.5:1). Se reprovar, o engine emite [override:accessibility] com o ratio exato e preserva o valor auto-computado.enforced
txtO texto de leitura ambiente sobre o canvas da página. Só relevante quando generateTxt: true está ativo no workspace.enforced
borderA cor de borda do estado. Aplicado incondicionalmente, sem checagem de contraste.nenhuma

Cada leaf deve declarar pelo menos um campo — um leaf vazio causa erro em parse time. Estados válidos: normal, action, active, focus.

overrides: {
interaction: {
function: {
items: {
primary: {
states: {
active: {
// Cor de marca diretamente no estado active — txtOn auto-computado WCAG AA
solid: { background: '#C73C9E' }
},
action: {
// Redirecionar via pipeline de dilução — surface é diluída, não o hex bruto
solid: { color: '#0067FF' }
},
focus: {
// Só borda — sem alterar surface ou texto
solid: { border: '#8B5CF6' }
}
}
}
}
}
}
}

A partir da v3.21: quando apenas background é declarado sem txtOn explícito, o txtOn é derivado da paleta gerada a partir do próprio hex declarado — não mais da paleta da cor original do item. Se precisar de um tom específico, declare txtOn explicitamente.

Formato por modo (desde v3.22.0):

Preset leaves agora aceitam valores diferentes para light e dark mode:

active: {
// Global — mesmo valor em ambos os modos (comportamento anterior, sem mudança)
solid: { background: '#C73C9E' }
}

active: {
// Modo-específico duplo — cor diferente por modo
solid: {
light: { background: '#C73C9E' },
dark: { background: '#AD2385' }
}
}

active: {
// Modo-específico parcial — light explícito, dark auto-gerado
solid: {
light: { background: '#C73C9E' }
}
}

Regra de detecção: se o preset contiver chave light ou dark, é modo-específico. Do contrário, é global. Todos os configs existentes continuam funcionando sem mudança.

O ciclo obrigatório: Estudar → Testar → Documentar

Antes de aplicar qualquer override, siga as três etapas:

Estudar: Por que o valor padrão não serve? O problema é real ou é preferência estética? Às vezes o que parece errado é correto para WCAG e o problema está em outra camada.

Testar: Aplique o override em um ambiente isolado. Verifique o comportamento em light e dark mode. Confirme que o contraste ainda é adequado em todos os contextos onde o valor é usado.

Documentar: Registre no config (via comentário) ou no CHANGELOG do tema: qual era o problema, qual foi a solução, quando foi aplicado. Overrides sem documentação se tornam misterios impossíveis de resolver meses depois.


O que NUNCA sobrepor

Tokens Semantic diretamente. A camada Semantic é gerada pelo sync:architecture — qualquer edição manual em data/semantic/default.json é sobrescrita na próxima execução. Se você precisa mudar o comportamento de um token Semantic, a mudança precisa acontecer no schema ou no config do tema, não no arquivo gerado.

Valores calculados de txtOn sem considerar o contraste. O txtOn é calculado para garantir WCAG AA. Se você configurar um override de txtOn e ele reprovar o contraste mínimo (4.5:1), o engine emite o warning nomeado [override:accessibility] com o ratio exato e a surface, e preserva o valor auto-computado — o override configurado é ignorado silenciosamente substituído pelo valor seguro. Se você precisa de uma estratégia diferente, use txtOnStrategy: 'brand-tint' ou 'custom-tint' no config.

⚠ [override:accessibility] action_primary.function.ghost.action.txtOn:
'#010101' fails WCAG AA (1.05:1) on surface #2d1a0e [dark] — skipped, auto-computed preserved.

Exemplo guiado

Três cenários: override correto, desnecessário e problemático


Cenário A — Override legítimo

Marca de alimentação artesanal. Identidade quente, rústica. O grayscale padrão (cinza frio) destoa visualmente de toda a paleta.

Solução:

overrides: {
grayscale: {
light: {
surface: {
'5': '#faf9f7', // branco levemente amarelado
'10': '#f5f3f0',
'20': '#ebe8e3',
'140':'#1f1d1a' // preto levemente amarronzado
}
}
}
}

Documentação no config:

// Override de grayscale: identidade quente da marca exige cinzas com tom
// de 2-3% de amarelo/laranja. Aprovado por: DS team, 2026-03.

Cenário B — Override desnecessário

Um designer achou que o nível 50 da palette primária ficou "diferente do que esperava no Figma". Quer sobrepor o surface do nível 50.

Análise:

  • O hex gerado pelo pipeline em OKLCh pode ser levemente diferente do hex declarado — isso é esperado e correto.
  • Se o contraste está correto e a paleta é harmônica, a diferença de 1-2 dígitos no hex não é um problema.
  • Sobrepor para "igualar ao Figma" vai criar um nível que destoa da curva de luminosidade da paleta inteira.

Decisão: Não aplicar o override. Atualizar o Figma para aceitar o valor gerado como canônico.


Cenário C — Override problemático

Um engenheiro quer que o txtOn do nível 100 da cor primária seja sempre preto (para manter o estilo da marca) mesmo que o branco tenha mais contraste.

Análise:

  • txtOn garante WCAG AA. Forçar preto sobre um fundo escuro pode reprovar o contraste.
  • Se a marca quer manter a identidade de cor no texto, a solução é txtOnStrategy: 'brand-tint' — o engine vai escolher o tom de marca que passa WCAG.

Decisão: Usar txtOnStrategy: 'brand-tint' em vez de override manual. Documentar se mesmo assim não atender.


Agora você tenta

Dado o cenário abaixo, decida se um override é necessário e, se sim, qual é o correto:

Marca de finanças pessoais. Paleta primária: azul #2563EB. O neutral gerado automaticamente tem um tom azulado muito perceptível — em um app de finanças, onde a confiança é visual, o neutral quase-azul parece "pouco sério". O time de branding quer neutrals mais próximos de cinza puro.

Resultado esperado:

Override legítimo. O croma de 10% aplicado ao azul vibrante resulta em um neutral com aparência mais azulada do que desejado para uma marca de finanças sérias.

Solução:

overrides: {
neutrals: {
brand_principal: { // chave correspondente ao hex #2563EB no config
light: {
surface: {
'5': '#f8f9fa', // cinza quase-puro
'20': '#e8eaed',
'60': '#9aa0a6',
'100': '#5f6368',
'140': '#1c1c1e'
}
}
}
}
}
// Documentação: neutrals do azul primário redesenhados para cinzas
// mais neutros. Croma reduzido para ~2% vs padrão 10%.
// Motivo: identidade de marca financeira exige sobriedade visual.

Checkpoint

Ao fim deste tutorial você deve saber:

  • Em quais pontos o engine aceita overrides (grayscale, neutrals, interaction states)
  • O ciclo Estudar → Testar → Documentar antes de qualquer override
  • Por que nunca editar data/semantic/ manualmente
  • A diferença entre color e background no leaf de override de interação
  • Como o warning [override:accessibility] funciona — e por que o engine preserva o valor seguro
  • Quando usar o formato por modo (light/dark no preset) versus o formato global
  • Distinguir um override legítimo de um desnecessário e de um problemático

Próximo passo

Você concluiu o N2. Você entende como o sistema foi construído e pode governá-lo com responsabilidade.

Se quiser aprofundar em como configurar um tema completo do zero, continue com N2-04 · Anatomia de um tema.

Se quiser avançar para construção técnica em código, comece o N3 por N3-01 · O contrato de tokens.


Referências