"use client"; import {motion, type SpringOptions, type Transition, useMotionValue, useSpring} from "motion/react"; import * as React from "react"; import {cn} from "@/lib/utilities"; import styles from "./bubble-background.module.css"; /** Configurable bubble color channels consumed by the CSS module. */ export interface BubbleBackgroundColors { /** Primary bubble RGB triplet. @default "18,113,255" */ first: string; /** Secondary bubble RGB triplet. @default "221,74,255" */ second: string; /** Tertiary bubble RGB triplet. @default "0,220,255" */ third: string; /** Fourth bubble RGB triplet. @default "200,50,50" */ fourth: string; /** Fifth bubble RGB triplet. @default "180,180,50" */ fifth: string; /** Sixth bubble RGB triplet used by the interactive layer. @default "140,100,255" */ sixth: string; } /** Props accepted by {@link BubbleBackground}. */ export interface BubbleBackgroundProps extends React.HTMLAttributes { /** Enables mouse-following motion for the interactive bubble layer. @default false */ interactive?: boolean; /** Spring configuration applied to the interactive bubble motion. @default {stiffness: 100, damping: 20} */ transition?: SpringOptions; /** Overrides the CSS custom property color palette for the bubbles. @default {first: "18,113,255", second: "221,74,255", third: "0,220,255", fourth: "200,50,50", fifth: "180,180,50", sixth: "140,100,255"} */ colors?: BubbleBackgroundColors; } type BubbleStyleProperties = React.CSSProperties & { "--ac-bubble-first-color": string; "--ac-bubble-second-color": string; "--ac-bubble-third-color": string; "--ac-bubble-fourth-color": string; "--ac-bubble-fifth-color": string; "--ac-bubble-sixth-color": string; }; /** * Renders drifting gradient bubbles with an optional interactive cursor follower. * * @remarks * - Animated component using the `motion` library * - Renders a `
` element * - Styling via CSS Modules with `--ac-*` custom properties * - Client-side only (`"use client"` directive) * * @example * ```tsx * * ``` * * @see {@link BubbleBackgroundProps} for available props */ const BubbleBackground = React.forwardRef( ( { className, children, interactive = false, transition = {stiffness: 100, damping: 20}, colors = { first: "18,113,255", second: "221,74,255", third: "0,220,255", fourth: "200,50,50", fifth: "180,180,50", sixth: "140,100,255", }, ...props }, ref, ) => { const containerRef = React.useRef(null); React.useImperativeHandle(ref, () => containerRef.current!, []); const mouseX = useMotionValue(0); const mouseY = useMotionValue(0); const springX = useSpring(mouseX, transition); const springY = useSpring(mouseY, transition); React.useEffect(() => { if (!interactive) { return; } const currentContainer = containerRef.current; if (!currentContainer) { return; } const handleMouseMove = (event: MouseEvent): void => { const rect = currentContainer.getBoundingClientRect(); const centerX = rect.left + rect.width / 2; const centerY = rect.top + rect.height / 2; mouseX.set(event.clientX - centerX); mouseY.set(event.clientY - centerY); }; currentContainer.addEventListener("mousemove", handleMouseMove); return () => { currentContainer.removeEventListener("mousemove", handleMouseMove); }; }, [interactive, mouseX, mouseY]); const style: BubbleStyleProperties = { "--ac-bubble-first-color": colors.first, "--ac-bubble-second-color": colors.second, "--ac-bubble-third-color": colors.third, "--ac-bubble-fourth-color": colors.fourth, "--ac-bubble-fifth-color": colors.fifth, "--ac-bubble-sixth-color": colors.sixth, }; return (
{interactive ? ( ) : null}
{children}
); }, ); BubbleBackground.displayName = "BubbleBackground"; export {BubbleBackground};