Web InnoventixFreeCode

Card Lift Hover Effect

Original · free

A card that lifts with a growing shadow on hover.

byWeb InnoventixReact + Tailwind
hovercardlift
Open

Copy prompt gives Claude Code, Cursor or v0 a ready-to-paste prompt that recreates this exact component.

npx shadcn@latest add https://webinnoventix.com/r/hover-card-lift.json
hover-card-lift.tsx
"use client";

import { useId } from "react";

type LiftCard = {
  eyebrow: string;
  title: string;
  body: string;
  meta: string;
  href: string;
};

const CARDS: LiftCard[] = [
  {
    eyebrow: "Playbook",
    title: "Ship a design system in a weekend",
    body: "Tokens, primitives, and a documented component API — the exact folder structure we use to go from zero to production.",
    meta: "8 min read",
    href: "#",
  },
  {
    eyebrow: "Deep dive",
    title: "Making hover feel physically real",
    body: "Elevation, shadow spread, and easing curves that mimic how objects actually rise off a surface under a light source.",
    meta: "12 min read",
    href: "#",
  },
  {
    eyebrow: "Field notes",
    title: "Accessible motion without the motion sickness",
    body: "Respecting prefers-reduced-motion while still giving sighted users a sense of depth, weight, and responsive feedback.",
    meta: "6 min read",
    href: "#",
  },
];

export default function HoverCardLift() {
  const styleId = useId().replace(/[^a-zA-Z0-9]/g, "");
  const prefix = `hcl${styleId}`;

  return (
    <section className="relative w-full bg-slate-50 px-6 py-20 sm:py-28 dark:bg-slate-950">
      <style>{`
        @keyframes ${prefix}Rise {
          from { opacity: 0; transform: translateY(18px); }
          to { opacity: 1; transform: translateY(0); }
        }
        .${prefix}-card {
          animation: ${prefix}Rise 0.6s cubic-bezier(0.22, 1, 0.36, 1) both;
          transition:
            transform 0.35s cubic-bezier(0.22, 1, 0.36, 1),
            box-shadow 0.35s cubic-bezier(0.22, 1, 0.36, 1),
            border-color 0.35s ease;
          will-change: transform, box-shadow;
        }
        .${prefix}-card:hover,
        .${prefix}-card:focus-within {
          transform: translateY(-10px);
          box-shadow:
            0 2px 4px -2px rgb(15 23 42 / 0.12),
            0 24px 40px -12px rgb(15 23 42 / 0.28),
            0 40px 64px -24px rgb(15 23 42 / 0.22);
        }
        :is(.dark) .${prefix}-card:hover,
        :is(.dark) .${prefix}-card:focus-within {
          box-shadow:
            0 2px 6px -2px rgb(0 0 0 / 0.6),
            0 28px 48px -12px rgb(0 0 0 / 0.7),
            0 48px 72px -24px rgb(79 70 229 / 0.25);
        }
        .${prefix}-arrow {
          transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
        }
        .${prefix}-card:hover .${prefix}-arrow,
        .${prefix}-card:focus-within .${prefix}-arrow {
          transform: translateX(4px);
        }
        .${prefix}-glow {
          transition: opacity 0.35s ease;
        }
        .${prefix}-card:hover .${prefix}-glow,
        .${prefix}-card:focus-within .${prefix}-glow {
          opacity: 1;
        }
        @media (prefers-reduced-motion: reduce) {
          .${prefix}-card,
          .${prefix}-arrow,
          .${prefix}-glow {
            animation: none !important;
            transition: box-shadow 0.2s ease, border-color 0.2s ease;
          }
          .${prefix}-card:hover,
          .${prefix}-card:focus-within {
            transform: none;
          }
          .${prefix}-card:hover .${prefix}-arrow,
          .${prefix}-card:focus-within .${prefix}-arrow {
            transform: none;
          }
        }
      `}</style>

      <div className="mx-auto max-w-6xl">
        <div className="mx-auto mb-14 max-w-2xl text-center">
          <span className="inline-flex items-center rounded-full border border-indigo-200 bg-indigo-50 px-3 py-1 text-xs font-semibold uppercase tracking-wider text-indigo-700 dark:border-indigo-500/30 dark:bg-indigo-500/10 dark:text-indigo-300">
            From the journal
          </span>
          <h2 className="mt-5 text-3xl font-bold tracking-tight text-slate-900 sm:text-4xl dark:text-slate-50">
            Hover to feel the lift
          </h2>
          <p className="mt-4 text-base leading-relaxed text-slate-600 dark:text-slate-400">
            Each card rises off the page with a soft, growing shadow. Point, tap,
            or tab to a card — the depth follows your focus.
          </p>
        </div>

        <ul className="grid list-none grid-cols-1 gap-6 p-0 sm:grid-cols-2 lg:grid-cols-3">
          {CARDS.map((card, i) => (
            <li key={card.title}>
              <article
                className={`${prefix}-card group relative flex h-full flex-col overflow-hidden rounded-2xl border border-slate-200 bg-white p-6 dark:border-slate-800 dark:bg-slate-900`}
                style={{ animationDelay: `${i * 90}ms` }}
              >
                <div
                  aria-hidden="true"
                  className={`${prefix}-glow pointer-events-none absolute -right-16 -top-16 h-40 w-40 rounded-full bg-gradient-to-br from-indigo-400/30 to-violet-400/10 opacity-0 blur-2xl dark:from-indigo-500/30 dark:to-violet-500/10`}
                />

                <span className="relative inline-flex w-fit items-center rounded-md bg-slate-100 px-2.5 py-1 text-xs font-medium uppercase tracking-wide text-slate-600 dark:bg-slate-800 dark:text-slate-300">
                  {card.eyebrow}
                </span>

                <h3 className="relative mt-4 text-xl font-semibold leading-snug text-slate-900 dark:text-slate-50">
                  <a
                    href={card.href}
                    className="rounded-sm outline-none after:absolute after:inset-0 after:content-[''] focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-slate-900"
                  >
                    {card.title}
                  </a>
                </h3>

                <p className="relative mt-3 flex-1 text-sm leading-relaxed text-slate-600 dark:text-slate-400">
                  {card.body}
                </p>

                <div className="relative mt-6 flex items-center justify-between border-t border-slate-100 pt-4 dark:border-slate-800">
                  <span className="text-xs font-medium text-slate-500 dark:text-slate-500">
                    {card.meta}
                  </span>
                  <span className="inline-flex items-center gap-1.5 text-sm font-semibold text-indigo-600 dark:text-indigo-400">
                    Read
                    <svg
                      className={`${prefix}-arrow h-4 w-4`}
                      viewBox="0 0 20 20"
                      fill="none"
                      stroke="currentColor"
                      strokeWidth="2"
                      strokeLinecap="round"
                      strokeLinejoin="round"
                      aria-hidden="true"
                    >
                      <path d="M4 10h11" />
                      <path d="M11 5l5 5-5 5" />
                    </svg>
                  </span>
                </div>
              </article>
            </li>
          ))}
        </ul>
      </div>
    </section>
  );
}

Dependencies

None (React + Tailwind only).

Licence

Built by Web Innoventix. Free for personal and commercial use, no attribution required.

Built by Web Innoventix

Need a custom interface, a full website, or SEO that gets you cited by AI? We design, build and rank it end to end.

Get a free quote

Similar components

Browse all →