HextaUI

Badge

Rótulos de status com pontos coloridos, tags removíveis que deslizam até fechar e contagens que rolam até o novo valor.

PendingIn progressIn reviewFailedSuccessExpired
import {
  IconAlertTriangle,
  IconCircleCheck,
  IconCircleX,
  IconClock,
  IconEye,
  IconLoader2,
} from "@tabler/icons-react"

import { Badge } from "@/components/ui/badge"

export function BadgeDemo() {
  return (
    <div className="flex flex-wrap items-center justify-center gap-2">
      <Badge variant="warning">
        <IconAlertTriangle data-icon="inline-start" />
        Pending
      </Badge>
      <Badge variant="info">
        <IconLoader2 data-icon="inline-start" />
        In progress
      </Badge>
      <Badge variant="warning">
        <IconEye data-icon="inline-start" />
        In review
      </Badge>
      <Badge variant="destructive">
        <IconCircleX data-icon="inline-start" />
        Failed
      </Badge>
      <Badge variant="success">
        <IconCircleCheck data-icon="inline-start" />
        Success
      </Badge>
      <Badge>
        <IconClock data-icon="inline-start" />
        Expired
      </Badge>
    </div>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/badge.json

Adiciona o componente, os tokens de tema do HextaUI e quaisquer componentes do HextaUI dos quais ele depende.

import {
  Badge,
  BadgeClose,
  BadgeCount,
  BadgeDot,
} from "@/components/ui/badge"
<Badge variant="success">
  <BadgeDot />
  Paid
</Badge>
Badge
├── BadgeDot
├── BadgeCount
└── BadgeClose

Variants e aparências

A aparência outline padrão mantém uma superfície neutra e coloca a cor de status no ponto ou no ícone. Use appearance="solid" quando o badge precisar se destacar, ou appearance="muted" para um chip preenchido discreto. shape="pill" o arredonda por completo.

import { Badge, BadgeDot } from "@/components/ui/badge"

export function BadgeVariants() {
  return (
    <div className="flex flex-col items-center gap-3">
      <div className="flex flex-wrap items-center justify-center gap-2">
        <Badge>
          <BadgeDot />
          Draft
        </Badge>
        <Badge variant="success">
          <BadgeDot />
          Paid
        </Badge>
        <Badge variant="info">
          <BadgeDot />
          Syncing
        </Badge>
        <Badge variant="warning">
          <BadgeDot />
          Pending
        </Badge>
        <Badge variant="destructive">
          <BadgeDot />
          Failed
        </Badge>
      </div>
      <div className="flex flex-wrap items-center justify-center gap-2">
        <Badge appearance="solid">Draft</Badge>
        <Badge appearance="solid" variant="success">
          Paid
        </Badge>
        <Badge appearance="solid" variant="info">
          Syncing
        </Badge>
        <Badge appearance="solid" variant="warning">
          Pending
        </Badge>
        <Badge appearance="solid" variant="destructive">
          Failed
        </Badge>
      </div>
      <div className="flex flex-wrap items-center justify-center gap-2">
        <Badge appearance="muted">Draft</Badge>
        <Badge appearance="muted" shape="pill">
          nngroup.com
        </Badge>
        <Badge appearance="muted" shape="pill" variant="info">
          <BadgeDot />
          Syncing
        </Badge>
      </div>
    </div>
  )
}

Tamanhos

sm, default e lg. Ícones, pontos, o botão de fechar e as contagens escalam com o badge.

import { IconCheck } from "@tabler/icons-react"

import { Badge, BadgeClose, BadgeCount, BadgeDot } from "@/components/ui/badge"

export function BadgeSizes() {
  return (
    <div className="flex flex-col items-center gap-3">
      <div className="flex flex-wrap items-center justify-center gap-2">
        <Badge size="sm">Small</Badge>
        <Badge size="sm" variant="success">
          <IconCheck data-icon="inline-start" />
          Paid
        </Badge>
        <Badge size="sm" variant="info">
          <BadgeDot pulse />
          Live
        </Badge>
        <Badge size="sm">
          design
          <BadgeClose />
        </Badge>
        <Badge size="sm">
          <BadgeCount value={42} />
        </Badge>
      </div>
      <div className="flex flex-wrap items-center justify-center gap-2">
        <Badge>Default</Badge>
        <Badge variant="success">
          <IconCheck data-icon="inline-start" />
          Paid
        </Badge>
        <Badge variant="info">
          <BadgeDot pulse />
          Live
        </Badge>
        <Badge>
          design
          <BadgeClose />
        </Badge>
        <Badge>
          <BadgeCount value={42} />
        </Badge>
      </div>
      <div className="flex flex-wrap items-center justify-center gap-2">
        <Badge size="lg">Large</Badge>
        <Badge size="lg" variant="success">
          <IconCheck data-icon="inline-start" />
          Paid
        </Badge>
        <Badge size="lg" variant="info">
          <BadgeDot pulse />
          Live
        </Badge>
        <Badge size="lg">
          design
          <BadgeClose />
        </Badge>
        <Badge size="lg">
          <BadgeCount value={42} />
        </Badge>
      </div>
    </div>
  )
}

Removível

Adicione um <BadgeClose /> para tornar um badge removível. Ele encolhe até fechar e seus vizinhos deslizam para ocupar o espaço, e então o foco vai para o próximo botão de fechar. Remova o item dos seus dados em onOpenChangeComplete para que a animação de saída termine antes.

"use client"

import * as React from "react"

import { Badge, BadgeClose } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"

const initialTags = [
  "design",
  "engineering",
  "research",
  "marketing",
  "customer-success",
  "ops",
]

export function BadgeRemovable() {
  const [tags, setTags] = React.useState(initialTags)
  const [next, setNext] = React.useState(1)

  return (
    <div className="flex w-full max-w-md flex-col gap-3">
      <div className="flex flex-wrap items-center gap-2">
        {tags.map((tag) => (
          <Badge
            key={tag}
            onOpenChangeComplete={(open) => {
              if (!open) {
                setTags((current) => current.filter((item) => item !== tag))
              }
            }}
          >
            {tag}
            <BadgeClose />
          </Badge>
        ))}
      </div>
      <div className="flex flex-wrap items-center gap-2">
        <Button
          variant="outline"
          size="sm"
          onClick={() => {
            setTags((current) => [...current, `tag-${next}`])
            setNext((value) => value + 1)
          }}
        >
          Add tag
        </Button>
        <Button variant="ghost" size="sm" onClick={() => setTags(initialTags)}>
          Reset
        </Button>
      </div>
    </div>
  )
}

Controlado

Passe open e onOpenChange para controlar a visibilidade. Voltar open para true traz o badge de volta com uma animação de entrada.

"use client"

import * as React from "react"

import { Badge, BadgeClose } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"

export function BadgeControlled() {
  const [open, setOpen] = React.useState(true)

  return (
    <div className="flex flex-wrap items-center justify-center gap-2">
      <Badge variant="warning" open={open} onOpenChange={setOpen}>
        Beta features on
        <BadgeClose />
      </Badge>
      <Button
        variant="outline"
        size="sm"
        disabled={open}
        onClick={() => setOpen(true)}
      >
        Restore
      </Button>
    </div>
  )
}

Contagens

<BadgeCount /> gira apenas os dígitos que mudam e para em max (99 por padrão) com um sinal de mais. Os leitores de tela sempre ouvem o número real.

"use client"

import * as React from "react"
import { IconInbox } from "@tabler/icons-react"

import { Badge, BadgeCount } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"

export function BadgeCounts() {
  const [count, setCount] = React.useState(9)

  return (
    <div className="flex flex-col items-center gap-4">
      <div className="flex flex-wrap items-center justify-center gap-2">
        <Badge appearance="solid" variant="destructive">
          <BadgeCount value={count} />
        </Badge>
        <Badge>
          <BadgeCount value={count} max={999} />
          unread
        </Badge>
        <Button variant="outline">
          <IconInbox />
          Inbox
          <Badge size="sm">
            <BadgeCount value={count} />
          </Badge>
        </Button>
      </div>
      <div className="flex flex-wrap items-center justify-center gap-2">
        <Button
          variant="outline"
          size="sm"
          onClick={() => setCount((value) => value + 1)}
        >
          +1
        </Button>
        <Button
          variant="outline"
          size="sm"
          onClick={() => setCount((value) => Math.max(0, value - 1))}
        >
          −1
        </Button>
        <Button
          variant="outline"
          size="sm"
          onClick={() => setCount((value) => value + 10)}
        >
          +10
        </Button>
        <Button
          variant="ghost"
          size="sm"
          onClick={() => setCount(Math.floor(Math.random() * 1200))}
        >
          Random
        </Button>
      </div>
    </div>
  )
}

Interativo

Use render para transformar um badge em link ou botão. Ele ganha um tom ao passar o mouse, uma escala ao pressionar e um anel de foco. aria-invalid exibe o estado de erro.

import { IconArrowUpRight, IconX } from "@tabler/icons-react"

import { Badge, BadgeDot } from "@/components/ui/badge"

export function BadgeInteractive() {
  return (
    <div className="flex flex-wrap items-center justify-center gap-2">
      <Badge render={<a href="#changelog" />}>
        v2.0 is out
        <IconArrowUpRight data-icon="inline-end" />
      </Badge>
      <Badge render={<button type="button" />} variant="info">
        <BadgeDot />
        Filter: open
      </Badge>
      <Badge render={<a href="#new" />}>New</Badge>
      <Badge render={<button type="button" />} aria-invalid>
        <IconX data-icon="inline-start" />
        Invalid
      </Badge>
    </div>
  )
}

Conteúdo longo

Um badge nunca fica mais largo que seu contêiner. Rótulos longos são truncados com reticências, enquanto os ícones e o botão de fechar continuam visíveis.

import { IconCheck } from "@tabler/icons-react"

import { Badge, BadgeClose } from "@/components/ui/badge"

export function BadgeLongContent() {
  return (
    <div className="flex w-56 max-w-full flex-col items-start gap-2">
      <Badge>
        SupercalifragilisticexpialidociousSupercalifragilisticexpialidocious
      </Badge>
      <Badge variant="success">
        <IconCheck data-icon="inline-start" />
        [email protected]
        <BadgeClose />
      </Badge>
    </div>
  )
}

Da direita para a esquerda

Os ícones, o botão de fechar e a animação de colapso seguem a direção de leitura. As contagens sempre são lidas da esquerda para a direita.

import { IconCheck } from "@tabler/icons-react"

import { Badge, BadgeClose, BadgeCount, BadgeDot } from "@/components/ui/badge"

export function BadgeRtl() {
  return (
    <div dir="rtl" className="flex flex-wrap items-center justify-center gap-2">
      <Badge variant="success">
        <IconCheck data-icon="inline-start" />
        مدفوع
      </Badge>
      <Badge>
        <BadgeDot pulse />
        مباشر
      </Badge>
      <Badge>
        تصميم
        <BadgeClose />
      </Badge>
      <Badge>
        <BadgeCount value={120} />
      </Badge>
    </div>
  )
}

Estas teclas se aplicam a <BadgeClose />.

TeclaAção
EnterSpaceRemove o badge.
BackspaceDeleteRemove o badge.
TabMove para o próximo botão de fechar. Após uma remoção, o foco vai para o próximo botão de fechar, ou para o anterior quando era o último.
  • O botão de fechar recebe o nome do badge, então um leitor de tela anuncia “Remove design” em vez de apenas “Remove”. Passe aria-label para substituí-lo.
  • <BadgeDot /> é decorativo e fica oculto para tecnologias assistivas. Mantenha o status no rótulo de texto.
  • <BadgeCount /> expõe o valor exato mesmo quando o texto visível é limitado, como 99+.
  • As animações de remoção e de contagem são ignoradas quando o movimento reduzido está ativado.

<Badge /> renderiza um <span> e aceita todos os seus atributos.

PropTipoPadrão
variantColore o ponto e os ícones em outline, ou a superfície em solid.
"default" | "success" | "info" | "warning" | "destructive""default"
appearance
"outline" | "solid" | "muted""outline"
shape
"default" | "pill""default"
size
"sm" | "default" | "lg""default"
openVisibilidade controlada.
boolean–
defaultOpen
booleantrue
onOpenChangeChamado quando BadgeClose é ativado.
(open: boolean) => void–
onOpenChangeCompleteChamado após o fim da animação de saída. Remova o item dos seus dados aqui.
(open: boolean) => void–
render
ReactElement | (props, state) => ReactElement<span>
AtributoDescrição
data-slot="badge"Seleciona os badges no CSS.
data-variantA variante atual.
data-appearanceA aparência atual.
data-shapeA forma atual.
data-sizeO tamanho atual.
data-ending-stylePresente enquanto o badge anima a saída.
--badge-accentA cor de status usada por pontos e ícones. Substitua-a para um destaque personalizado.
--badge-heightA altura do badge.
--badge-radiusO raio dos cantos. O botão de fechar deriva o próprio raio dele.
PropTipoPadrão
pulseAdiciona uma animação de ping para estados ao vivo. Desativada quando o movimento reduzido está ativado.
booleanfalse
AtributoDescrição
data-slot="badge-dot"Seleciona os pontos no CSS.
data-pulsePresente quando pulse está ativado.
PropTipoPadrão
children
ReactNode<IconX />
aria-labelSubstitui o nome automático “Remove {label}”.
string–
onClickChame event.preventDefault() para manter o badge aberto.
(event) => void–
AtributoDescrição
data-slot="badge-close"Seleciona o botão de fechar no CSS.
PropTipoPadrão
valueValores negativos e não finitos exibem 0.
number–
maxValores acima dele aparecem como max+. Passe Infinity para não ter limite.
number99
duration
number600
animated
booleantrue
trendA direção em que os dígitos giram.
"auto" | "up" | "down" | "shortest""auto"
locales
Intl.LocalesArgument–
format
Intl.NumberFormatOptions–
AtributoDescrição
data-slot="badge-count"Seleciona as contagens no CSS.

Usado em blocos

Blocos que se baseiam em Badge.