Przejdź do treści

Badge

Krótka, nieinteraktywna etykieta, która oznacza obiekt statusem, kategorią lub liczbą.

Kod źródłowyRejestr
DziałaNowośćv1.0.0Szkic
import { Badge, BadgeDot } from "@/components/ui/badge"

export default function BadgeDemo() {
  return (
    <div className="flex flex-wrap items-center justify-center gap-2">
      <Badge variant="success">
        <BadgeDot />
        Działa
      </Badge>
      <Badge variant="brand">Nowość</Badge>
      <Badge variant="secondary">v1.0.0</Badge>
      <Badge variant="outline">Szkic</Badge>
    </div>
  )
}

Anatomia

DziałaDokumentacja
  1. 1KropkaOpcjonalna. Kropka statusu w kolorze odznaki, która może pulsować przy stanach w toku.
  2. 2KontenerMała etykieta z zaokrąglonymi rogami. Warianty semantyczne barwią ją kolorami statusów; outline zamiast wypełnienia dodaje obramowanie.
  3. 3EtykietaJedno lub dwa słowa, wielka litera tylko na początku: status, liczba lub kategoria.
  4. 4IkonaOpcjonalna. Jej rozmiar ustala odznaka, która po tej stronie zmniejsza swój wewnętrzny odstęp.

Instalacja

$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/badge.json

Użycie

import { Badge, BadgeDot } from "@/components/ui/badge"
<Badge variant="success">
  <BadgeDot />
  Paid
</Badge>

Przykłady

Warianty

Warianty wyrażają znaczenie, a nie gust. Cztery warianty statusu — success, warning, destructive, info — są zarezerwowane dla stanów, więc zielona odznaka znaczy to samo w każdym miejscu produktu.

DomyślnyDrugorzędnyKonturowyMarkaDrugi kolor markiSukcesOstrzeżenieDestrukcyjnyInformacjaPrzezroczystyLink
import { Badge } from "@/components/ui/badge"

export default function BadgeVariants() {
  return (
    <div className="flex max-w-lg flex-wrap items-center justify-center gap-2">
      <Badge>Domyślny</Badge>
      <Badge variant="secondary">Drugorzędny</Badge>
      <Badge variant="outline">Konturowy</Badge>
      <Badge variant="brand">Marka</Badge>
      <Badge variant="brand-secondary">Drugi kolor marki</Badge>
      <Badge variant="success">Sukces</Badge>
      <Badge variant="warning">Ostrzeżenie</Badge>
      <Badge variant="destructive">Destrukcyjny</Badge>
      <Badge variant="info">Informacja</Badge>
      <Badge variant="ghost">Przezroczysty</Badge>
      <Badge variant="link">Link</Badge>
    </div>
  )
}
WariantZastosowanie
defaultMocno wyróżnione etykiety, które muszą się wybijać na zatłoczonych powierzchniach. Używaj oszczędnie.
secondaryNeutralne metadane: wersje, typy plików, role, liczniki.
outlineNeutralne kategorie i tagi, które powinny pozostać w tle.
brandWyróżnienia, takie jak Nowy czy Beta, oraz jedna polecana opcja w zestawie.
brand-secondaryPlan lub poziom, taki jak Pro, w drugim kolorze marki. Monochromatyczny, dopóki motyw go nie ustawi.
successStany zakończone, prawidłowe lub zatwierdzone: Opłacono, Działa, Scalono.
warningStany, które wkrótce będą wymagać uwagi: Oczekuje, Obniżona wydajność, Wygasa.
destructiveBłędy i stany blokujące: Błąd, Awaria, Po terminie.
infoNeutralne stany informacyjne: Prace serwisowe, Zaplanowano, Do przeglądu.
ghost, linkOdznaki w gęstych wierszach lub w tekście, które do czasu najechania mają wyglądać jak metadane.

Rozmiary

default (20 px) pasuje do tekstu głównego i komórek tabel. Używaj sm do liczników w nawigacji, a lg do odznak obok nagłówków.

MałyDomyślnyDuży
import { Badge } from "@/components/ui/badge"

export default function BadgeSizes() {
  return (
    <div className="flex items-center gap-3">
      <Badge variant="brand" size="sm">
        Mały
      </Badge>
      <Badge variant="brand">Domyślny</Badge>
      <Badge variant="brand" size="lg">
        Duży
      </Badge>
    </div>
  )
}

Status

