Card Grid
An interactive card grid component with smooth hover animations using motion, where hovering over a card expands it to reveal more content.
Installation
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
| Prop | Type | Default | Description |
|---|---|---|---|
items | CardGridItem[] | - | Array of card items to display |
className | string | - | Additional CSS classes |
articleWidth | string | number | "100%" | Width of the expanded article content |
gap | number | 8 | Gap between grid items in pixels |
CardGridItem
| Property | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique identifier for the card |
title | string | Yes | Card title (displayed vertically when active) |
description | string | Yes | Card description (shown when active) |
icon | ReactNode | Yes | Icon component (e.g., from lucide-react) |
href | string | No | Optional link URL (shows "Watch now" button) |
image | string | Yes | Background image URL |
Examples
Basic Usage
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:
- The
hoveredIndexstate updates to the hovered card's index - Motion animates the grid template columns, expanding the hovered card to
10frand others to1fr - The card's title, description, icon, and link fade in with opacity animations
- The image transitions from grayscale to full color and scales from 1.1 to 1
- 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
classNameprop - CSS Variables: Uses theme variables for colors (e.g.,
--border,--card,--foreground) - Motion Props: Customize animations by modifying the
motioncomponent props - Props: Use
articleWidthandgapprops 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
motionas 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-reactfor 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)