Badge

Pronto

Selo curto e estático que comunica status, contagem ou categoria. Não-clicável (ao contrário de Chip).

Usar quando

Status de processo (Em andamento, Encerrado), contador de notificação, etiqueta de categoria, indicador de prazo.

Não usar quando

Ações clicáveis (use Chip ou Button). Texto longo (use parágrafo). Múltiplos badges competindo (escolha 1 protagonista).

Variantes

Por sinal (cores funcionais do v6)
Em andamentoPrazo ativoVencidoFavorávelProtocolado
Tamanhos
PequenoMédioGrande
Com ícone (status semântico)
OnlineEm revisãoBloqueado
Counter (notificações)
Inbox3
Inbox42
Inbox99+
Inbox99+
Outline (variante mais discreta)
TagPremiumTrabalhista

Microinterações

MicrointeraçãoDisparada porComportamentoTiming
Counter popcount incrementaScale 1→1.2→1 do badge300ms cubic-bezier(0.32,0.72,0,1)
Dot pulsevariant=verde com dot (online)Dot pulsa suavemente2s ease loop
Mount fadecomponente entraopacity 0→1150ms ease

Acessibilidade

Acessibilidade — checklist

ARIA esperado
  • role="status" se badge informa estado dinâmico (ex: contador)
  • aria-label="3 notificações novas" para counter
  • Badges decorativos: nada (texto já é legível)
  • Cor NUNCA é única indicação — texto/ícone sempre presente
Notas
  • Counter > 99 → mostre "99+" para não estourar largura.
  • Cor é sinal — vermelho só para urgente, verde só para positivo.
  • NÃO use badge como botão — use Chip ou Button.
  • Em badge inline com texto, mantenha contraste mínimo 4.5:1.

Código

'use client';
import { T, TYPE, RADIUS } from '@/lib/tokens';

interface BadgeProps {
  children: React.ReactNode;
  variant?: 'neutral' | 'brass' | 'carmim' | 'verde' | 'selo';
  size?: 'sm' | 'md' | 'lg';
  outline?: boolean;
  dot?: boolean;
}

const VARIANT = {
  neutral: { bg: T.surface3, fg: T.ink2, edge: T.border },
  brass:   { bg: T.brassTint, fg: T.brassLo, edge: T.brassEdge },
  carmim:  { bg: T.carmimTint, fg: T.carmim, edge: T.carmimEdge },
  verde:   { bg: T.verdeTint, fg: T.verde, edge: T.verdeEdge },
  selo:    { bg: T.seloTint, fg: T.selo, edge: T.seloEdge },
};

export function Badge({ children, variant = 'neutral', size = 'md', outline, dot }: BadgeProps) {
  const v = VARIANT[variant];
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 4,
      background: outline ? 'transparent' : v.bg,
      color: v.fg,
      border: outline ? `1px solid ${v.edge}` : 'none',
      borderRadius: RADIUS.sm,
      padding: size === 'sm' ? '2px 6px' : size === 'lg' ? '6px 12px' : '4px 8px',
      fontSize: size === 'sm' ? 10 : size === 'lg' ? 13 : 11,
      fontWeight: 500,
    }}>
      {dot && <span style={{ width: 6, height: 6, borderRadius: 980, background: v.fg }} />}
      {children}
    </span>
  );
}

Regras

Faça

  • Use cor por sinal: brass=ativo, carmim=urgente, verde=positivo, selo=info, neutral=default.
  • Counter > 99 → mostre "99+".
  • Tamanho sm para cabeçalho de tabela, md inline, lg em hero.
  • Outline para badges em fundos coloridos.
  • Dot semântico para status (online/busy/offline).

Não faça

  • Não use badge como botão.
  • Não use cor sem texto (a11y fail).
  • Não tenha 5+ badges na mesma área (caos visual).
  • Não use brass como decoração genérica.
  • Não force texto longo — badge é selo, não etiqueta longa.

Tokens usados

TokenValorPapel
T.brassTintrgba(164,124,43,0.08)background variant brass
T.carmimTintrgba(163,46,46,0.07)background variant carmim
T.verdeTintrgba(47,107,60,0.07)background variant verde
T.seloTintrgba(43,90,138,0.07)background variant selo
T.surface3#F3F0E6background variant neutral
RADIUS.sm6borderRadius
TYPE.xs11fontSize md