Pular para o conteúdo principal

Variantes e Estados de Componentes

Premissa

Cada componente existe em múltiplas variantes (primary, secondary, ghost…) e múltiplos estados (normal, hover, active, focus, disabled). O sistema de tokens trata variantes e estados como dimensões independentes e previsíveis — não como combinações ad hoc.

Este documento mostra como mapear cada combinação de variante × estado para o token correto, e como aplicar esse padrão a qualquer componente do DS.


O Modelo: Variante × Estado × Propriedade

A estrutura de tokens de interface segue um padrão de três eixos:

semantic.color.interface.function.{variante}.{estado}.{propriedade}
EixoValores disponíveis
Varianteprimary, secondary, link, active, disabled
Estadonormal, action, active (disabled só tem normal)
Propriedadebackground, txtOn, border, txt (desde 3.6.0)

Esses três eixos formam a grade de tokens funcionais. Para qualquer combinação de variante e estado, o token correspondente já existe no build.


Estados — Mapeamento para CSS

Estado do tokenTrigger no CSS
normalEstado padrão — sem pseudo-classe
action:hover, :focus-visible (durante o feedback visual de interação)
active:active (pressed), [aria-selected="true"], [aria-pressed="true"]

O estado disabled é um caso especial: só existe normal e nunca responde a :hover ou :active.


Variantes de Função — Botões e Controles

Tabela de tokens por variante

VarianteQuando usar
primaryCTA principal — a ação mais importante da tela
secondaryAção de suporte — complementa o primário
linkAção textual — baixa proeminência, navegacional
activeControle atualmente selecionado (toggle, tab ativa)
disabledControle não disponível para interação

Exemplo: Botão com todos os estados

/* ── Botão Primary ── */
.btn-primary {
background: var(--semantic-color-interface-function-primary-normal-background);
color: var(--semantic-color-interface-function-primary-normal-txt-on);
border: 1px solid var(--semantic-color-interface-function-primary-normal-border);
}

.btn-primary:hover,
.btn-primary:focus-visible {
background: var(--semantic-color-interface-function-primary-action-background);
color: var(--semantic-color-interface-function-primary-action-txt-on);
border: 1px solid var(--semantic-color-interface-function-primary-action-border);
}

.btn-primary:active,
.btn-primary[aria-pressed="true"] {
background: var(--semantic-color-interface-function-primary-active-background);
color: var(--semantic-color-interface-function-primary-active-txt-on);
border: 1px solid var(--semantic-color-interface-function-primary-active-border);
}

/* ── Botão Disabled ── */
.btn-primary:disabled,
.btn-primary[aria-disabled="true"] {
background: var(--semantic-color-interface-function-disabled-normal-background);
color: var(--semantic-color-interface-function-disabled-normal-txt-on);
border: 1px solid var(--semantic-color-interface-function-disabled-normal-border);
cursor: not-allowed;
}

Exemplo: Botão Secondary

.btn-secondary {
background: var(--semantic-color-interface-function-secondary-normal-background);
color: var(--semantic-color-interface-function-secondary-normal-txt-on);
border: 1px solid var(--semantic-color-interface-function-secondary-normal-border);
}

.btn-secondary:hover {
background: var(--semantic-color-interface-function-secondary-action-background);
}

.btn-secondary:active {
background: var(--semantic-color-interface-function-secondary-active-background);
}

Variantes de Feedback — Alerts, Banners, Toasts

Componentes de feedback seguem o padrão {tipo}.{variante}.{estado}:

semantic.color.interface.feedback.{tipo}.{variante}.{estado}.{propriedade}
SegmentoValores
{tipo}info, success, warning, danger
{variante}default (suave, para backgrounds), secondary (saturada, para bordas e ícones)
{estado}normal, action, active

Exemplo: Alert com todos os tipos

/* Alert genérico */
.alert {
padding: var(--semantic-dimension-spacing-small);
border-radius: var(--semantic-border-radii-small);
border-left: 4px solid;
}

/* Info */
.alert--info {
background: var(--semantic-color-interface-feedback-info-default-normal-background);
color: var(--semantic-color-interface-feedback-info-default-normal-txt-on);
border-color: var(--semantic-color-interface-feedback-info-secondary-normal-border);
}

