Pluto UIpluto/ui
Components

Card Grid

An interactive card grid component with smooth hover animations using motion, where hovering over a card expands it to reveal more content.

// Code for card-grid/basic

Installation

Loading installation instructions...

Usage

import { CardGrid } from "@/components/pluto-ui/card-grid"
import { CardGrid, type CardGridItem } from "@/components/pluto-ui/card-grid"
import { Sparkles, Layout } from "lucide-react"

const items: CardGridItem[] = [
  {
    id: "1",
    title: "The Craft",
    description: "Gain the confidence to build anything you envision.",
    icon: <Sparkles />,
    href: "#",
    image: "https://picsum.photos/720/720?random=12",
  },
  {
    id: "2",
    title: "CSS Animation",
    description: "Master CSS animations from your very first set of @keyframes.",
    icon: <Layout />,
    href: "#",
    image: "https://picsum.photos/720/720?random=17",
  },
]

<CardGrid items={items} />

Props

PropTypeDefaultDescription
itemsCardGridItem[]-Array of card items to display
classNamestring-Additional CSS classes
articleWidthstring | number"100%"Width of the expanded article content
gapnumber8Gap between grid items in pixels

CardGridItem

PropertyTypeRequiredDescription
idstringYesUnique identifier for the card
titlestringYesCard title (displayed vertically when active)
descriptionstringYesCard description (shown when active)
iconReactNodeYesIcon component (e.g., from lucide-react)
hrefstringNoOptional link URL (shows "Watch now" button)
imagestringYesBackground image URL

Examples

Basic Usage

// Code for card-grid/basic

Features

  • Smooth Animations: Powered by motion for fluid transitions and animations
  • Dynamic Grid Layout: Hovered card expands to 10fr while others remain at 1fr
  • React State Management: Uses React state to track hovered card for predictable behavior
  • Image Effects: Non-hovered cards show grayscale images with brightness boost
  • Content Reveal: Description, icon, and link fade in smoothly when hovering
  • Theme-aware: Fully supports light and dark themes using CSS variables
  • Responsive: Adapts to different screen sizes using container queries
  • Type-safe: Full TypeScript support
  • No CSS File Required: All styling handled with Tailwind and motion

How It Works

The component uses React state (useState) to track which card is currently hovered. When you hover over a card:

  1. The hoveredIndex state updates to the hovered card's index
  2. Motion animates the grid template columns, expanding the hovered card to 10fr and others to 1fr
  3. The card's title, description, icon, and link fade in with opacity animations
  4. The image transitions from grayscale to full color and scales from 1.1 to 1
  5. All animations use consistent timing and easing for smooth transitions

The component uses motion.ul to animate the grid layout and motion components for individual card elements.

Animation Details

  • Grid Layout: Smoothly transitions grid template columns when hovering
  • Title Animation: Opacity changes from 0.6 to 1 on hover
  • Description & Link: Fade in with opacity 0 to 0.8, with a slight delay for staggered effect
  • Icon Animation: Opacity changes from 0.6 to 1 on hover
  • Image Animation: Filter transitions from grayscale(1) brightness(1.5) to grayscale(0) brightness(1), with scale from 1.1 to 1
  • Timing: All animations use 0.6s duration with easeInOut easing

Styling

The component uses Tailwind CSS for all styling and can be customized through:

  • Tailwind Classes: Pass custom classes via the className prop
  • CSS Variables: Uses theme variables for colors (e.g., --border, --card, --foreground)
  • Motion Props: Customize animations by modifying the motion component props
  • Props: Use articleWidth and gap props to adjust layout

Customization

Custom Animations

You can customize animations by modifying the motion props:

<motion.li
  animate={{ scale: 1.05 }}
  transition={{ duration: 0.3 }}
>
  {/* content */}
</motion.li>

Custom Styling

Override styles using Tailwind classes:

<CardGrid 
  items={items} 
  className="custom-class"
  gap={16}
  articleWidth="120%"
/>

Notes

  • Requires motion as a dependency
  • The component uses container queries (containerType: "inline-size") for responsive sizing
  • Images are masked with a radial gradient for a unique visual effect
  • The hovered card's content (description, icon, and link) fades in with a slight delay for a staggered effect
  • Icons should be SVG components from libraries like lucide-react for best results
  • The component uses a monospace font for titles and text
  • When you hover out, the grid smoothly returns to the default state (all cards at 1fr)