HextaUI

Badge

Statuslabels mit farbigen Punkten, entfernbare Tags, die zugleiten, und Zähler, die zu ihrem neuen Wert rollen.

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

Fügt die Komponente, die HextaUI-Theme-Tokens und alle HextaUI-Komponenten hinzu, von denen sie abhängt.

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

Varianten und Erscheinungsbilder

Das standardmäßige Erscheinungsbild outline behält eine neutrale Fläche und legt die Statusfarbe auf Punkt oder Icon. Verwende appearance="solid", wenn das Badge hervorstechen soll, oder appearance="muted" für einen ruhigen gefüllten Chip. shape="pill" rundet es vollständig ab.

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>
  )
}

Größen

sm, default und lg. Icons, Punkte, der Schließen-Button und Zähler skalieren mit dem 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>
  )
}

Entfernbar

Füge ein <BadgeClose /> hinzu, um ein Badge entfernbar zu machen. Es schrumpft zusammen, die Nachbarn rutschen in die Lücke, dann wandert der Fokus zum nächsten Schließen-Button. Entferne das Element in onOpenChangeComplete aus deinen Daten, damit die Ausblendanimation zuerst zu Ende laufen kann.

"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>
  )
}

Kontrolliert

Übergib open und onOpenChange, um die Sichtbarkeit selbst zu steuern. Setzt du open zurück auf true, kehrt das Badge mit einer Einblendanimation zurück.

"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>
  )
}

Zähler

<BadgeCount /> rollt nur die Ziffern, die sich ändern, und begrenzt bei max (standardmäßig 99) mit einem Pluszeichen. Screenreader hören immer die echte Zahl.

"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>
  )
}

Interaktiv

Nutze render, um ein Badge zu einem Link oder Button zu machen. Es erhält einen Hover-Farbton, eine Skalierung beim Drücken und einen Fokusring. aria-invalid zeigt den Fehlerzustand.

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>
  )
}

Langer Inhalt

Ein Badge wird nie breiter als sein Container. Lange Labels werden mit Auslassungspunkten gekürzt, während Icons und der Schließen-Button sichtbar bleiben.

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>
  )
}

Rechts nach links

Icons, der Schließen-Button und die Einklappanimation folgen der Leserichtung. Zahlen werden immer von links nach rechts gelesen.

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>
  )
}

Diese Tasten gelten für <BadgeClose />.

TasteAktion
EnterSpaceEntfernt das Badge.
BackspaceDeleteEntfernt das Badge.
TabWechselt zum nächsten Schließen-Button. Nach dem Entfernen landet der Fokus auf dem nächsten Schließen-Button oder auf dem vorherigen, wenn es der letzte war.
  • Der Schließen-Button wird nach dem Badge benannt, sodass ein Screenreader „Remove design“ statt eines bloßen „Remove“ ansagt. Übergib aria-label, um das zu überschreiben.
  • <BadgeDot /> ist dekorativ und für assistive Technologien verborgen. Halte den Status im Textlabel fest.
  • <BadgeCount /> stellt den exakten Wert bereit, auch wenn der sichtbare Text begrenzt ist, etwa 99+.
  • Die Entfernen- und Zähleranimationen werden bei reduzierter Bewegung übersprungen.

<Badge /> rendert ein <span> und akzeptiert alle seine Attribute.

PropTypStandard
variantFärbt bei outline den Punkt und die Icons, bei solid die Fläche.
"default" | "success" | "info" | "warning" | "destructive""default"
appearance
"outline" | "solid" | "muted""outline"
shape
"default" | "pill""default"
size
"sm" | "default" | "lg""default"
openGesteuerte Sichtbarkeit.
boolean–
defaultOpen
booleantrue
onOpenChangeWird aufgerufen, wenn BadgeClose aktiviert wird.
(open: boolean) => void–
onOpenChangeCompleteWird aufgerufen, nachdem die Ausblendanimation beendet ist. Entferne das Element hier aus deinen Daten.
(open: boolean) => void–
render
ReactElement | (props, state) => ReactElement<span>
AttributBeschreibung
data-slot="badge"Badges in CSS ansprechen.
data-variantDie aktuelle Variante.
data-appearanceDas aktuelle Erscheinungsbild.
data-shapeDie aktuelle Form.
data-sizeDie aktuelle Größe.
data-ending-styleVorhanden, während das Badge ausblendet.
--badge-accentDie Statusfarbe für Punkte und Icons. Überschreibe sie für einen eigenen Akzent.
--badge-heightDie Höhe des Badges.
--badge-radiusDer Eckenradius. Der Schließen-Button leitet daraus seinen eigenen Radius ab.
PropTypStandard
pulseFügt eine Ping-Animation für Live-Zustände hinzu. Bei reduzierter Bewegung aus.
booleanfalse
AttributBeschreibung
data-slot="badge-dot"Punkte in CSS ansprechen.
data-pulseVorhanden, wenn pulse aktiv ist.
PropTypStandard
children
ReactNode<IconX />
aria-labelÜberschreibt den automatischen Namen „Remove {label}“.
string–
onClickRufe event.preventDefault() auf, um das Badge offen zu halten.
(event) => void–
AttributBeschreibung
data-slot="badge-close"Den Schließen-Button in CSS ansprechen.
PropTypStandard
valueNegative und nicht endliche Werte zeigen 0.
number–
maxWerte darüber werden als max+ angezeigt. Übergib Infinity für keine Begrenzung.
number99
duration
number600
animated
booleantrue
trendDie Richtung, in der sich die Ziffern drehen.
"auto" | "up" | "down" | "shortest""auto"
locales
Intl.LocalesArgument–
format
Intl.NumberFormatOptions–
AttributBeschreibung
data-slot="badge-count"Zähler in CSS ansprechen.

In Blocks verwendet

Blocks, die auf Badge aufbauen.