Skip to content
cennge-ui
GitHubv0.3.1, changelog

Badge

A short label in the line of text: a status, a count, a category. Five variants in seven colours.

Pull requests3
  • Kbd draws its key symbols at the size of the cap
    fix/kbd-symbols-at-cap-sizebugkbd
    Passing
  • Charts in a new entry, cennge-ui/charts
    feat/chartsfeaturecharts
    Running
  • Carousel dots show the pointer cursor
    fix/carousel-dot-cursorbugcarousel
    Failing
  1. Labels are flat, the default; the branch is faded, the count solid, the checks a dot.
  2. The dot is hidden from screen readers, so the word beside it says the status.
  3. The branch badge has a max width: a longer name ends in an ellipsis instead of widening the row.
Live: switch the accent at the top of the page; the count follows it.

Field marks

Import
import { Badge } from "cennge-ui";
Size
1.1 KB gzip for this import, its dependencies aside
Pattern
A <span>: no role, not focusable
SSR
Rendered on the server in the Next.js check; hydrates without a warning
Defaults
Colour and size from CenngeProvider when you set them there
Tests
Unit tests

When to use#

Use a Badge for a word or a number that labels something next to it: the state of a CI run, an issue's labels, the count on a tab. It is text, not a control. If clicking it should do something, use a Button with size="sm".

Similar#

  • Indicator pins a dot or a count to the corner of another element, such as an icon button or an avatar; a Badge sits in the line.
  • TagsInput makes tags you add and remove; a Badge only shows one.

Variants and colours#

flat is the default. solid is the loudest, for the one badge that must be seen; dot keeps the text neutral and puts the colour in a dot before it. primary takes the accent, the other colours keep their meaning.

solid
defaultprimarysecondarysuccesswarningdangerinfo
flat
defaultprimarysecondarysuccesswarningdangerinfo
bordered
defaultprimarysecondarysuccesswarningdangerinfo
faded
defaultprimarysecondarysuccesswarningdangerinfo
dot
defaultprimarysecondarysuccesswarningdangerinfo
variants.tsx
import { Badge, type BadgeVariant, type CenngeColor } from "cennge-ui";

const variants: BadgeVariant[] = ["solid", "flat", "bordered", "faded", "dot"];
const colors: CenngeColor[] = ["default", "primary", "secondary", "success", "warning", "danger", "info"];

export default function BadgeVariants() {
  return (
    <div className="grid grid-cols-[4.5rem_1fr] items-start gap-x-4 gap-y-3">
      {variants.map((variant) => (
        <div key={variant} className="contents">
          <span className="text-sm leading-6 text-muted">{variant}</span>
          <div className="flex flex-wrap gap-1.5">
            {colors.map((color) => (
              <Badge key={color} variant={variant} color={color}>
                {color}
              </Badge>
            ))}
          </div>
        </div>
      ))}
    </div>
  );
}

Counts and icons#

A count after a tab's name is a small Badge inside the trigger. startContent and endContent put an icon before or after the text; mark the icon aria-hidden, the text already says it.

payload-cache@1.4.0Latest
counts.tsx
import { Badge, Tabs, TabsContent, TabsList, TabsTrigger } from "cennge-ui";

function CheckIcon() {
  return (
    <svg viewBox="0 0 24 24" className="size-3.5" fill="none" stroke="currentColor" strokeWidth="2.75" aria-hidden="true">
      <path d="M5 12.5l4.5 4.5L19 7.5" strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}

export default function RepositoryTabs() {
  return (
    <Tabs defaultValue="code" className="w-full max-w-md">
      <TabsList aria-label="payload-cache">
        <TabsTrigger value="code">Code</TabsTrigger>
        <TabsTrigger value="issues">
          Issues
          <Badge size="sm">4</Badge>
        </TabsTrigger>
        <TabsTrigger value="pulls">
          Pull requests
          <Badge size="sm">1</Badge>
        </TabsTrigger>
      </TabsList>
      <TabsContent value="code" className="flex items-center gap-2 pt-3 text-sm">
        payload-cache@1.4.0
        <Badge color="success" size="sm" startContent={<CheckIcon />}>
          Latest
        </Badge>
      </TabsContent>
      <TabsContent value="issues" className="pt-3 text-sm text-muted">
        Four open issues.
      </TabsContent>
      <TabsContent value="pulls" className="pt-3 text-sm text-muted">
        One open pull request.
      </TabsContent>
    </Tabs>
  );
}

Sizes, radius and long text#

Three sizes, and radius from none to full, the default. The text sits in its own truncating span, so a narrow parent cuts it with an ellipsis instead of pushing the layout wider.

SmallMediumLarge
v0.3.1MITTypeScript
fix/select-chevron-inside-an-open-dialog
sizes.tsx
import { Badge } from "cennge-ui";

export default function BadgeSizes() {
  return (
    <div className="flex flex-col gap-4">
      <div className="flex flex-wrap items-center gap-2">
        <Badge size="sm" color="primary">
          Small
        </Badge>
        <Badge size="md" color="primary">
          Medium
        </Badge>
        <Badge size="lg" color="primary">
          Large
        </Badge>
      </div>
      <div className="flex flex-wrap items-center gap-2">
        <Badge variant="bordered" radius="sm">
          v0.3.1
        </Badge>
        <Badge variant="bordered" radius="md">
          MIT
        </Badge>
        <Badge variant="bordered" radius="full">
          TypeScript
        </Badge>
      </div>
      {/* A badge never grows past its parent: long text ends in an ellipsis. */}
      <div className="w-40">
        <Badge variant="faded">fix/select-chevron-inside-an-open-dialog</Badge>
      </div>
    </div>
  );
}

Accessibility#

A Badge is a plain <span> that screen readers read with the text around it: a tab named "Issues 4". Its colour is not announced and the dot of variant="dot" is aria-hidden, so the words must carry the meaning: "Failing", not a red badge alone.

Props#

PropTypeDefault
color
"default""primary""secondary""success""warning""danger""info"
"default"
variant
"bordered""flat""faded""solid""dot"
"flat"
size
"sm""md""lg"
"md"
radius
"none""sm""md""lg""full"
"full"
startContent
React.ReactNode
–
endContent
React.ReactNode
–

Also takes every attribute of <span>, except color, which mean something else here.