Łącz odznaki statusu z BadgeDot. Kropka pozwala przejrzeć kolumnę stanów jednym spojrzeniem, a słowo sprawia, że stan jest jednoznaczny. Ustaw live, żeby kropka pulsowała przy trwającej czynności, takiej jak budowanie czy synchronizacja. Stan stały zachowuje nieruchomą kropkę, niezależnie od tego, jak jest ważny. Pulsowanie wyłącza się przy ograniczonym ruchu.

  • APIDziała
  • PulpitZakłócenia
  • WebhookiAwaria
  • Indeks wyszukiwaniaPonowne indeksowanie
import { Badge, BadgeDot } from "@/components/ui/badge"

const services = [
  { name: "API", status: "Działa", variant: "success", live: false },
  { name: "Pulpit", status: "Zakłócenia", variant: "warning", live: false },
  { name: "Webhooki", status: "Awaria", variant: "destructive", live: false },
  { name: "Indeks wyszukiwania", status: "Ponowne indeksowanie", variant: "info", live: true },
] as const

export default function BadgeStatus() {
  return (
    <ul className="flex w-full max-w-sm flex-col divide-y rounded-xl border bg-card">
      {services.map((service) => (
        <li
          key={service.name}
          className="flex items-center justify-between px-4 py-3 text-sm"
        >
          <span className="font-medium">{service.name}</span>
          <Badge variant={service.variant}>
            <BadgeDot live={service.live} />
            {service.status}
          </Badge>
        </li>
      ))}
    </ul>
  )
}

Z ikonami

Oznacz ikony atrybutem data-icon="inline-start" lub data-icon="inline-end"; odznaka zmniejszy po tej stronie wewnętrzny odstęp i nada ikonie rozmiar 12 px, a w rozmiarze lg — 14 px.

ZweryfikowanoWygenerowane przez AIPrywatne+12,4%Dziennik zmian
import {
  ArrowUpRightIcon,
  CircleCheckIcon,
  LockIcon,
  SparklesIcon,
  TrendingUpIcon,
} from "lucide-react"

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

export default function BadgeWithIcon() {
  return (
    <div className="flex max-w-md flex-wrap items-center justify-center gap-2">
      <Badge variant="success">
        <CircleCheckIcon data-icon="inline-start" />
        Zweryfikowano
      </Badge>
      <Badge variant="brand">
        <SparklesIcon data-icon="inline-start" />
        Wygenerowane przez AI
      </Badge>
      <Badge variant="secondary">
        <LockIcon data-icon="inline-start" />
        Prywatne
      </Badge>
      <Badge variant="outline">
        <TrendingUpIcon data-icon="inline-start" />
        +12,4%
      </Badge>
      <Badge variant="info">
        Dziennik zmian
        <ArrowUpRightIcon data-icon="inline-end" />
      </Badge>
    </div>
  )
}

Odznaka może prowadzić dalej — do wpisu w dzienniku zmian, przefiltrowanej listy, tagu. Użyj propa render, żeby stała się prawdziwym linkiem; odznaki będące linkami automatycznie zyskują stan najechania.

import { ArrowRightIcon } from "lucide-react"
import Link from "next/link"

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

const tags = [
  { label: "kolor", href: "/pl/docs/foundations/color" },
  { label: "typografia", href: "/pl/docs/foundations/typography" },
  { label: "dostępność", href: "/pl/docs/foundations/accessibility" },
  { label: "tokeny-projektowe", href: "/pl/docs/foundations/tokens" },
]

export default function BadgeAsLink() {
  return (
    <div className="flex flex-col items-center gap-4">
      <Badge
        variant="brand"
        size="lg"
        render={<Link href="/pl/docs/resources/changelog" />}
      >
        Zobacz, co nowego
        <ArrowRightIcon data-icon="inline-end" />
      </Badge>
      <div className="flex flex-wrap justify-center gap-1.5">
        {tags.map((tag) => (
          <Badge
            key={tag.label}
            variant="secondary"
            render={<Link href={tag.href} />}
          >
            #{tag.label}
          </Badge>
        ))}
      </div>
    </div>
  )
}
<Badge variant="brand" render={<Link href="/docs/resources/changelog" />}>
  See what's new
</Badge>

Liczniki

Licznik mówi, ile czeka w miejscu, do którego prowadzi. Pokazuj najwyżej 99+, pełnego wariantu default używaj tylko dla nieprzeczytanych elementów wymagających działania i dodaj do liczby kontekst dla czytników ekranu.

import { BellIcon, InboxIcon, MessageSquareIcon } from "lucide-react"

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

const items = [
  { label: "Odebrane", icon: InboxIcon, count: 12, "unread": true },
  { label: "Wzmianki", icon: MessageSquareIcon, count: 3, "unread": true },
  { label: "Powiadomienia", icon: BellIcon, count: 128, "unread": false },
]

