Badge
Krótka, nieinteraktywna etykieta, która oznacza obiekt statusem, kategorią lub liczbą.
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
- 1KropkaOpcjonalna. Kropka statusu w kolorze odznaki, która może pulsować przy stanach w toku.
- 2KontenerMała etykieta z zaokrąglonymi rogami. Warianty semantyczne barwią ją kolorami statusów; outline zamiast wypełnienia dodaje obramowanie.
- 3EtykietaJedno lub dwa słowa, wielka litera tylko na początku: status, liczba lub kategoria.
- 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.jsonUż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.
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>
)
}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.
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.
- API
- Pulpit
- Webhooki
- Indeks wyszukiwania
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.
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>
)
}Jako link
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
Krótko
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.
BadgeDotmaaria-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
livejest dekoracyjne i wyłącza się przyprefers-reduced-motion.
Dokumentacja API
Badge
Renderuje <span>. Zbudowany na useRender z Base UI, więc przyjmuje prop render.
BadgeDot
Kropka statusu o średnicy 6 px w kolorze tekstu odznaki.
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>