KDS
Display

Badge

Label compacto para status, contadores e tags — quatro variantes visuais.

Overview

Badge é o label compacto de status, contagem e tag do KDS. Renderiza como um <span> inline pequeno (text-xs, rounded-full, padding mínimo) com quatro variantes visuais — default, secondary, destructive, outline.

A escolha entre badge, chip e tag é uma das fontes mais comuns de inconsistência em design systems. KDS resolve assim: Badge é estático — para labels de status, contadores e tags fixas. Chips interativos (selecionáveis) são responsabilidade do ToggleGroup (Phase 4). Se você está pensando em adicionar onClick em um Badge para selecioná-lo, provavelmente quer ToggleGroup.

Preview
Variante default com texto simples.
Novo

Anatomy

<Badge variant="default">
  ├─ [data-slot="badge"]
  ├─ [data-variant="default"]
  └─ children (texto + ícone opcional de 12px)
</Badge>

A altura é determinada pelo padding (py-0.5) + line-height do texto, não por h-*. Isso permite que Badges fiquem alinhados inline com texto sem quebrar baseline. Quando usado standalone em listas, costuma medir ~20px de altura.

Usage

import { Badge } from "@kalvner/kds/display/badge";

export function PostStatus({ status }: { status: "draft" | "published" }) {
  return status === "draft" ? (
    <Badge variant="outline">Em rascunho</Badge>
  ) : (
    <Badge>Publicado</Badge>
  );
}

Props

PropTipoDefaultDescrição
variant'default' | 'secondary' | 'destructive' | 'outline''default'Variante visual.
asChildbooleanfalseRenderiza o filho em vez de <span> (Radix Slot). Útil para <a> clicável.
...restReact.ComponentProps<'span'>Tudo o que <span> aceita (className, aria-*).

Variants

Default
Status feliz, ação completada.
Publicado
Secondary
Estado neutro com peso visual.
Em revisão
Destructive
Erros e falhas — não use para "deletado".
Falhou
Outline
Discreto — bom para tags em listas longas.
Beta

States

With leading icon
Ícone de 12px reforça o status.

Online

Aprovado

Crescendo

Count
Contador numérico — 99+ para limites.
1299+3
As link (asChild)
Vira <a> clicável com hover style.

Composition

Status in list
Estados misturados — outline, default, secondary.
Atualização sobre o redesignEm rascunho
Lançamento da versão 2.0Publicado
Migração para PostgresEm revisão
Sidebar nav count
Badge contador alinhado à direita.

When to use

  • Para indicar status de um item em uma lista (Em rascunho, Publicado, Em revisão, Bloqueado).
  • Como contador inline em itens de navegação ou em ícones (Inbox 12, Mencionados 3).
  • Para tags estáticas — categorias, frameworks, ambientes.
  • Para sinalizar atributos de um item (Beta, Pro, Novo).

When not to use

  • Para chips selecionáveis (filtros toggleáveis em uma busca) — use ToggleGroup (Phase 4).
  • Para botões — Badge não é interativo. Para uma ação clicável, use Button com size="xs".
  • Para mensagens longas — Badge é para 1-2 palavras. Mensagens estruturadas são Alert.
  • Para estados temporais ("Salvando…") — use loading state no componente que dispara a ação, não um Badge separado.

Best practices

  • Variantes seguem semântica. destructive é para erros e falhas (Falhou, Erro, Bloqueado), não para qualquer status vermelho. "Deletado" é um estado estável — outline ou secondary.
  • Texto curto, sempre. 1-2 palavras é o ideal. Para 3+, considere se o que você quer não é uma frase em texto inline com text-xs text-muted-foreground em vez de Badge.
  • Mantenha consistência por estado em todo o produto. Se "Em revisão" é secondary em uma tela, deve ser secondary em todas.
  • Evite uma Badge ao lado de outra cor de fundo forte. Badge em um Card colorido vira ruído. Use outline se o entorno já é carregado.
  • Não confie só em cor. Para usuários com daltonismo, ícone + texto + cor formam o conjunto. Apenas cor não basta.

Accessibility

ConcernComportamento
Roles<span> por default — sem role implícita. Não confunda Badge com botão; ele não é interativo. Para virar <a>, use asChild.
KeyboardNão recebe foco no estado padrão. Quando asChild com <a>, herda foco do link.
Screen readerLê o texto interno. Para contadores, considere aria-label="12 mensagens não lidas" para dar contexto além do número solto.
Cor + íconePara comunicar status, combine cor + ícone + texto. Não dependa só de cor (daltonismo, low-vision).
Live regionQuando um contador muda dinamicamente (mensagens recebidas), wrap o Badge em um container aria-live="polite" para anunciar a mudança.
  • Button — para Badge clicável que vira ação, use Button com size="xs" no lugar.
  • Alert — para mensagens estruturadas com título + descrição, em vez de Badge curto.
  • ToggleGroup (Phase 4) — para chips selecionáveis e filtros toggleáveis.

On this page