{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "badge", "title": "Badge", "description": "Compact label with semantic status variants and a private categorical-color palette.", "dependencies": [ "class-variance-authority" ], "registryDependencies": [ "https://zeron-ui.vercel.app/r/surfaces.json", "utils", "https://zeron-ui.vercel.app/r/shape-context.json" ], "files": [ { "path": "src/components/ui/badge.tsx", "content": "\"use client\";\n\nimport { forwardRef, type HTMLAttributes } from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"@/lib/utils\";\nimport { useShape } from \"@/lib/shape-context\";\nimport {\n badgeCategoricalTokens,\n badgeColors,\n badgeStatusTokens,\n type BadgeColor,\n type BadgeStatus,\n} from \"./badge-colors\";\n\n\nconst badgeVariants = cva(\n \"inline-flex w-fit max-w-full items-center font-medium whitespace-nowrap\",\n {\n variants: {\n variant: {\n solid: \"\",\n dot: \"border border-border text-fg-default\",\n },\n size: {\n sm: \"h-5 px-2 text-label gap-1\",\n md: \"h-6 px-2.5 text-label gap-1.5\",\n lg: \"h-control-xs px-3 text-body gap-1.5\",\n },\n },\n defaultVariants: {\n variant: \"solid\",\n size: \"md\",\n },\n }\n);\n\ninterface BadgeProps\n extends Omit, \"color\">,\n VariantProps {\n color?: BadgeColor;\n /** Product status, intentionally distinct from categorical `color`. */\n status?: BadgeStatus;\n}\n\nconst Badge = forwardRef(\n (\n {\n className,\n variant = \"solid\",\n size = \"md\",\n color = \"gray\",\n status,\n children,\n style,\n ...props\n },\n ref\n ) => {\n const shape = useShape();\n const isSolid = variant === \"solid\";\n const statusColors = status ? badgeStatusTokens[status] : null;\n const categoricalTokens = badgeCategoricalTokens(color);\n const dotSize = size === \"sm\" ? 6 : size === \"lg\" ? 8 : 7;\n\n const colorStyle = isSolid\n ? statusColors\n ? {\n color: statusColors.foreground,\n backgroundColor: statusColors.background,\n borderColor: statusColors.border,\n }\n : {\n color: categoricalTokens.foreground,\n backgroundColor: categoricalTokens.background,\n borderColor: categoricalTokens.border,\n }\n : {};\n\n const dotColor = statusColors\n ? statusColors.icon\n : categoricalTokens.dot;\n\n return (\n \n {!isSolid && (\n \n )}\n {children}\n \n );\n }\n);\n\nBadge.displayName = \"Badge\";\n\nexport { Badge, badgeVariants, badgeColors };\nexport type { BadgeProps, BadgeColor, BadgeStatus };\n", "type": "registry:ui", "target": "components/ui/badge.tsx" }, { "path": "src/components/ui/badge-colors.ts", "content": "/**\n * Badge-private categorical palette and component-token mapping.\n *\n * Categorical colours describe classification only. They deliberately do not\n * create global CSS variables or semantic status tokens.\n */\nconst categoricalPalette = {\n gray: { dot: \"#A3A3A3\", light: \"#E5E5E5\", dark: \"#525252\" },\n red: { dot: \"#EF4444\", light: \"#F8DFDF\", dark: \"#371E1E\" },\n orange: { dot: \"#F97316\", light: \"#FAE6D8\", dark: \"#392517\" },\n amber: { dot: \"#F59E0B\", light: \"#F9ECD6\", dark: \"#382B15\" },\n yellow: { dot: \"#EAB308\", light: \"#F8EFD6\", dark: \"#372E15\" },\n lime: { dot: \"#84CC16\", light: \"#E8F3D8\", dark: \"#273217\" },\n green: { dot: \"#22C55E\", light: \"#DAF2E3\", dark: \"#193122\" },\n emerald: { dot: \"#10B981\", light: \"#D7F0E8\", dark: \"#162F27\" },\n teal: { dot: \"#14B8A6\", light: \"#D8F0ED\", dark: \"#172F2C\" },\n cyan: { dot: \"#06B6D4\", light: \"#D5F0F4\", dark: \"#142F33\" },\n blue: { dot: \"#3B82F6\", light: \"#DDE8F9\", dark: \"#1C2738\" },\n indigo: { dot: \"#6366F1\", light: \"#E3E4F9\", dark: \"#222338\" },\n violet: { dot: \"#8B5CF6\", light: \"#E9E2F9\", dark: \"#282138\" },\n purple: { dot: \"#A855F7\", light: \"#EEE1FA\", dark: \"#2D2039\" },\n fuchsia: { dot: \"#D946EF\", light: \"#F5DFF8\", dark: \"#341E37\" },\n pink: { dot: \"#EC4899\", light: \"#F8DFEB\", dark: \"#371E2B\" },\n rose: { dot: \"#F43F5E\", light: \"#F9DEE3\", dark: \"#381D22\" },\n} as const;\n\nexport type BadgeColor = keyof typeof categoricalPalette;\nexport type BadgeStatus = \"danger\" | \"warning\" | \"info\" | \"neutral\";\n\nexport const badgeStatusTokens: Record = {\n danger: { foreground: \"var(--fg-danger)\", background: \"var(--danger-surface)\", border: \"var(--danger-border)\", icon: \"var(--fg-danger)\" },\n warning: { foreground: \"var(--fg-warning)\", background: \"var(--warning-surface)\", border: \"var(--warning-border)\", icon: \"var(--fg-warning)\" },\n info: { foreground: \"var(--fg-info)\", background: \"var(--info-surface)\", border: \"var(--info-border)\", icon: \"var(--fg-info)\" },\n neutral: { foreground: \"var(--fg-neutral-status)\", background: \"var(--neutral-status-surface)\", border: \"var(--neutral-status-border)\", icon: \"var(--fg-neutral-status)\" },\n};\n\nexport function badgeCategoricalTokens(color: BadgeColor) {\n const palette = categoricalPalette[color];\n return {\n foreground: \"var(--fg-default)\",\n background: `light-dark(${palette.light}, ${palette.dark})`,\n border: \"transparent\",\n dot: color === \"gray\" ? \"var(--fg-muted)\" : palette.dot,\n };\n}\n\nexport const badgeColors = Object.freeze(\n Object.fromEntries(Object.entries(categoricalPalette).map(([name, palette]) => [name, palette.dot])) as Record\n);\n", "type": "registry:ui", "target": "components/ui/badge-colors.ts" } ], "type": "registry:ui" }