Skip to content
UI Kit
Accent color

Badge

Small labels in four styles, with status tones, a pulsing dot and a small size for counts.

4 variantsOne of 9 components in Data display

Variants

Styles, status and countsNew in 1.2

Default, outline, soft and solid, six status tones for the same states everywhere, and the small size for numbers.

Status in a listNew in 1.2

Invoices with their state at the end of each row, one color per meaning. On phones the badge moves under the amount.

Counts in navigationNew in 1.2

Unread numbers next to each mailbox, read out with their meaning. The active entry fills its badge, on phones the list scrolls sideways.

On cardsNew in 1.2

New and sale on a product image, most popular on a plan, the category on an article. The badge sits on the image or beside the title, never over text.

Details

What Badge brings along, read from the registry and the source.

Category
Data display
Variants
4
Version
1.1.0 · since 1.0.0
File
components/ui/badge.tsx
Brings along
utils
npm packages
None
Languages
Texts come through props
Studio
Adjust in code

Props

The BadgeProps interface from components/ui/badge.tsx, with the comments from the code. Also takes every prop of React.HTMLAttributes<HTMLSpanElement>.

  • variantoptional
    BadgeVariant

    defaultoutlinesoftsolid

  • dotoptional
    boolean

    A pulsing point in front

  • toneoptional
    BadgeTone

    accentneutralsuccesswarningdangerinfo

    Status meaning: success, warning, danger, info or neutral. Default the accent.

  • sizeoptional
    'sm' | 'md'

    sm for counts in tabs and navigation, md the default

  • childrenrequired
    React.ReactNode

Accessibility

Read from the source of the component.

  • Needs no client state. The server writes it in full, so it reads without JavaScript too.

Motion

  • Brings animations of its own, calmly timed and without wobble.
  • With reduced motion (prefers-reduced-motion) it stays still. 1 places in the code switch motion off then.

Source

The file aiw add puts into your project. From then on it is yours.

Show the file, 65 lines
components/ui/badge.tsx
'use client';

import { cn } from '@/lib/kit';

export type BadgeVariant = 'default' | 'outline' | 'soft' | 'solid';
/** Meaning of a status badge. Colors soft and outline badges and the dot of a default badge, solid stays on the accent. */
export type BadgeTone = 'accent' | 'neutral' | 'success' | 'warning' | 'danger' | 'info';

const VARIANT: Record<BadgeVariant, string> = {
  default: 'bg-muted text-foreground',
  outline: 'border border-border-strong text-muted-foreground',
  soft: 'bg-[color-mix(in_oklab,var(--primary)_12%,transparent)] text-[color-mix(in_oklab,var(--primary)_80%,var(--foreground))]',
  solid: 'bg-primary text-primary-foreground',
};

const TONE: Record<Exclude<BadgeTone, 'accent'>, { color: string; text: string }> = {
  neutral: { color: 'var(--muted-foreground)', text: 'text-muted-foreground' },
  success: { color: 'var(--success)', text: 'text-success-foreground' },
  warning: { color: 'var(--warning)', text: 'text-warning-foreground' },
  danger: { color: 'var(--destructive)', text: 'text-destructive' },
  info: { color: 'var(--info)', text: 'text-info' },
};

const TONED: Partial<Record<BadgeVariant, string>> = {
  soft: 'bg-[color-mix(in_oklab,var(--badge)_15%,transparent)]',
  outline: 'border border-[color-mix(in_oklab,var(--badge)_45%,var(--border))]',
};

const SIZE = {
  sm: 'h-5 gap-1 px-2 text-[11px] tabular-nums',
  md: 'h-6 gap-1.5 px-2.5 text-xs',
};

export interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement> {
  variant?: BadgeVariant;
  /** A pulsing point in front */
  dot?: boolean;
  /** Status meaning: success, warning, danger, info or neutral. Default the accent. */
  tone?: BadgeTone;
  /** sm for counts in tabs and navigation, md the default */
  size?: 'sm' | 'md';
  className?: string;
  children: React.ReactNode;
}

/** Small label for status, category or "New". With dot it gets a pulsing point in front, with tone a status color. */
export function Badge({ variant = 'default', dot, tone, size = 'md', className, style, children, ...rest }: BadgeProps) {
  const t = tone && tone !== 'accent' ? TONE[tone] : undefined;
  const toned = t && TONED[variant];
  return (
    <span
      {...rest}
      style={t ? ({ '--badge': t.color, ...style } as React.CSSProperties) : style}
      className={cn('inline-flex items-center whitespace-nowrap rounded-full font-medium', SIZE[size], toned ? cn(toned, t.text) : VARIANT[variant], className)}
    >
      {dot && (
        <span className={cn('relative flex size-1.5', t && !toned && variant === 'default' && t.text)} aria-hidden>
          <span className="absolute inset-0 animate-kit-pulse rounded-full bg-current motion-reduce:animate-none" />
          <span className="relative size-1.5 rounded-full bg-current" />
        </span>
      )}
      {children}
    </span>
  );
}

Frequent questions

How do I install Badge?

With aiw add @ai-website/badge. The command puts components/ui/badge.tsx into your project, the code is yours from then on. It needs no further npm packages.

Which variants does Badge have?

4 variants, Styles, status and counts, Status in a list, Counts in navigation and On cards. You pick one with the variant prop, for example variant="soft".

Can an AI assistant build with Badge?

Yes. Through the MCP server of the aiw CLI, Claude, Cursor, GitHub Copilot, Windsurf, Codex and Gemini find Badge with aiw_components and install it with aiw_add @ai-website/badge. Name the variant in your prompt, for example variant="soft", and the assistant writes your content straight into the props.

Often searched

Jump to

Copied to the clipboard