my/ui

Command Palette

Search for a command to run...

All components

Focus Cards

cards

Aceternity UI component.

responsive · 640px

Install

Same command in any shadcn project — React (Vite/CRA), Next.js, Remix, Astro, and more:

$npx shadcn@latest add https://your-domain/r/focus-cards.json

Usage

"use client";

import { FocusCards } from "@/registry/aceternity-ui/focus-cards";

export default function Demo() {
  const cards = [
    {
      title: "Forest in the mountains",
      src: "https://picsum.photos/seed/forest/800/600",
    },
    {
      title: "City lights at night",
      src: "https://picsum.photos/seed/city/800/600",
    },
    {
      title: "Ocean waves at sunset",
      src: "https://picsum.photos/seed/ocean/800/600",
    },
    {
      title: "Desert dunes",
      src: "https://picsum.photos/seed/desert/800/600",
    },
    {
      title: "Snowy mountain peak",
      src: "https://picsum.photos/seed/mountain/800/600",
    },
    {
      title: "Tropical rainforest",
      src: "https://picsum.photos/seed/rainforest/800/600",
    },
  ];

  return (
    <div className="w-full max-w-5xl mx-auto py-10 px-4">
      <FocusCards cards={cards} />
    </div>
  );
}

Component source

"use client";

import React, { useState } from "react";
import { cn } from "@/lib/utils";

export const Card = React.memo(
  ({
    card,
    index,
    hovered,
    setHovered,
  }: {
    card: any;
    index: number;
    hovered: number | null;
    setHovered: React.Dispatch<React.SetStateAction<number | null>>;
  }) => (
    <div
      onMouseEnter={() => setHovered(index)}
      onMouseLeave={() => setHovered(null)}
      className={cn(
        "rounded-lg relative bg-gray-100 dark:bg-neutral-900 overflow-hidden h-60 md:h-96 w-full transition-all duration-300 ease-out",
        hovered !== null && hovered !== index && "blur-sm scale-[0.98]"
      )}
    >
      <img
        src={card.src}
        alt={card.title}
        className="object-cover absolute inset-0"
      />
      <div
        className={cn(
          "absolute inset-0 bg-black/50 flex items-end py-8 px-4 transition-opacity duration-300",
          hovered === index ? "opacity-100" : "opacity-0"
        )}
      >
        <div className="text-xl md:text-2xl font-medium bg-clip-text text-transparent bg-gradient-to-b from-neutral-50 to-neutral-200">
          {card.title}
        </div>
      </div>
    </div>
  )
);

Card.displayName = "Card";

type Card = {
  title: string;
  src: string;
};

export function FocusCards({ cards }: { cards: Card[] }) {
  const [hovered, setHovered] = useState<number | null>(null);

  return (
    <div className="grid grid-cols-1 md:grid-cols-3 gap-10 max-w-5xl mx-auto md:px-8 w-full">
      {cards.map((card, index) => (
        <Card
          key={card.title}
          card={card}
          index={index}
          hovered={hovered}
          setHovered={setHovered}
        />
      ))}
    </div>
  );
}

Source: Aceternity UI