Velora UI
All components

Bento Grid

Feature bento with hover-reveal CTAs and rich backgrounds.

1.0 KB gzippedDeps: lucide-reactReduced-motion safeBase UI & Radix compatible

Patterns

Maskable backdrops.

Meteors

Streaks of light.

Installation

npx shadcn@latest add https://velora.colorlib.com/r/bento-grid.json

Installs with: lucide-react

Source

import { ArrowRightIcon } from "lucide-react";

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

interface BentoGridProps extends React.HTMLAttributes<HTMLDivElement> {
  children: React.ReactNode;
}

export function BentoGrid({ className, children, ...props }: BentoGridProps) {
  return (
    <div
      data-slot="bento-grid"
      className={cn(
        "grid w-full auto-rows-[22rem] grid-cols-1 gap-4 md:grid-cols-3",
        className
      )}
      {...props}
    >
      {children}
    </div>
  );
}

interface BentoCardProps extends React.HTMLAttributes<HTMLDivElement> {
  name: string;
  description: string;
  /** Visual filling the card behind the text (chart, image, animation…) */
  background?: React.ReactNode;
  icon?: React.ReactNode;
  href?: string;
  cta?: string;
}

export function BentoCard({
  name,
  description,
  background,
  icon,
  href,
  cta = "Learn more",
  className,
  ...props
}: BentoCardProps) {
  return (
    <div
      data-slot="bento-card"
      className={cn(
        "group relative flex flex-col justify-end overflow-hidden rounded-2xl border bg-card transition-shadow duration-300 hover:shadow-xl hover:shadow-primary/5",
        className
      )}
      {...props}
    >
      {background && (
        <div className="absolute inset-0 overflow-hidden transition-transform duration-500 ease-out group-hover:scale-[1.04] motion-reduce:transition-none">
          {background}
        </div>
      )}
      <div
        aria-hidden
        className="pointer-events-none absolute inset-0 bg-gradient-to-t from-card via-card/60 to-transparent"
      />
      <div
        className={cn(
          "pointer-events-none relative z-10 flex flex-col gap-1 p-6 transition-transform duration-300 ease-out motion-reduce:transition-none",
          href && "group-hover:-translate-y-7"
        )}
      >
        {icon && (
          <div className="mb-2 w-fit text-primary [&_svg]:size-8">{icon}</div>
        )}
        <h3 className="text-lg font-semibold text-card-foreground">{name}</h3>
        <p className="text-sm text-muted-foreground">{description}</p>
      </div>
      {href && (
        <div className="absolute inset-x-0 bottom-0 z-10 translate-y-full p-6 pt-0 transition-transform duration-300 ease-out group-hover:translate-y-0 motion-reduce:translate-y-0">
          <a
            href={href}
            className="inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline"
          >
            {cta}
            <ArrowRightIcon className="size-4 transition-transform group-hover:translate-x-0.5" />
          </a>
        </div>
      )}
    </div>
  );
}