Saltar al contenido
ssntl/ui

Tag

Una píldora de estado donde el estado siempre es una palabra; el punto solo la acompaña.

LiveNewPayment failed2026

Instalación

$ pnpm dlx shadcn@latest add @ssntl/tag

Uso

tsx
import { Tag } from "@/components/ui/tag"

<Tag tone="live">En vivo</Tag>
<Tag tone="danger">Pago fallido</Tag>
  • El estado siempre lleva una palabra: el punto solo la acompaña, así se entiende sin color.
  • live para disponible o listo, accent para Nuevo, danger para errores; sin tono para años y conteos.
  • No uses etiquetas para metadatos comunes que se leen bien como texto.

Código

components/ui/tag.tsx
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"

import { cn } from "@/lib/utils"

// Essential Design `es-tag`: a 28px pill on `secondary`. State always carries a
// word, never color alone: the dot is decorative, the text is the signal.
const tagVariants = cva(
  "inline-flex h-7 shrink-0 items-center gap-2 rounded-full bg-secondary px-3 text-caption font-medium whitespace-nowrap",
  {
    variants: {
      tone: {
        neutral: "text-muted-foreground",
        live: "text-green-600 dark:text-green-400 [&_[data-dot]]:bg-green-400",
        accent: "text-foreground [&_[data-dot]]:bg-blue-600",
        danger: "text-destructive [&_[data-dot]]:bg-destructive",
      },
    },
    defaultVariants: { tone: "neutral" },
  }
)

function Tag({
  tone,
  dot = tone !== "neutral" && tone !== undefined,
  className,
  children,
  ...props
}: React.ComponentProps<"span"> &
  VariantProps<typeof tagVariants> & {
    /** A 7px dot before the word. On by default for live, accent and danger. */
    dot?: boolean
  }) {
  return (
    <span
      data-slot="tag"
      className={cn(tagVariants({ tone, className }))}
      {...props}
    >
      {dot && (
        <span
          aria-hidden
          data-dot
          className="size-1.5 shrink-0 rounded-full bg-current"
        />
      )}
      {children}
    </span>
  )
}

export { Tag, tagVariants }