Us against the two tools teams switch from, with logo cards instead of prices: each mark shrinks in beside its name as the cards dock. On a phone, the plan pills pick whose column the table shows, or swipe the table sideways.
@keyframeui
registry/demos/pricing-dock-compare-demo.tsx
"use client";
import * as React from "react";
import { PricingDock, type PricingDockGroup, type PricingDockPlan } from "@/components/ui/pricing-dock";
function Mark({ className, children }: { className: string; children: React.ReactNode }) {
return (
<span className={`flex size-10 items-center justify-center rounded-[11px] ${className}`} aria-hidden>
{children}
</span>
);
}
const plans: PricingDockPlan[] = [
{
id: "us",
name: "Northlight",
logo: (
<Mark className="bg-blue-600 text-white dark:bg-sky-400 dark:text-sky-950">
<svg viewBox="0 0 20 20" fill="currentColor" className="size-5">
<path d="M10 2.5 12.2 8l5.3 2-5.3 2L10 17.5 7.8 12l-5.3-2 5.3-2L10 2.5Z" />
</svg>
</Mark>
),
pitch: "Reviews that read the whole repo, not just the diff.",
highlights: ["First review in under a minute", "Learns your team's conventions", "Fixes as one-click suggestions", "Cloud or self-hosted"],
featured: true,
badge: "Free migration",
cta: { label: "Switch in 5 minutes", href: "#" },
},
{
id: "a",
name: "Competitor A",
logo: (
<Mark className="bg-zinc-200 text-zinc-500 dark:bg-zinc-800 dark:text-zinc-400">
<span className="font-mono text-sm font-semibold">A</span>
</Mark>
),
pitch: "Rule-based linting with an AI summary on top.",
highlights: ["First review in 5–10 minutes", "Fixed rule sets", "Comments only, no fixes", "Cloud only"],
cta: { label: "Moving from A", href: "#" },
},
{
id: "b",
name: "Competitor B",
logo: (
<Mark className="bg-zinc-200 text-zinc-500 dark:bg-zinc-800 dark:text-zinc-400">
<span className="font-mono text-sm font-semibold">B</span>
</Mark>
),
pitch: "A general chat assistant wired into pull requests.",
highlights: ["First review in 2–4 minutes", "Prompted per repository", "Suggestions in a chat thread", "Cloud only"],
cta: { label: "Moving from B", href: "#" },
},
];
const groups: PricingDockGroup[] = [
{
label: "Speed",
rows: [
{ feature: "Time to first review", values: { us: "Under 1 min", a: "5–10 min", b: "2–4 min" } },
{ feature: "Large pull requests", values: { us: "1,000+ files", a: "Up to 200", b: "Up to 300" } },
{ feature: "Re-reviews only what changed", values: { us: true, a: false, b: true } },
],
},
{
label: "Review quality",
rows: [
{
feature: "Reads the whole repository",
hint: "Follows imports, call sites and tests outside the diff before it comments.",
values: { us: true, a: false, b: "Open files" },
},
{ feature: "Learns team conventions", values: { us: true, a: false, b: "By prompt" } },
{ feature: "One-click fixes", values: { us: true, a: false, b: true } },
{
feature: "False-positive rate",
hint: "Comments dismissed without a change, across 40 open-source repos we reviewed in September 2026.",
values: { us: "3%", a: "18%", b: "9%" },
},
{ feature: "Writes missing tests", values: { us: true, a: false, b: false } },
],
},
{
label: "Workflow",
rows: [
{ feature: "GitHub, GitLab, Bitbucket", values: { us: true, a: "GitHub only", b: "GitHub, GitLab" } },
{ feature: "Slack and Linear", values: { us: true, a: "Slack", b: false } },
{ feature: "Custom review rules", values: { us: true, a: true, b: false } },
{ feature: "CLI and pre-commit hook", values: { us: true, a: false, b: false } },
],
},
{
label: "Security",
rows: [
{ feature: "Self-hosting", values: { us: true, a: false, b: false } },
{ feature: "Zero data retention", values: { us: true, a: false, b: "Enterprise" } },
{ feature: "SOC 2 Type II", values: { us: true, a: true, b: true } },
{ feature: "SSO and SCIM", values: { us: true, a: "Enterprise", b: true } },
{ feature: "Never trains on your code", values: { us: true, a: "Opt-out", b: true } },
],
},
{
label: "Price",
rows: [
{ feature: "Per seat, per month", values: { us: "$19", a: "$29", b: "$24" } },
{ feature: "Free for open source", values: { us: true, a: false, b: true } },
{ feature: "Migration help", values: { us: "Free, hands-on", a: false, b: false } },
],
},
];
export function PricingDockCompareDemo() {
const scrollRef = React.useRef<HTMLDivElement>(null);
return (
<div className="relative isolate w-full overflow-hidden rounded-xl border border-zinc-200 bg-zinc-50 dark:border-zinc-800 dark:bg-[#08090c]">
<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="pt-14 pb-10 sm:pt-20">
<p className="font-mono text-[11px] tracking-widest text-blue-600 uppercase dark:text-sky-400">Compare</p>
<h2 className="mt-3 max-w-lg text-3xl/tight font-semibold tracking-tight text-balance text-zinc-900 sm:text-4xl/tight dark:text-zinc-50">
Why teams switch to Northlight
</h2>
<p className="mt-3 max-w-md text-sm/6 text-pretty text-zinc-500">
The two tools teams most often move from, side by side. Hover a column to follow it, or click a name once
it docks to see its card again.
</p>
</header>
<PricingDock
containerRef={scrollRef}
plans={plans}
groups={groups}
intro="Checked against each tool's public docs and pricing in October 2026."
compareLabel="Jump to the comparison"
className="[--dock-accent:var(--color-blue-600)] [--dock-accent-foreground:white] [--dock-page:var(--color-zinc-50)] [--dock-surface:white] dark:[--dock-accent:var(--color-sky-400)] dark:[--dock-accent-foreground:var(--color-zinc-950)] dark:[--dock-page:#08090c] dark:[--dock-surface:var(--color-zinc-900)]"
/>
<footer className="flex flex-col items-center py-24 text-center">
<h3 className="text-xl font-semibold tracking-tight text-zinc-900 dark:text-zinc-50">
Bring your rules with you
</h3>
<p className="mt-2 max-w-sm text-sm/6 text-pretty text-zinc-500">
We import your existing review rules and ignore lists, so day one feels like day one hundred.
</p>
<a
href="#"
className="mt-5 rounded-full bg-blue-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-500 dark:bg-sky-400 dark:text-sky-950"
>
Start the migration
</a>
</footer>
</div>
</div>
</div>
);
}
Live preview, examples and props: https://keyframeui.com/components/pricing-dock#pricing-dock-compare-demo




