export default function BadgeCount() {
  return (
    <nav
      aria-label="Skrzynki pocztowe"
      className="flex w-56 flex-col gap-px rounded-xl border bg-card p-1 shadow-raised"
    >
      {items.map(({ label, icon: Icon, count, unread }) => (
        <a
          key={label}
          href="#"
          className="flex h-9 items-center gap-2.5 rounded-lg px-2.5 text-sm hover:bg-accent"
        >
          <Icon className="size-4 text-muted-foreground" aria-hidden="true" />
          <span className="flex-1">{label}</span>
          <Badge
            variant={unread ? "default" : "secondary"}
            size="sm"
            className="min-w-5 tabular-nums"
          >
            {count > 99 ? "99+" : count}
            <span className="sr-only">
              {unread ? " wiadomości do przeczytania" : " wiadomości łącznie"}
            </span>
          </Badge>
        </a>
      ))}
    </nav>
  )
}

Wytyczne

Kiedy używać

  • Aby pokazać stan obiektu: płatności, wdrożenia, usługi.
  • Aby dodać krótkie metadane: rolę, wersję, plan, tag.
  • Aby pokazać liczbę elementów czekających za pozycją nawigacji.

Kiedy nie używać

  • Do wywoływania akcji — użyj komponentu Button.
  • Do filtrowania widoku — użyj komponentu Toggle Group lub pól wyboru, żeby zaznaczenie było ogłaszane.
  • Do wyjaśniania — jeśli potrzeba więcej niż dwóch słów, potrzebne jest zdanie, Alert lub Tooltip.

Nigdy sam kolor

OpłaconoPo terminie
DobrzeKropka do szybkiego przeglądania, słowo dla znaczenia. Czytelne w skali szarości, dla osób z zaburzeniami widzenia barw i dla czytników ekranu.
ŹleKolorowa kropka lub plakietka bez etykiety zmusza do zapamiętywania kodu kolorów.

Krótko

Wkrótce wygasa
DobrzeJedno lub dwa słowa, wielka litera tylko na początku, bez interpunkcji.
Twoja karta wygasa za 3 dni, zaktualizuj ją
ŹleZdania w odznakach są ucinane i nie da się ich szybko przejrzeć.

Umiar

Odznaki działają, bo są rzadkie. Jeśli każdy wiersz tabeli ma trzy odznaki, żadna nie zostanie zauważona — zostaw jedną odznakę statusu na obiekt, a resztę metadanych przenieś do zwykłego tekstu.

Dostępność

  • Domyślnie statyczna. Odznaka renderuje <span> i nie przyjmuje fokusu. Gdy prowadzi do innej strony, renderuj ją jako link — wtedy zyska rolę, fokus i widoczny pierścień fokusu.
  • Status to tekst. Każdy wariant statusu łączy kolor ze słowem. BadgeDot ma aria-hidden; znaczenie niesie etykieta.
  • Liczniki wymagają kontekstu. Samo „12” odczytane na głos jest niejednoznaczne. Dodaj wizualnie ukryty dopisek — <span className="sr-only"> unread</span> — albo umieść liczbę w dostępnej nazwie linku.
  • Kontrast. Warianty delikatne używają tekstu w stopniu 11 na wypełnieniach w stopniu 3, dla których paleta gwarantuje co najmniej 4,5:1 w obu trybach. Warianty pełne również osiągają 4,5:1.
  • Ruch. Pulsowanie live jest dekoracyjne i wyłącza się przy prefers-reduced-motion.

Dokumentacja API

Badge

Renderuje <span>. Zbudowany na useRender z Base UI, więc przyjmuje prop render.

PropTypDomyślnie
variant

Znaczenie i stopień wyróżnienia odznaki.

"default" | "secondary" | "outline" | "brand" | "brand-secondary" | "success" | "warning" | "destructive" | "info" | "ghost" | "link""default"
size

Wysokość 18, 20 lub 24 piksele.

"sm" | "default" | "lg""default"
render

Renderuje inny element, np. link, z zachowaniem stylów odznaki.

ReactElement | (props, state) => ReactElementBrak wartości domyślnej

BadgeDot

Kropka statusu o średnicy 6 px w kolorze tekstu odznaki.

PropTypDomyślnie
live

Pulsowanie kropki przy trwającej czynności. Wyłączone przy ograniczonym ruchu.

booleanfalse

badgeVariants

Generator klas jest eksportowany, żeby inne elementy — komórka tabeli, własny link — mogły korzystać ze stylów odznaki.

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

<span className={badgeVariants({ variant: "info", size: "sm" })}>Beta</span>