Badge

PrimitivesFree

Quiet status badge; optional ping ring for live statuses.

DefaultSecondaryOutlineLiveFailed

Install

npx shadcn@latest add https://labs.duku.design/r/badge.json

Ask your agent to use this

Connect DUKU Labs to MCP, then hand your coding agent this prompt:

Use the DUKU Labs MCP server to install the Badge component (get_component "badge", then its install command). Adapt it to my existing design tokens, preserve all accessibility and reduced-motion behavior, and keep every interaction state working.

Interaction

TryWhat happens
Watch the pulse variantA ring scales 1→1.8 and fades every 1.8s

Props

PropTypeDefaultDescription
variant"default" | "secondary" | "outline" | "success" | "destructive""default"Visual variant.
pulsebooleanfalseLive-status ping ring.
Source — registry/default/ui/badge.tsx
"use client";

import * as React from "react";
import { motion } from "motion/react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
import { useReducedMotion } from "@/registry/default/lib/use-reduced-motion";

const badgeVariants = cva(
  "relative inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 text-xs font-medium transition-colors duration-200",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground",
        secondary: "bg-muted text-foreground",
        outline: "border border-border text-foreground",
        success:
          "bg-emerald-600/10 text-emerald-600 dark:text-emerald-400",
        destructive: "bg-destructive/10 text-destructive",
      },
    },
    defaultVariants: { variant: "default" },
  }
);

export interface BadgeProps
  extends React.HTMLAttributes<HTMLSpanElement>,
    VariantProps<typeof badgeVariants> {
  /** Renders a live-status ping ring behind a leading dot. */
  pulse?: boolean;
  ref?: React.Ref<HTMLSpanElement>;
}

export function Badge({
  className,
  variant,
  pulse = false,
  children,
  ref,
  ...rest
}: BadgeProps) {
  const reduced = useReducedMotion();
  return (
    <span
      ref={ref}
      data-slot="badge"
      className={cn(badgeVariants({ variant }), className)}
      {...rest}
    >
      {pulse ? (
        <span
          data-slot="pulse"
          className="relative flex size-1.5"
          aria-hidden="true"
        >
          {!reduced ? (
            <motion.span
              className="absolute inset-0 rounded-full bg-current"
              initial={{ scale: 1, opacity: 0.4 }}
              animate={{ scale: 1.8, opacity: 0 }}
              transition={{
                duration: 1.8,
                repeat: Infinity,
                ease: "easeOut",
              }}
            />
          ) : null}
          <span className="relative size-1.5 rounded-full bg-current" />
        </span>
      ) : null}
      {children}
    </span>
  );
}

export { badgeVariants };