Hang on there! We will do a proper launch soon and this is just for testing purposes. 🚀
kinetic-kit
beta

Gooey Button

A versatile UI element that provides a flexible and customizable way to organize and display menu items within your application.

Examples

Basic

Custom Icon

Code

import * as React from 'react';
import { Slot, Slottable } from '@radix-ui/react-slot';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';
import { ArrowRightIcon } from 'lucide-react';
import { Transition, Variant } from 'framer-motion';

const gooeyButtonVariants = cva(
  'group relative  inline-flex items-center justify-center whitespace-nowrap text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
  {
    variants: {
      variant: {
        default: ' text-white bg-zinc-700   hover:bg-zinc-700/90',
        destructive: 'bg-red-700 text-white hover:bg-red-700/90',
        outline:
          'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
        secondary:
          'bg-secondary text-secondary-foreground hover:bg-secondary/80',
        ghost: 'hover:bg-accent hover:text-accent-foreground',
        link: ' text-zinc-700 border-2 border-black bg-white underline-black underline-offset-4 hover:underline',
      },
      size: {
        default:
          'h-9 rounded-xs px-4 py-2 text-md font-senibold leading-2 tracking-wide',
        sm: 'h-9 rounded-sm px-3',
        lg: 'h-14 rounded-md px-5 py-3 text-lg font-bold leading-5 tracking-wide',
        xl: 'h-16 rounded-xl px-6 py-5 text-xl font-bold leading-10',
        icon: 'h-10 w-10',
      },
    },
    defaultVariants: {
      variant: 'default',
      size: 'lg',
    },
  }
);

interface IconProps {
  Icon?: React.ElementType;
  iconPlacement?: 'left' | 'right';
}

interface IconRefProps {
  Icon?: never;
  iconPlacement?: undefined;
}

export interface GooeyButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof gooeyButtonVariants> {
  containerClassName?: string;
  asChild?: boolean;
  variants?: {
    hidden: Variant;
    visible: Variant;
  };
  transition?: Transition;
}

const defaultVariants = {
  hidden: { opacity: 0 },
  visible: { opacity: 1 },
};

export type GooeyButtonIconProps = IconProps | IconRefProps;

const GooeyButton = React.forwardRef<
  HTMLButtonElement,
  GooeyButtonProps & GooeyButtonIconProps
>(
  (
    {
      className,
      containerClassName,
      variant,
      size,
      asChild = false,
      Icon = ArrowRightIcon,
      iconPlacement = 'right',
      variants = defaultVariants,
      transition,
      ...props
    },
    ref
  ) => {
    const Comp = asChild ? Slot : 'button';
    return (
      <div
        className={cn(
          'flex size-full items-center justify-center [filter:_url(#gooey)]',
          containerClassName
        )}
      >
        <Comp
          className={cn(gooeyButtonVariants({ variant, size, className }))}
          ref={ref}
          {...props}
        >
          {Icon && iconPlacement === 'left' && (
            <div className='absolute left-0 -z-10 grid aspect-square h-full w-auto translate-x-0 place-items-center rounded-[inherit] bg-inherit [transition:_all_1s_cubic-bezier(0.64,0.23,0.26,0.87)] group-hover:-translate-x-[130%]'>
              <Icon />
            </div>
          )}
          <Slottable>{props.children}</Slottable>
          {Icon && iconPlacement === 'right' && (
            <div className='absolute right-0 -z-10 grid aspect-square h-full w-auto translate-x-0 place-items-center rounded-[inherit] bg-inherit [transition:_all_1s_cubic-bezier(0.64,0.23,0.26,0.87)] group-hover:translate-x-[130%]'>
              <Icon />
            </div>
          )}
        </Comp>
        <svg
          className='absolute hidden'
          width='0'
          height='0'
          xmlns='http://www.w3.org/2000/svg'
          version='1.1'
        >
          <defs>
            <filter id='gooey'>
              <feGaussianBlur
                in='SourceGraphic'
                stdDeviation='10'
                result='blur'
              />
              <feColorMatrix
                in='blur'
                mode='matrix'
                values='1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 20 -10'
                result='gooey'
              />
              <feComposite in='SourceGraphic' in2='gooey' operator='atop' />
            </filter>
          </defs>
        </svg>
      </div>
    );
  }
);
GooeyButton.displayName = 'GooeyButton';

export { GooeyButton, gooeyButtonVariants };

Component API

GooeyButton

PropTypeDefaultDescription
childrenReact.ReactNodeThe content to display in the button.
asChildbooleanfalseReplaces the button tag with the provided child.
IconReact.ElementType<ArrowRightIcon />The icon to display in the button.
iconPlacement'left' | 'right''right'The placement of the icon in the button.
variant'default' | 'destructive'
| 'outline' | 'secondary'
| 'ghost' | 'link'
'default'The visual style of the button.
size'default' | 'sm'
| 'lg' | 'icon'
'default'The size of the button.
refReact.Ref<HTMLButtonElement>Provides a reference to the button DOM element.
classNamestringAdditional classes to apply to the button.
...propsHTMLButtonElementAdditional props to pass to the button element.