{
  "$schema": "https://blode.co/ui/schema/registry-item.json",
  "name": "bubble",
  "title": "Bubble",
  "author": "Matthew Blode",
  "description": "A message surface for chat interfaces, with variants, alignment, reactions, and composable content.",
  "dependencies": ["@base-ui/react"],
  "files": [
    {
      "path": "ui/bubble.tsx",
      "content": "import { mergeProps } from \"@base-ui/react/merge-props\";\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { cva } from \"class-variance-authority\";\nimport type { VariantProps } from \"class-variance-authority\";\nimport type * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst BubbleGroup = ({ className, ...props }: React.ComponentProps<\"div\">) => (\n  <div\n    className={cn(\"flex min-w-0 flex-col gap-2\", className)}\n    data-slot=\"bubble-group\"\n    {...props}\n  />\n);\n\nconst bubbleVariants = cva(\n  \"group/bubble relative flex w-fit min-w-0 max-w-[80%] flex-col gap-1 group-data-[align=end]/message:self-end data-[align=end]:self-end data-[variant=ghost]:max-w-full\",\n  {\n    defaultVariants: {\n      variant: \"default\",\n    },\n    variants: {\n      variant: {\n        default:\n          \"*:data-[slot=bubble-content]:bg-primary *:data-[slot=bubble-content]:text-primary-foreground [&>[data-slot=bubble-content]:is(button,a):hover]:bg-primary/80\",\n        destructive:\n          \"*:data-[slot=bubble-content]:bg-destructive/10 *:data-[slot=bubble-content]:text-destructive dark:*:data-[slot=bubble-content]:bg-destructive/20 [&>[data-slot=bubble-content]:is(button,a):hover]:bg-destructive/20 dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-destructive/30\",\n        ghost:\n          \"border-none *:data-[slot=bubble-content]:rounded-none *:data-[slot=bubble-content]:bg-transparent *:data-[slot=bubble-content]:p-0 [&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted [&>[data-slot=bubble-content]:is(button,a):hover]:text-foreground dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted/50\",\n        muted:\n          \"*:data-[slot=bubble-content]:bg-muted [&>[data-slot=bubble-content]:is(button,a):hover]:bg-[color-mix(in_oklch,var(--muted),var(--foreground)_5%)]\",\n        outline:\n          \"*:data-[slot=bubble-content]:border-border *:data-[slot=bubble-content]:bg-background [&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted [&>[data-slot=bubble-content]:is(button,a):hover]:text-foreground dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-input/30\",\n        secondary:\n          \"*:data-[slot=bubble-content]:bg-secondary *:data-[slot=bubble-content]:text-secondary-foreground [&>[data-slot=bubble-content]:is(button,a):hover]:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)]\",\n        tinted:\n          \"*:data-[slot=bubble-content]:bg-[oklch(from_var(--primary)_0.93_calc(c*0.4)_h)] *:data-[slot=bubble-content]:text-foreground dark:*:data-[slot=bubble-content]:bg-[oklch(from_var(--primary)_0.3_calc(c*0.4)_h)] [&>[data-slot=bubble-content]:is(button,a):hover]:bg-[oklch(from_var(--primary)_0.88_calc(c*0.5)_h)] dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-[oklch(from_var(--primary)_0.35_calc(c*0.5)_h)]\",\n      },\n    },\n  },\n);\n\nconst Bubble = ({\n  variant = \"default\",\n  align = \"start\",\n  className,\n  ...props\n}: React.ComponentProps<\"div\"> &\n  VariantProps<typeof bubbleVariants> & {\n    align?: \"start\" | \"end\";\n  }) => (\n  <div\n    className={cn(bubbleVariants({ variant }), className)}\n    data-align={align}\n    data-slot=\"bubble\"\n    data-variant={variant}\n    {...props}\n  />\n);\n\nconst BubbleContent = ({ className, render, ...props }: useRender.ComponentProps<\"div\">) =>\n  useRender({\n    defaultTagName: \"div\",\n    props: mergeProps<\"div\">(\n      {\n        className: cn(\n          \"w-fit min-w-0 max-w-full overflow-hidden wrap-break-word rounded-3xl border border-transparent px-3 py-2.5 text-sm leading-relaxed group-data-[align=end]/bubble:self-end [button,a]:transition-colors [button,a]:outline-none [button,a]:focus-visible:border-ring [button,a]:focus-visible:ring-3 [button,a]:focus-visible:ring-ring/30 [button]:text-left\",\n          className,\n        ),\n      },\n      props,\n    ),\n    render,\n    state: {\n      slot: \"bubble-content\",\n    },\n  });\n\nconst bubbleReactionsVariants = cva(\n  \"absolute z-10 flex w-fit shrink-0 items-center justify-center gap-1 rounded-full bg-muted px-1.5 py-0.5 text-sm ring-3 ring-card has-[button]:p-0\",\n  {\n    defaultVariants: {\n      align: \"end\",\n      side: \"bottom\",\n    },\n    variants: {\n      align: {\n        end: \"right-3\",\n        start: \"left-3\",\n      },\n      side: {\n        bottom: \"bottom-0 translate-y-3/4\",\n        top: \"-translate-y-3/4 top-0\",\n      },\n    },\n  },\n);\n\nconst BubbleReactions = ({\n  side = \"bottom\",\n  align = \"end\",\n  className,\n  ...props\n}: React.ComponentProps<\"div\"> & {\n  align?: \"start\" | \"end\";\n  side?: \"top\" | \"bottom\";\n}) => (\n  <div\n    className={cn(bubbleReactionsVariants({ align, side }), className)}\n    data-align={align}\n    data-side={side}\n    data-slot=\"bubble-reactions\"\n    {...props}\n  />\n);\n\nexport { BubbleGroup, Bubble, BubbleContent, BubbleReactions };\n",
      "type": "registry:ui",
      "target": ""
    }
  ],
  "type": "registry:ui"
}
