# Sticky Deck: Pricing

> Plans dealt in order, each one reading "Everything in Pro, plus" as it lands on the plan it builds on. Taller edges keep every tier's color showing at the top of the deck.

A shadcn/ui block from the [@keyframeui](https://keyframeui.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/keyframeui/sticky-deck-pricing-demo).

- Page, with a live preview: https://shadcn.exchange/r/keyframeui/sticky-deck-pricing-demo
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/keyframeui/sticky-deck-pricing-demo-light-fddbf207bf6e682e.png
- Registry item: https://keyframeui.com/r/sticky-deck-pricing-demo.json
- Categories: scroll, cards, sticky
- Listed: 2026-10-15, source last changed 2026-10-15

## Install

```bash
npx shadcn@latest add https://keyframeui.com/r/sticky-deck-pricing-demo.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- Registry items: `https://keyframeui.com/r/sticky-deck.json`

## Notes from the registry

Live preview, examples and props: https://keyframeui.com/components/sticky-deck#sticky-deck-pricing-demo

## Files

### `registry/demos/sticky-deck-pricing-demo.tsx`

```tsx
"use client";

import * as React from "react";

import { cn } from "@/lib/utils";
import { StickyDeck, StickyDeckCard } from "@/components/ui/sticky-deck";

// Each plan sits on top of the one before it, the way it builds on it.
const plans = [
  {
    name: "Starter",
    price: "$0",
    unit: "per month",
    blurb: "For trying it out on a side project.",
    includes: "What you get",
    features: ["1 repository", "50 agent runs a month", "Pull request summaries", "Community support"],
    cta: "Start building",
    tone: "bg-white text-zinc-900 ring-zinc-900/10 dark:bg-zinc-900 dark:text-zinc-50 dark:ring-white/10",
    muted: "text-zinc-500",
    check: "text-zinc-900 dark:text-zinc-50",
    button: "bg-zinc-900 text-white hover:bg-zinc-700 dark:bg-white dark:text-zinc-900 dark:hover:bg-zinc-200",
  },
  {
    name: "Pro",
    price: "$24",
    unit: "per seat / month",
    blurb: "For developers who ship every day.",
    includes: "Everything in Starter, plus",
    features: ["Unlimited repositories", "1,000 agent runs a month", "Draft PRs with previews", "Priority queue"],
    cta: "Upgrade to Pro",
    badge: "Most popular",
    tone: "bg-indigo-600 text-white ring-white/10",
    muted: "text-indigo-100/75",
    check: "text-white",
    button: "bg-white text-indigo-700 hover:bg-indigo-50",
  },
  {
    name: "Team",
    price: "$48",
    unit: "per seat / month",
    blurb: "For teams that review code together.",
    includes: "Everything in Pro, plus",
    features: ["Shared rules and conventions", "Review routing by code owner", "Usage analytics", "SSO with Google"],
    cta: "Start a team trial",
    tone: "bg-zinc-900 text-white ring-white/10 dark:bg-zinc-800",
    muted: "text-zinc-400",
    check: "text-emerald-400",
    button: "bg-emerald-400 text-zinc-950 hover:bg-emerald-300",
  },
  {
    name: "Enterprise",
    price: "Custom",
    unit: "billed yearly",
    blurb: "For organizations with security reviews.",
    includes: "Everything in Team, plus",
    features: ["SAML SSO and SCIM", "Self-hosted runners", "Audit log export", "99.9% uptime SLA"],
    cta: "Talk to sales",
    tone: "bg-linear-to-br from-amber-100 to-orange-200 text-zinc-900 ring-zinc-900/10",
    muted: "text-zinc-700/80",
    check: "text-orange-700",
    button: "bg-zinc-900 text-white hover:bg-zinc-700",
  },
];

export function StickyDeckPricingDemo() {
  const scrollRef = React.useRef<HTMLDivElement>(null);

  return (
    <div className="relative w-full overflow-hidden rounded-xl border border-zinc-200 bg-zinc-100 dark:border-zinc-800 dark:bg-zinc-950">
      <div
        ref={scrollRef}
        className="@container relative h-[var(--demo-height,36rem)] overflow-y-auto overscroll-contain"
      >
        <div className="mx-auto max-w-2xl px-4 sm:px-6">
          <header className="pt-14 pb-12 text-center sm:pt-20">
            <p className="font-mono text-[11px] tracking-widest text-zinc-500 uppercase">Pricing</p>
            <h2 className="mt-3 text-3xl/tight font-semibold tracking-tight text-balance text-zinc-900 dark:text-zinc-50">
              Start small. Every plan builds on the last.
            </h2>
          </header>

          <StickyDeck containerRef={scrollRef} top={88} offsetStep={18} scaleTo={0.95} hold={96} className="gap-6">
            {plans.map((plan) => (
              <StickyDeckCard
                key={plan.name}
                className={cn(
                  "grid h-[min(21rem,calc(var(--demo-height,36rem)-7.5rem))] grid-rows-[auto_1fr] gap-x-8 gap-y-5 rounded-[1.75rem] p-6 shadow-[0_-14px_36px_-14px_rgb(0_0_0/0.3)] ring-1 @lg:grid-cols-[1fr_1.1fr] @lg:grid-rows-1 @lg:p-8",
                  plan.tone,
                )}
              >
                <div className="flex flex-col">
                  <p className="flex items-center gap-2 text-sm font-medium">
                    {plan.name}
                    {plan.badge ? (
                      <span className="rounded-full bg-white/15 px-2 py-0.5 text-[11px] font-medium ring-1 ring-white/25">
                        {plan.badge}
                      </span>
                    ) : null}
                  </p>
                  <p className="mt-3 flex items-baseline gap-2">
                    <span className="text-4xl font-semibold tracking-tight tabular-nums">{plan.price}</span>
                    <span className={cn("text-sm", plan.muted)}>{plan.unit}</span>
                  </p>
                  <p className={cn("mt-2 text-sm/6 text-pretty", plan.muted)}>{plan.blurb}</p>
                  <a
                    href="#"
                    className={cn(
                      "mt-auto hidden w-fit rounded-full px-4 py-2 text-sm font-medium transition-colors @lg:inline-flex",
                      plan.button,
                    )}
                  >
                    {plan.cta}
                  </a>
                </div>

                <div className="flex min-h-0 flex-col">
                  <p className={cn("text-xs font-medium", plan.muted)}>{plan.includes}</p>
                  <ul className="mt-3 space-y-2.5 text-sm">
                    {plan.features.map((feature) => (
                      <li key={feature} className="flex items-start gap-2.5">
                        <svg
                          viewBox="0 0 16 16"
                          fill="none"
                          stroke="currentColor"
                          strokeWidth="2"
                          aria-hidden
                          className={cn("mt-0.5 size-4 shrink-0", plan.check)}
                        >
                          <path d="m3.5 8.5 3 3 6-7" strokeLinecap="round" strokeLinejoin="round" />
                        </svg>
                        {feature}
                      </li>
                    ))}
                  </ul>
                  <a
                    href="#"
                    className={cn(
                      "mt-auto inline-flex w-fit rounded-full px-4 py-2 text-sm font-medium transition-colors @lg:hidden",
                      plan.button,
                    )}
                  >
                    {plan.cta}
                  </a>
                </div>
              </StickyDeckCard>
            ))}
          </StickyDeck>

          <footer className="flex h-[calc(var(--demo-height,36rem)-6rem)] flex-col items-center justify-center text-center">
            <p className="max-w-xs text-sm/6 text-pretty text-zinc-500">
              Every plan starts with a 14-day trial of Pro. Change plans any time.
            </p>
            <a href="#" className="mt-3 text-sm font-medium text-zinc-900 underline-offset-4 hover:underline dark:text-zinc-50">
              Compare every feature →
            </a>
          </footer>
        </div>
      </div>
    </div>
  );
}
```
