'use client'; import * as React from 'react'; import { cva, type VariantProps } from '@/lib/cva'; import * as Slot from 'radix-ui/slot'; import { cn } from '@/lib/utils'; import { colorPalettes, focusRing, invalidState } from '@/lib/cva-presets'; const badgeVariants = cva( cn( 'inline-flex w-fit shrink-0 items-center justify-center overflow-hidden rounded-xs border border-transparent font-medium whitespace-nowrap', 'transition-[color,box-shadow] duration-(--ui-duration-fast) ease-(--ui-ease-standard)', '[&>svg]:pointer-events-none', focusRing, invalidState ), { variants: { /* * The same treatments Button uses, but hover only applies when the badge * has been made into a link with `asChild` — a plain badge is not * interactive and should not light up under the cursor. * * CBAR's Badge set (`2456:5146`) draws two of these five, and its * "surface" is this kit's `surface`: measured through the Figma bridge, * all sixteen of those variants carry * `box-shadow: 0 0 1px 0 inset` — a 1px border drawn as an inset * shadow rather than as a stroke — which is the tint-plus-outline meant * here. `subtle`, `outline` and `ghost` are the kit's own additions. */ variant: { solid: 'bg-(--ctl-solid) text-(--ctl-solid-fg) [a&]:hover:bg-(--ctl-solid-hover)', subtle: 'bg-(--ctl-subtle) text-(--ctl-fg) [a&]:hover:bg-(--ctl-subtle-hover)', surface: 'bg-(--ctl-subtle) text-(--ctl-fg) border-(--ctl-border) [a&]:hover:bg-(--ctl-subtle-hover)', outline: 'border-(--ctl-border) text-(--ctl-fg) [a&]:hover:bg-(--ctl-subtle)', /* * One-word shorthands for the statuses a badge carries most often. * Like Button's, they paint from the semantic roles rather than the * `--ctl-*` palette roles, so they pin their colour and stay clear of * `colorPalette` instead of competing with it on specificity. */ default: 'bg-primary text-primary-foreground [a&]:hover:bg-primary/90', /** * Quiet grey badge. Note this is the kit's neutral `--secondary` role, * not the turquoise `colorPalette="secondary"` ramp — the two names * collide, which is the price of keeping the old shorthands. */ secondary: 'bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90', destructive: 'bg-destructive text-destructive-foreground focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90', success: 'bg-success text-success-foreground [a&]:hover:bg-success/90', warning: 'bg-warning text-warning-foreground [a&]:hover:bg-warning/90', ghost: '[a&]:hover:bg-accent [a&]:hover:text-accent-foreground', }, colorPalette: colorPalettes, size: { xs: 'h-4 gap-1 px-1 text-[10px] [&>svg]:size-2.5', sm: 'h-5 gap-1 px-1.5 text-xs [&>svg]:size-3', md: 'h-6 gap-1.5 px-2 text-sm [&>svg]:size-3.5', lg: 'h-7 gap-2 px-2.5 text-sm [&>svg]:size-4', }, }, defaultVariants: { variant: 'solid', /* CBAR draws its default badge turquoise, but that traces back to the same palette transposition its Button and Avatar show. Every control in this kit defaults to the navy its semantic layer calls primary. */ colorPalette: 'primary', size: 'md', }, } ); export interface BadgeProps extends React.ComponentProps<'span'>, VariantProps { /** Render the child element instead of a `` — e.g. to make it a link. */ asChild?: boolean; } /** * Compact status or category marker. * * A badge is not a button. If it needs to be clickable, use `asChild` with an * anchor so it is announced and focused as a link. */ function Badge({ className, variant = 'solid', colorPalette, size, asChild = false, ...props }: BadgeProps) { const Comp = asChild ? Slot.Root : 'span'; return ( ); } export { Badge, badgeVariants };