/* Success */
.alert--success {
background: var(--semantic-color-interface-feedback-success-default-normal-background);
color: var(--semantic-color-interface-feedback-success-default-normal-txt-on);
border-color: var(--semantic-color-interface-feedback-success-secondary-normal-border);
}

/* Warning */
.alert--warning {
background: var(--semantic-color-interface-feedback-warning-default-normal-background);
color: var(--semantic-color-interface-feedback-warning-default-normal-txt-on);
border-color: var(--semantic-color-interface-feedback-warning-secondary-normal-border);
}

/* Danger */
.alert--danger {
background: var(--semantic-color-interface-feedback-danger-default-normal-background);
color: var(--semantic-color-interface-feedback-danger-default-normal-txt-on);
border-color: var(--semantic-color-interface-feedback-danger-secondary-normal-border);
}

Regra: sempre use default para o fundo e secondary para borda e ícone — essa combinação garante hierarquia visual e contraste adequado.


Variantes de Input — Estados de Validação

Inputs têm um estado de validação que se sobrepõe ao estado interativo:

/* Input — estado normal */
.input {
background: var(--semantic-color-brand-ambient-contrast-deep-positive-background);
color: var(--semantic-color-text-body);
border: 1px solid var(--semantic-color-brand-ambient-neutral-mid-border);
border-radius: var(--semantic-border-radii-extra-small);
padding: var(--semantic-dimension-spacing-extra-small) var(--semantic-dimension-spacing-small);
}

/* Input — focus */
.input:focus-visible {
border-color: var(--semantic-color-interface-function-primary-action-border);
outline: none;
}

/* Input — erro */
.input--error {
border-color: var(--semantic-color-interface-feedback-danger-secondary-normal-border);
}

/* Mensagem de erro abaixo do input */
.input__error-msg {
color: var(--semantic-color-text-danger_default);
font-size: var(--semantic-typography-font-sizes-extra-small);
}

/* Input — sucesso */
.input--success {
border-color: var(--semantic-color-interface-feedback-success-secondary-normal-border);
}

/* Input — desabilitado */
.input:disabled {
background: var(--semantic-color-interface-function-disabled-normal-background);
color: var(--semantic-color-interface-function-disabled-normal-txt-on);
border-color: var(--semantic-color-interface-function-disabled-normal-border);
cursor: not-allowed;
}

Variantes de Intensidade — Componentes de Marca

Para componentes que expressam a identidade de marca (badges, chips, tags de marca), use os níveis de intensidade do brand.branding:

semantic.color.brand.branding.{papel}.{intensidade}.{propriedade}
IntensidadeVisualUso típico
lowestMuito sutilBackground de chip, tag em contexto denso
lowSuaveBackground de badge secundário
defaultPadrão da marcaCTA, botão hero, elemento principal
highSaturado/escuroTexto de ênfase sobre fundo claro
highestMáximoRaro — elemento de altíssima proeminência
/* Chip de marca — suave */
.chip--brand {
background: var(--semantic-color-brand-branding-first-lowest-background);
color: var(--semantic-color-brand-branding-first-lowest-txt-on);
border: 1px solid var(--semantic-color-brand-branding-first-low-border);
}

/* Badge de marca — destaque */
.badge--brand {
background: var(--semantic-color-brand-branding-first-default-background);
color: var(--semantic-color-brand-branding-first-default-txt-on);
}

Variantes de Tamanho — Dimensão por Escala

Variantes de tamanho (small, medium, large) de um componente mapeiam para a escala de dimensão semântica. A convenção mais comum:

Tamanho do componenteSpacing internoSizing de altura
xsmicroextraSmall
smextraSmallsmall
mdsmallmedium
lgmediumlarge
xllargeextraLarge
/* Botão medium (padrão) */
.btn--md {
padding: var(--semantic-dimension-spacing-extra-small) var(--semantic-dimension-spacing-small);
height: var(--semantic-dimension-sizing-large);
font-size: var(--semantic-typography-font-sizes-small);
border-radius: var(--semantic-border-radii-small);
}

/* Botão small */
.btn--sm {
padding: var(--semantic-dimension-spacing-micro) var(--semantic-dimension-spacing-extra-small);
height: var(--semantic-dimension-sizing-medium);
font-size: var(--semantic-typography-font-sizes-extra-small);
border-radius: var(--semantic-border-radii-extra-small);
}

