Skip to main content

Cascade Card Deck

A scroll-stacked feature list where each card is sticky at a stepped top offset, so scrolling pins them into an overlapping deck with the newest on top. Big stat numbers anchor every card, and the surface and badge colours are per-card props so the deck can stay monochrome or lean into an accent.

CardReactTailwind CSSSolar Icons
CSSTailwind

Manual

Create a file and paste the following code into it.

cascade-card-deck.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
"use client";

import { type IconProps } from "@solar-icons/react";
import { cn } from "@/lib/cn";

/** Solar exports only IconProps at the root; alias the component type. */
type Icon = import("react").ComponentType<IconProps>;

/* ------------------------------------------------------------------ */
/*  CascadeCardDeck                                                */
/* ------------------------------------------------------------------ */

export interface FeatureCard {
  value: string;
  title: string;
  description: string;
  icon: Icon;
  /** Card surface classes [Optional, default: neutral] */
  cardClassName?: string;
  /** Icon badge classes [Optional, default: neutral] */
  iconClassName?: string;
}

interface CascadeCardDeckProps {
  cards: FeatureCard[];
  className?: string;
}

/**
 * A scroll-stacked feature list: each card is sticky at a stepped top offset,
 * so scrolling pins them into an overlapping deck with the newest on top. Big
 * stat numbers anchor each card, and the surface/badge colours are per-card
 * props so the deck can stay monochrome or lean into an accent.
 * @param {FeatureCard[]} cards - Cards, rendered top to bottom [Required]
 */
export function CascadeCardDeck({ cards, className }: CascadeCardDeckProps) {
  return (
    <ul className={cn("relative space-y-0 pb-14", className)}>
      {cards.map((card, index) => {
        const CardIcon = card.icon;
        return (
          <li
            key={card.value}
            className="sticky"
            style={{ zIndex: index + 1, top: `${6 + 2.4 * index}rem` }}
          >
            <article
              className={cn(
                "flex h-100 flex-col justify-between rounded-[2.4rem] p-7 shadow-xl sm:p-9",
                card.cardClassName ?? "bg-neutral-100 text-neutral-900",
              )}
            >
              <div className="flex items-start justify-between gap-4">
                <p className="text-7xl font-bold leading-none tracking-tighter sm:text-8xl">
                  {card.value}
                  <span className="align-top text-4xl">+</span>
                </p>
                <span
                  className={cn(
                    "inline-flex size-12 items-center justify-center rounded-full",
                    card.iconClassName ?? "bg-neutral-200 text-neutral-700",
                  )}
                >
                  <CardIcon className="size-6" weight="Bold" />
                </span>
              </div>
              <div className="max-w-xl">
                <p className="text-xl font-bold leading-tight tracking-tight sm:text-3xl">
                  {card.title}
                </p>
                <p className="mt-4 text-base font-medium leading-relaxed opacity-95 sm:text-lg">
                  {card.description}
                </p>
              </div>
            </article>
          </li>
        );
      })}
    </ul>
  );
}

Update the import paths to match your project setup.

Similar components

MaxNew

Graph Plot

Ambient Glow Video

Pin Dock List

Borealis Card

Install via CLI

Resource details

PublishedJuly 17, 2026
CategoryCard
ReactTailwind CSSSolar Icons