logo

Nurav UI

Community
Components

Card

A flexible container for content fragments, with support for headers, footers, and variants.Copy Markdown

Preview

Choose from three distinct visual styles to perfectly match your UI's aesthetic.

Loading...

Variant V1 (Modern Gloss)

A sophisticated design with ultra-rounded corners, subtle gradients, and a lifting hover interaction.

Loading...

Variant V2 (Glassmorphism)

Premium design featuring deep backdrop-blur, edge-light effects, and a responsive glow that appears on hover.

Loading...

Variant V3 (Brutalist Modern)

Bold, high-contrast design with offset shadows and sharp edges. Features a snappy "push" interaction on hover.

Loading...

Installation

Automatic (CLI)

The fastest way to get started is using the shadcn CLI.

Before using the @nurav-ui alias, add this to your components.json:

components.json
{
  "registries": {
    "@nurav-ui": "https://nurav-ui.vercel.app/r"
  }
}

Or skip alias setup entirely and use the direct URL below.

Terminal
npx shadcn@latest add @nurav-ui/card

Alternatively, install via direct URL (no components.json changes needed):

Terminal
npx shadcn@latest add https://nurav-ui.vercel.app/r/card.json

Manual Setup

If you prefer manual configuration, follow these steps:

Install Dependencies

Install the necessary animation and utility libraries:

Terminal
npm install motion clsx tailwind-merge

Create/Customize Component

Copy the following code into your project (e.g., components/nurav-ui/Card.tsx). You can modify this code to create your own custom card designs:

Card.tsx
'use client';

import React from 'react';
import { motion, Variants } from 'motion/react';
import { cn } from "@/lib/utils";

interface CardProps {
  title?: React.ReactNode;
  description?: React.ReactNode;
  content?: React.ReactNode;
  footer?: React.ReactNode;
  variant?: 'v1' | 'v2' | 'v3';
  className?: string;
  headerClassName?: string;
  contentClassName?: string;
  footerClassName?: string;
  onClick?: () => void;
}

export const Card = ({
  title,
  description,
  content,
  footer,
  variant = 'v1',
  className,
  headerClassName,
  contentClassName,
  footerClassName,
  onClick,
}: CardProps) => {
  const containerVariants: Variants = {
    initial: { opacity: 0, y: 10 },
    animate: { opacity: 1, y: 0 },
    hover: {
      y: -8,
      transition: { duration: 0.4, ease: [0.16, 1, 0.3, 1] },
    },
    tap: {
      scale: 0.98,
    },
  };

  const getVariantStyles = () => {
    switch (variant) {
      case 'v1':
        return cn(
          "rounded-[2.5rem] border border-foreground/[0.08] bg-gradient-to-br from-background via-background/95 to-background/90",
          "shadow-[0_8px_30px_rgb(0,0,0,0.04)] dark:shadow-[0_8px_30px_rgb(0,0,0,0.1)]",
          "hover:border-foreground/20 hover:shadow-[0_20px_50px_rgba(0,0,0,0.1)] transition-colors transition-shadow duration-500"
        );
      case 'v2':
        return cn(
          "rounded-3xl border border-white/10 bg-white/[0.03] backdrop-blur-xl",
          "before:absolute before:inset-0 before:-z-10 before:rounded-3xl before:bg-gradient-to-br before:from-white/10 before:via-transparent before:to-transparent before:opacity-0 hover:before:opacity-100 before:transition-opacity",
          "shadow-2xl hover:shadow-white/[0.05] transition-shadow duration-500 relative overflow-hidden"
        );
      case 'v3':
        return cn(
          "rounded-none border-[3px] border-foreground bg-background shadow-[8px_8px_0px_0px_rgba(0,0,0,1)] dark:shadow-[8px_8px_0px_0px_rgba(255,255,255,1)]",
          "hover:shadow-[12px_12px_0px_0px_rgba(0,0,0,1)] dark:hover:shadow-[12px_12px_0px_0px_rgba(255,255,255,1)]",
          "transition-shadow duration-200"
        );
      default:
        return "";
    }
  };

  return (
    <motion.div
      initial="initial"
      animate="animate"
      whileHover="hover"
      whileTap="tap"
      variants={containerVariants}
      onClick={onClick}
      className={cn(
        "group relative flex flex-col w-full max-w-sm transition-colors duration-500",
        getVariantStyles(),
        onClick && "cursor-pointer",
        className
      )}
    >
      {/* Decorative Glow for V1 & V2 */}
      {(variant === 'v1' || variant === 'v2') && (
        <div className="absolute -inset-px rounded-[inherit] opacity-0 group-hover:opacity-100 group-active:opacity-100 transition-opacity duration-500 pointer-events-none bg-gradient-to-br from-primary/20 via-transparent to-primary/5 -z-10" />
      )}

      {/* Header */}
      {(title || description) && (
        <div className={cn("px-8 py-7 flex flex-col gap-2", headerClassName)}>
          {title && (
            <h3 className={cn(
              "text-2xl font-bold tracking-[ -0.02em] text-foreground leading-tight transition-colors duration-300",
              variant === 'v3' && "uppercase text-3xl italic font-black",
              "group-hover:text-primary group-active:text-primary transition-colors"
            )}>
              {title}
            </h3>
          )}
          {description && (
            <p className={cn(
              "text-sm text-muted-foreground/80 leading-relaxed font-medium transition-all duration-300",
              variant === 'v3' && "text-foreground font-bold"
            )}>
              {description}
            </p>
          )}
        </div>
      )}

      {/* Content */}
      <div className={cn(
        "px-8 py-5 flex-1 text-base text-foreground/80 leading-relaxed",
        (title || description) && "pt-0",
        contentClassName
      )}>
        {content || (
          <div className="h-32 w-full bg-foreground/[0.02] dark:bg-white/[0.02] rounded-2xl flex items-center justify-center border border-dashed border-foreground/10 group-hover:border-primary/30 group-active:border-primary/30 transition-colors duration-500 overflow-hidden relative">
            <span className="text-muted-foreground/30 text-sm font-medium italic relative z-10">Empty Card Content</span>
            <div className="absolute inset-0 bg-gradient-to-br from-primary/5 via-transparent to-transparent opacity-0 group-hover:opacity-100 group-active:opacity-100 transition-opacity duration-700" />
          </div>
        )}
      </div>

      {/* Footer */}
      {footer && (
        <div className={cn(
          "px-8 py-6 mt-auto border-t border-foreground/[0.05] bg-foreground/[0.01]",
          variant === 'v3' && "border-t-[3px] border-foreground bg-transparent py-4",
          footerClassName
        )}>
          {footer}
        </div>
      )}
    </motion.div>
  );
};

Component Properties

Card Props

PropTypeDefaultDescription
title
ReactNodeundefinedThe header title for the card.
description
ReactNodeundefinedSubtext shown below the title.
content
ReactNodeundefinedThe main body content of the card.
footer
ReactNodeundefinedContent shown at the bottom of the card.
variant
'v1' | 'v2' | 'v3''v1'Visual style variant for the card.
className
stringundefinedAdditional CSS classes for the container.
headerClassName
stringundefinedCustom classes for the header section.
contentClassName
stringundefinedCustom classes for the content section.
footerClassName
stringundefinedCustom classes for the footer section.
onClick
() => voidundefinedOptional click handler (enables hover lift).

Built by Varun

Last Updated:February 22, 2026

On this page