/* Botão large */
.btn--lg {
padding: var(--semantic-dimension-spacing-small) var(--semantic-dimension-spacing-medium);
height: var(--semantic-dimension-sizing-extra-large);
font-size: var(--semantic-typography-font-sizes-medium);
border-radius: var(--semantic-border-radii-medium);
}

O Estado Disabled — Dois Padrões

Há duas formas válidas de implementar o estado disabled; a escolha depende do tipo de componente.

Padrão 1 — Token de disabled (preferido para botões e controles)

Use os tokens interface.function.disabled — eles garantem o contraste correto sobre o background atual:

.btn:disabled {
background: var(--semantic-color-interface-function-disabled-normal-background);
color: var(--semantic-color-interface-function-disabled-normal-txt-on);
border-color: var(--semantic-color-interface-function-disabled-normal-border);
}

Padrão 2 — Opacidade (para conteúdo e regiões inteiras)

Quando uma seção inteira está desabilitada e não faz sentido trocar cada token individual:

.section--disabled {
opacity: calc(var(--semantic-opacity-raw-translucid) / 100); /* 50% */
pointer-events: none;
}

Atenção: Opacidade sozinha não garante contraste suficiente sobre todos os fundos. Use com cuidado em texto pequeno. Para controles individuais, sempre prefira o Padrão 1.


Focus Ring — Acessibilidade por Token

O anel de foco é um elemento de acessibilidade obrigatório (WCAG 2.4.7). Use a borda da variante action do papel correspondente:

/* Focus ring genérico */
:focus-visible {
outline: 2px solid var(--semantic-color-interface-function-primary-action-border);
outline-offset: 2px;
}

/* Focus em elemento de feedback (ex: alerta clicável) */
.alert:focus-visible {
outline-color: var(--semantic-color-interface-feedback-info-secondary-normal-border);
}

Variantes de Produto — Badges e Selos

Componentes de sinalização de produto (badges de promo, selos de cashback, indicadores de tier) usam semantic.color.product.*:

/* Badge de promoção */
.badge--promo {
background: var(--semantic-color-product-promo-default-default-background);
color: var(--semantic-color-product-promo-default-default-txt-on);
border: 1px solid var(--semantic-color-product-promo-default-default-border);
}

/* Variante sutil de promo (menor proeminência) */
.badge--promo-subtle {
background: var(--semantic-color-product-promo-default-lowest-background);
color: var(--semantic-color-product-promo-default-lowest-txt-on);
}

/* Badge de cashback */
.badge--cashback {
background: var(--semantic-color-product-cashback-default-default-background);
color: var(--semantic-color-product-cashback-default-default-txt-on);
}

Componente de Referência — Mapa Completo (Button)

A tabela abaixo mostra o mapeamento completo de tokens para um componente Button com 3 variantes e 4 estados:

VarianteEstadoBackgroundTextBorder
primarynormalfunction.primary.normal.backgroundfunction.primary.normal.txtOnfunction.primary.normal.border
primaryhoverfunction.primary.action.backgroundfunction.primary.action.txtOnfunction.primary.action.border
primarypressedfunction.primary.active.backgroundfunction.primary.active.txtOnfunction.primary.active.border
primarydisabledfunction.disabled.normal.backgroundfunction.disabled.normal.txtOnfunction.disabled.normal.border
secondarynormalfunction.secondary.normal.backgroundfunction.secondary.normal.txtOnfunction.secondary.normal.border
secondaryhoverfunction.secondary.action.backgroundfunction.secondary.action.txtOnfunction.secondary.action.border
secondarypressedfunction.secondary.active.backgroundfunction.secondary.active.txtOnfunction.secondary.active.border
secondarydisabledfunction.disabled.normal.backgroundfunction.disabled.normal.txtOnfunction.disabled.normal.border
linknormaltransparentefunction.link.normal.txtOnnone
linkhoverfunction.link.action.backgroundfunction.link.action.txtOnnone
linkpressedfunction.link.active.backgroundfunction.link.active.txtOnnone
linkdisabledfunction.disabled.normal.backgroundfunction.disabled.normal.txtOnfunction.disabled.normal.border

Todos os caminhos acima são prefixados com semantic.color.interface..


Referências