Reveal Card

A compact, interactive card that expands on hover to reveal additional content, efficiently presenting layered information in a limited space.

John Smith

The Computer Guy

When your computer is slower than your grandma's dial-up, and your code has more bugs than a picnic, who you gonna call? Not Ghostbusters, but close!

  • Master of turning it off and on again
  • Fluent in Binary, HTML, and Sarcasm
  • Can debug your life (results may vary)
  • Makes Excel sheets more exciting than Netflix

Installation

Install Dependencies

npm i clsx tailwind-merge framer-motion

Create @/utils/cn.ts file

import clsx, { ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

Reveal Card Component

"use client";

import { cn } from "@/lib/utils";
import { cva, type VariantProps } from "class-variance-authority";
import React, { createContext, useContext } from "react";

type CardSize = "default" | "small" | "large";

const revealCardVariants = cva("relative overflow-hidden rounded-md shadow-md group", {
  variants: {
    size: {
      default: "w-[350px] h-[350px]",
      small: "w-[250px] h-[250px]",
      large: "w-full sm:w-[450px] h-[450px]"
    }
  },
  defaultVariants: {
    size: "default"
  }
});

interface RevealCardProps extends React.HTMLAttributes<HTMLDivElement>, Omit<VariantProps<typeof revealCardVariants>, "size"> {
  size?: CardSize | null;
}

const RevealCardContext = createContext<{ size: CardSize }>({ size: "default" });

export const RevealCard: React.FC<RevealCardProps> = ({ children, className, size, ...props }) => {
  const safeSize: CardSize = size ?? "default";
  return (
    <RevealCardContext.Provider value={{ size: safeSize }}>
      <div className={revealCardVariants({ size: safeSize, className })} {...props}>
        {children}
      </div>
    </RevealCardContext.Provider>
  );
};

const revealCardContentVariants = cva("absolute inset-0 rounded-md bg-gray-100", {
  variants: {
    size: {
      default: "",
      small: "",
      large: ""
    }
  },
  defaultVariants: {
    size: "default"
  }
});

interface RevealCardContentProps extends React.HTMLAttributes<HTMLDivElement> {}

export const RevealCardContent: React.FC<RevealCardContentProps> = ({ children, className, ...props }) => {
  const { size } = useContext(RevealCardContext);
  return (
    <div className={cn(revealCardContentVariants({ size }), className)} {...props}>
      {children}
    </div>
  );
};

const revealCardHeaderVariants = cva("absolute left-0 right-0 bottom-0 bg-white transition-transform duration-700 ease-in-out z-10", {
  variants: {
    size: {
      default: "h-[90px] group-hover:translate-y-[-260px]",
      small: "h-[70px] group-hover:translate-y-[-180px]",
      large: "h-[110px] group-hover:translate-y-[-340px]"
    }
  },
  defaultVariants: {
    size: "default"
  }
});

interface RevealCardHeaderProps extends React.HTMLAttributes<HTMLDivElement> {}

export const RevealCardHeader: React.FC<RevealCardHeaderProps> = ({ children, className, ...props }) => {
  const { size } = useContext(RevealCardContext);
  return (
    <div className={cn(revealCardHeaderVariants({ size }), className)} {...props}>
      <div className="p-4">{children}</div>
    </div>
  );
};

const revealCardBodyVariants = cva(
  "absolute inset-x-0 bottom-0 bg-white transition-transform duration-700 ease-in-out transform translate-y-full group-hover:translate-y-0 overflow-y-auto",
  {
    variants: {
      size: {
        default: "top-[90px]",
        small: "top-[70px]",
        large: "top-[110px]"
      }
    },
    defaultVariants: {
      size: "default"
    }
  }
);

interface RevealCardBodyProps extends React.HTMLAttributes<HTMLDivElement> {}

export const RevealCardBody: React.FC<RevealCardBodyProps> = ({ children, className, ...props }) => {
  const { size } = useContext(RevealCardContext);
  return (
    <div className={cn(revealCardBodyVariants({ size }), className)} {...props}>
      <div className="h-px w-full bg-gray-200"></div>
      <div className="p-6">{children}</div>
    </div>
  );
};

Examples

Large

Alpine Adventure

Discover the majestic beauty of the mountains

Snowcapped Serenity

A winter wonderland awaits

Embark on an unforgettable journey to our pristine mountain resort. Nestled in the heart of snow-capped peaks, you'll find world-class skiing, cozy lodges, and breathtaking views that will leave you in awe.

  • Ski slopes for all levels
  • Luxurious mountain-view accommodations
  • Gourmet dining with local flavors
  • Relaxing spa treatments

Small

React Development

Custom Solutions

Tailored for your needs

  • • Scalable architecture
  • • Performance optimization
  • • API integration