# Pricing Dock: Studio Packages

> A design studio's three ways to work together, set in an editorial serif with tighter corners (`radius={6}`), a wider `gap` and warm `--dock-border` lines. `formatPrice` writes prices in thousands, the Sprint sets its own `/sprint` period, and Quarterly billing rolls the retainers down.

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

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

## Install

```bash
npx shadcn@latest add https://keyframeui.com/r/pricing-dock-studio-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/pricing-dock.json`

## Notes from the registry

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

## Files

### `registry/demos/pricing-dock-studio-demo.tsx`

```tsx
"use client";

import * as React from "react";

import { PricingDock, type PricingDockGroup, type PricingDockPlan } from "@/components/ui/pricing-dock";

const plans: PricingDockPlan[] = [
  {
    id: "sprint",
    name: "Sprint",
    price: 8,
    period: "/sprint",
    caption: "Two weeks, fixed scope",
    pitch: "A launch page, deck or rebrand in two weeks.",
    highlights: ["One request at a time", "Delivered in two weeks", "Two rounds of revisions", "Source files included"],
    cta: { label: "Book a call", href: "#" },
  },
  {
    id: "retainer",
    name: "Retainer",
    price: { monthly: 12, quarterly: 11 },
    caption: { monthly: "Pause or cancel any month", quarterly: "Billed every three months" },
    pitch: "Senior designers shipping weekly, no hiring.",
    highlights: ["Two active requests", "48-hour turnaround", "A dedicated lead designer", "Shared Slack channel"],
    featured: true,
    badge: "Most chosen",
    cta: { label: "Book a call", href: "#" },
  },
  {
    id: "partner",
    name: "Partner",
    price: { monthly: 20, quarterly: 18 },
    caption: { monthly: "Three-month minimum", quarterly: "Billed every three months" },
    pitch: "Brand, product and web, run like your in-house team.",
    highlights: ["Three active requests", "Same-day turnaround on small asks", "Design lead and design engineer", "Quarterly brand strategy"],
    cta: { label: "Book a call", href: "#" },
  },
];

const groups: PricingDockGroup[] = [
  {
    label: "Capacity",
    rows: [
      {
        feature: "Active requests",
        hint: "How many briefs we work on at once. Queue as many as you like; the next starts when one ships.",
        values: { sprint: "1", retainer: "2", partner: "3" },
      },
      { feature: "Turnaround", values: { sprint: "2 weeks", retainer: "48 hours", partner: "Same day" } },
      { feature: "Revisions", values: { sprint: "2 rounds", retainer: "Unlimited", partner: "Unlimited" } },
      { feature: "Pause any time", values: { sprint: false, retainer: true, partner: true } },
    ],
  },
  {
    label: "Team",
    rows: [
      { feature: "Dedicated designer", values: { sprint: false, retainer: true, partner: true } },
      { feature: "Design engineer", values: { sprint: false, retainer: false, partner: true } },
      { feature: "Creative direction", values: { sprint: false, retainer: "Monthly", partner: "Weekly" } },
      { feature: "Slack channel", values: { sprint: false, retainer: true, partner: true } },
    ],
  },
  {
    label: "What we make",
    rows: [
      { feature: "Brand identity", values: { sprint: "Refresh", retainer: true, partner: true } },
      { feature: "Websites and landing pages", values: { sprint: true, retainer: true, partner: true } },
      {
        feature: "Framer or Webflow build",
        hint: "We hand over a live site your team can edit, not just the design files.",
        values: { sprint: false, retainer: true, partner: true },
      },
      { feature: "Motion and video", values: { sprint: false, retainer: "Add-on", partner: true } },
      { feature: "Product UI", values: { sprint: false, retainer: true, partner: true } },
    ],
  },
  {
    label: "Terms",
    rows: [
      { feature: "Minimum term", values: { sprint: "One sprint", retainer: "One month", partner: "Three months" } },
      { feature: "You own everything", values: { sprint: true, retainer: true, partner: true } },
    ],
  },
];

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

  return (
    <div className="relative isolate w-full overflow-hidden rounded-xl border border-stone-300/70 bg-[#f4efe7] font-serif text-stone-900 dark:border-stone-800 dark:bg-stone-950 dark:text-stone-100">
      <div ref={scrollRef} className="h-[var(--demo-height,44rem)] overflow-y-auto overscroll-contain">
        <div className="mx-auto max-w-5xl px-4 sm:px-6">
          <header className="flex flex-col items-center pt-16 pb-12 text-center sm:pt-24">
            <p className="font-sans text-[11px] tracking-[0.2em] text-[#a8492a] uppercase dark:text-[#e2875f]">
              Atelier North · Work with us
            </p>
            <h2 className="mt-4 max-w-lg text-4xl/[1.1] tracking-tight text-balance sm:text-5xl/[1.05]">
              A senior design team, <em className="text-[#a8492a] dark:text-[#e2875f]">on call</em>
            </h2>
            <p className="mt-4 max-w-md font-sans text-sm/6 text-pretty text-stone-600 dark:text-stone-400">
              Three ways to work together. Every plan gets the same people; you choose how much of their week.
            </p>
          </header>

          <PricingDock
            containerRef={scrollRef}
            plans={plans}
            groups={groups}
            billing={[
              { id: "monthly", label: "Monthly" },
              { id: "quarterly", label: "Quarterly", note: "−8%" },
            ]}
            formatPrice={(amount) => `$${amount}k`}
            intro="Not sure which fits? Most studios start with a Sprint and move to a Retainer once the first project ships."
            compareLabel="See everything included"
            radius={6}
            gap={16}
            className="[--dock-border:#e2d8c8] dark:[--dock-border:var(--color-stone-800)] [--dock-accent:#a8492a] [--dock-accent-foreground:#fbf8f3] [--dock-page:#f4efe7] [--dock-surface:#fbf8f3] dark:[--dock-accent:#e2875f] dark:[--dock-accent-foreground:#1c1917] dark:[--dock-page:var(--color-stone-950)] dark:[--dock-surface:var(--color-stone-900)] [&_[role=cell]]:font-sans [&_[role=cell]]:text-[13px] [&_[role=rowheader]]:text-[15px]"
          />

          <footer className="flex flex-col items-center py-28 text-center">
            <h3 className="text-3xl tracking-tight">
              Let&apos;s talk about <em>your</em> next launch
            </h3>
            <p className="mt-3 max-w-sm font-sans text-sm/6 text-pretty text-stone-600 dark:text-stone-400">
              Twenty minutes with a founder, no slides. We&apos;ll tell you honestly if we&apos;re the right fit.
            </p>
            <a
              href="#"
              className="mt-6 rounded-full bg-[#a8492a] px-5 py-2.5 font-sans text-sm font-medium text-[#fbf8f3] transition-colors hover:bg-[#93401f] dark:bg-[#e2875f] dark:text-stone-900"
            >
              Book a call
            </a>
          </footer>
        </div>
      </div>
    </div>
  );
}
```
