{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "hero-liquid-metal", "type": "registry:ui", "description": "Split-layout hero section with responsive LiquidMetal shader visuals, CTA, and tech stack badges", "dependencies": [ "@paper-design/shaders-react" ], "registryDependencies": [ "badge", "button" ], "files": [ { "path": "registry/default/ui/hero-liquid-metal.tsx", "content": "\"use client\"\n\nimport * as React from \"react\"\nimport type { SVGProps } from \"react\"\nimport { LiquidMetal } from \"@paper-design/shaders-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Badge } from \"@/components/ui/badge\"\nimport { Button } from \"@/components/ui/button\"\n\nconst MemoizedLiquidMetal = React.memo(LiquidMetal)\n\ntype LiquidMetalProps = React.ComponentProps\ntype LiquidMetalIcon = React.ComponentType>\n\nexport interface HeroLiquidMetalTechItem {\n name: string\n version?: string\n icon?: LiquidMetalIcon\n}\n\nexport interface HeroLiquidMetalCTAProps {\n label: React.ReactNode\n href: string\n target?: React.HTMLAttributeAnchorTarget\n rel?: string\n onClick?: React.MouseEventHandler\n className?: string\n buttonClassName?: string\n}\n\n/** LiquidMetal shader props that can be passed at the root for convenience */\nexport type HeroLiquidMetalShaderOverrides = Partial<\n Pick<\n LiquidMetalProps,\n | \"width\"\n | \"height\"\n | \"image\"\n | \"colorBack\"\n | \"colorTint\"\n | \"shape\"\n | \"repetition\"\n | \"softness\"\n | \"shiftRed\"\n | \"shiftBlue\"\n | \"distortion\"\n | \"contour\"\n | \"angle\"\n | \"speed\"\n | \"frame\"\n | \"scale\"\n | \"rotation\"\n | \"offsetX\"\n | \"offsetY\"\n | \"fit\"\n | \"originX\"\n | \"originY\"\n | \"minPixelRatio\"\n | \"maxPixelCount\"\n >\n>\n\nexport interface HeroLiquidMetalRootProps\n extends Omit, \"title\">,\n HeroLiquidMetalShaderOverrides {\n srTitle?: string\n title?: React.ReactNode\n subtitle?: React.ReactNode\n description?: React.ReactNode\n showCta?: boolean\n ctaProps?: Partial\n renderCta?: (defaultCta: React.ReactNode) => React.ReactNode\n showBadges?: boolean\n techStack?: HeroLiquidMetalTechItem[]\n renderBadge?: (\n tech: HeroLiquidMetalTechItem,\n index: number,\n defaultBadge: React.ReactNode\n ) => React.ReactNode\n desktopShaderProps?: Partial\n mobileShaderProps?: Partial\n}\n\nexport interface HeroLiquidMetalHeadingProps\n extends Omit, \"title\"> {\n title?: React.ReactNode\n subtitle?: React.ReactNode\n headingClassName?: string\n}\n\nexport interface HeroLiquidMetalDescriptionProps\n extends React.ComponentPropsWithoutRef<\"div\"> {\n description?: React.ReactNode\n descriptionClassName?: string\n}\n\nexport interface HeroLiquidMetalActionsProps\n extends React.ComponentPropsWithoutRef<\"div\"> {\n showCta?: boolean\n ctaProps?: Partial\n renderCta?: (defaultCta: React.ReactNode) => React.ReactNode\n}\n\nexport interface HeroLiquidMetalBadgesProps\n extends React.ComponentPropsWithoutRef<\"div\"> {\n showBadges?: boolean\n techStack?: HeroLiquidMetalTechItem[]\n renderBadge?: (\n tech: HeroLiquidMetalTechItem,\n index: number,\n defaultBadge: React.ReactNode\n ) => React.ReactNode\n}\n\nexport interface HeroLiquidMetalVisualProps\n extends React.ComponentPropsWithoutRef<\"div\"> {\n desktopShaderProps?: Partial\n desktopClassName?: string\n}\n\nexport interface HeroLiquidMetalMobileVisualProps\n extends React.ComponentPropsWithoutRef<\"div\"> {\n mobileShaderProps?: Partial\n}\n\nexport interface HeroLiquidMetalProps extends HeroLiquidMetalRootProps {\n containerClassName?: string\n contentClassName?: string\n headingWrapClassName?: string\n headingClassName?: string\n descriptionWrapClassName?: string\n descriptionClassName?: string\n ctaWrapClassName?: string\n badgesWrapClassName?: string\n visualClassName?: string\n mobileVisualClassName?: string\n}\n\ninterface HeroLiquidMetalContextValue {\n srTitle: string\n title: React.ReactNode\n subtitle: React.ReactNode\n description: React.ReactNode\n showCta: boolean\n mergedCtaProps: HeroLiquidMetalCTAProps\n renderCta?: (defaultCta: React.ReactNode) => React.ReactNode\n showBadges: boolean\n techStack: HeroLiquidMetalTechItem[]\n renderBadge?: HeroLiquidMetalBadgesProps[\"renderBadge\"]\n mergedDesktopShaderProps: Partial\n mergedMobileShaderProps: Partial\n}\n\nconst defaultDesktopShaderProps: Partial = {\n width: 1280,\n height: 720,\n image: \"/cult-icon.svg\",\n colorBack: \"#ffffff00\",\n colorTint: \"#2c5d72\",\n shape: undefined,\n repetition: 6,\n softness: 0.8,\n shiftRed: 1,\n shiftBlue: -1,\n distortion: 0.4,\n contour: 0.4,\n angle: 0,\n speed: 1,\n scale: 0.6,\n fit: \"contain\",\n}\n\nconst defaultMobileShaderProps: Partial = {\n image: \"/cult-icon.svg\",\n colorBack: \"#ffffff00\",\n colorTint: \"#2c5d72\",\n shape: undefined,\n repetition: 6,\n softness: 0.8,\n shiftRed: 1,\n shiftBlue: -1,\n distortion: 0.4,\n contour: 0.4,\n angle: 0,\n speed: 1,\n scale: 0.68,\n fit: \"contain\",\n style: { height: \"100%\", width: \"100%\" },\n}\n\nconst defaultCtaProps: HeroLiquidMetalCTAProps = {\n label: \"Check it out today\",\n href: \"https://aisdkagents.com\",\n target: \"_blank\",\n rel: \"noopener noreferrer\",\n}\n\nconst defaultDescription = (\n <>\n Full-stack vercel ai sdk patterns for workflows, tool calling, and agent\n orchestration. Built with{\" \"}\n ai sdk v6 and{\" \"}\n shadcn/ui.\n Headless, themable, practical.\n \n)\n\nconst defaultTechStack: HeroLiquidMetalTechItem[] = [\n {\n name: \"Next.js\",\n version: \"v16\",\n icon: NextjsIcon,\n },\n {\n name: \"AI SDK\",\n version: \"v6\",\n icon: AISDKIcon,\n },\n]\n\nconst HeroLiquidMetalContext = React.createContext<\n HeroLiquidMetalContextValue | undefined\n>(undefined)\n\nfunction useHeroLiquidMetalContext() {\n const context = React.useContext(HeroLiquidMetalContext)\n if (!context) {\n throw new Error(\n \"HeroLiquidMetal components must be used within HeroLiquidMetalRoot\"\n )\n }\n return context\n}\n\nexport function useHeroLiquidMetal() {\n return useHeroLiquidMetalContext()\n}\n\nexport const HeroLiquidMetalRoot = React.forwardRef<\n HTMLElement,\n HeroLiquidMetalRootProps\n>(({ className, children, srTitle = \"AI SDK Agents\", title = \n AI SDK Agents\n , subtitle = \"Copy and Paste\", description = defaultDescription, showCta = true, ctaProps, renderCta, showBadges = true, techStack = defaultTechStack, renderBadge, desktopShaderProps, mobileShaderProps, width, height, image, colorBack, colorTint, shape, repetition, softness, shiftRed, shiftBlue, distortion, contour, angle, speed, frame, scale, rotation, offsetX, offsetY, fit, originX, originY, minPixelRatio, maxPixelCount, ...props }, ref) => {\n const mergedCtaProps = React.useMemo(\n () => ({\n ...defaultCtaProps,\n ...ctaProps,\n }),\n [ctaProps]\n )\n\n const shaderOverrides = React.useMemo((): Partial => {\n const overrides: Partial = {}\n if (width !== undefined) overrides.width = width\n if (height !== undefined) overrides.height = height\n if (image !== undefined) overrides.image = image\n if (colorBack !== undefined) overrides.colorBack = colorBack\n if (colorTint !== undefined) overrides.colorTint = colorTint\n if (shape !== undefined) overrides.shape = shape\n if (repetition !== undefined) overrides.repetition = repetition\n if (softness !== undefined) overrides.softness = softness\n if (shiftRed !== undefined) overrides.shiftRed = shiftRed\n if (shiftBlue !== undefined) overrides.shiftBlue = shiftBlue\n if (distortion !== undefined) overrides.distortion = distortion\n if (contour !== undefined) overrides.contour = contour\n if (angle !== undefined) overrides.angle = angle\n if (speed !== undefined) overrides.speed = speed\n if (frame !== undefined) overrides.frame = frame\n if (scale !== undefined) overrides.scale = scale\n if (rotation !== undefined) overrides.rotation = rotation\n if (offsetX !== undefined) overrides.offsetX = offsetX\n if (offsetY !== undefined) overrides.offsetY = offsetY\n if (fit !== undefined) overrides.fit = fit\n if (originX !== undefined) overrides.originX = originX\n if (originY !== undefined) overrides.originY = originY\n if (minPixelRatio !== undefined) overrides.minPixelRatio = minPixelRatio\n if (maxPixelCount !== undefined) overrides.maxPixelCount = maxPixelCount\n return overrides\n }, [\n width,\n height,\n image,\n colorBack,\n colorTint,\n shape,\n repetition,\n softness,\n shiftRed,\n shiftBlue,\n distortion,\n contour,\n angle,\n speed,\n frame,\n scale,\n rotation,\n offsetX,\n offsetY,\n fit,\n originX,\n originY,\n minPixelRatio,\n maxPixelCount,\n ])\n\n const mergedDesktopShaderProps = React.useMemo(\n () => ({\n ...defaultDesktopShaderProps,\n ...shaderOverrides,\n ...desktopShaderProps,\n }),\n [shaderOverrides, desktopShaderProps]\n )\n\n const mergedMobileShaderProps = React.useMemo(\n () => ({\n ...defaultMobileShaderProps,\n ...shaderOverrides,\n ...mobileShaderProps,\n style: {\n ...(defaultMobileShaderProps.style as React.CSSProperties),\n ...(mobileShaderProps?.style as React.CSSProperties | undefined),\n },\n }),\n [shaderOverrides, mobileShaderProps]\n )\n\n const contextValue = React.useMemo(\n () => ({\n srTitle,\n title,\n subtitle,\n description,\n showCta,\n mergedCtaProps,\n renderCta,\n showBadges,\n techStack,\n renderBadge,\n mergedDesktopShaderProps,\n mergedMobileShaderProps,\n }),\n [\n srTitle,\n title,\n subtitle,\n description,\n showCta,\n mergedCtaProps,\n renderCta,\n showBadges,\n techStack,\n renderBadge,\n mergedDesktopShaderProps,\n mergedMobileShaderProps,\n ]\n )\n\n return (\n \n \n

{srTitle}

\n {children}\n \n
\n )\n})\nHeroLiquidMetalRoot.displayName = \"HeroLiquidMetalRoot\"\n\nexport function HeroLiquidMetalContainer({\n className,\n ...props\n}: React.ComponentPropsWithoutRef<\"div\">) {\n return (\n \n )\n}\n\nexport function HeroLiquidMetalContent({\n className,\n ...props\n}: React.ComponentPropsWithoutRef<\"div\">) {\n return (\n \n )\n}\n\nexport function HeroLiquidMetalHeading({\n className,\n title,\n subtitle,\n headingClassName,\n children,\n ...props\n}: HeroLiquidMetalHeadingProps) {\n const context = useHeroLiquidMetalContext()\n const resolvedTitle = title ?? context.title\n const resolvedSubtitle = subtitle ?? context.subtitle\n\n return (\n \n {children ?? (\n
\n \n {resolvedTitle}
\n {resolvedSubtitle}\n \n
\n )}\n \n )\n}\n\nexport function HeroLiquidMetalDescription({\n className,\n description,\n descriptionClassName,\n children,\n ...props\n}: HeroLiquidMetalDescriptionProps) {\n const context = useHeroLiquidMetalContext()\n const resolvedDescription = description ?? context.description\n\n return (\n \n {children ?? (\n \n {resolvedDescription}\n

\n )}\n \n )\n}\n\nexport function HeroLiquidMetalActions({\n className,\n showCta,\n ctaProps,\n renderCta,\n children,\n ...props\n}: HeroLiquidMetalActionsProps) {\n const context = useHeroLiquidMetalContext()\n const shouldShowCta = showCta ?? context.showCta\n const resolvedCtaProps = { ...context.mergedCtaProps, ...ctaProps }\n const resolvedRenderCta = renderCta ?? context.renderCta\n\n if (!shouldShowCta) {\n return null\n }\n\n const defaultCta = \n\n return (\n \n {children ??\n (resolvedRenderCta ? resolvedRenderCta(defaultCta) : defaultCta)}\n \n )\n}\n\nexport function HeroLiquidMetalCTA({\n label,\n href,\n target,\n rel,\n onClick,\n className,\n buttonClassName,\n}: HeroLiquidMetalCTAProps) {\n return (\n \n \n \n )\n}\n\nexport function HeroLiquidMetalBadges({\n className,\n showBadges,\n techStack,\n renderBadge,\n ...props\n}: HeroLiquidMetalBadgesProps) {\n const context = useHeroLiquidMetalContext()\n const shouldShowBadges = showBadges ?? context.showBadges\n const resolvedTechStack = techStack ?? context.techStack\n const resolvedRenderBadge = renderBadge ?? context.renderBadge\n\n if (!shouldShowBadges) {\n return null\n }\n\n return (\n \n {resolvedTechStack.map((tech, index) => {\n const Icon = tech.icon\n const defaultBadge = (\n \n {Icon ? : null}\n {tech.name}\n {tech.version ? (\n \n {tech.version}\n \n ) : null}\n \n )\n\n if (resolvedRenderBadge) {\n return (\n \n {resolvedRenderBadge(tech, index, defaultBadge)}\n \n )\n }\n\n return defaultBadge\n })}\n \n )\n}\n\nexport function HeroLiquidMetalVisual({\n className,\n desktopClassName,\n desktopShaderProps,\n ...props\n}: HeroLiquidMetalVisualProps) {\n const context = useHeroLiquidMetalContext()\n const resolvedDesktopShaderProps = {\n ...context.mergedDesktopShaderProps,\n ...desktopShaderProps,\n }\n\n return (\n