ComponentsPlan Comparison

Plan Comparison

section

A tier × feature matrix with one highlighted column — the upgrade-decision surface.

Compare plans
A tier × feature matrix with the recommended plan highlighted.
Best practices
Do
  • Reach for a comparison when the choice is “which tier?” — plans across the top, features down the side, one value per cell.
  • Highlight exactly one column (the current or recommended plan) — the band draws behind that tier so the eye lands on it.
  • Keep cells to a check, a dash, or a short value (“Unlimited”, “5”); a comparison is scanned, not read.
Don't
  • Don't use .pricing (standalone tier cards) when the job is comparing feature-by-feature — that’s this recipe.
  • Don't highlight two columns; the whole point is to aim the decision at one.
Compare
Starter$0/mo
Pro$19/mo
Scale$96/mo
Projects
3
Unlimited
Unlimited
Monthly tokens
10K
500K
Unlimited
Advanced exports
Audit log & SSO
Priority support
Dedicated CSM

Composes: Button

Best practices

Do
  • Reach for a comparison when the choice is “which tier?” — plans across the top, features down the side, one value per cell.
  • Highlight exactly one column (the current or recommended plan) — the band draws behind that tier so the eye lands on it.
  • Keep cells to a check, a dash, or a short value (“Unlimited”, “5”); a comparison is scanned, not read.
Don't
  • Don't use .pricing (standalone tier cards) when the job is comparing feature-by-feature — that’s this recipe.
  • Don't highlight two columns; the whole point is to aim the decision at one.

Recipe CSS

This is the exact CSS your kit ships for Plan Comparison — token-driven, so it re-themes with every knob. Get it (and the rest) from the configurator's Use kit panel.

/* === Plan comparison (.plan-compare) — a tier × feature matrix ==========
 * The upgrade-decision surface: plans across the top, features down the
 * side, a check / dash / value in each cell, and ONE highlighted column
 * (the current or recommended plan) drawn as a continuous band behind its
 * cells. A CSS grid (not a table) so the highlight can span every row while
 * the columns stay perfectly aligned. On the root set --pc-cols (tier count)
 * and --pc-hl (the 1-based grid column of the highlighted tier = its
 * index + 2, since column 1 is the feature-label rail). */
/* Scroll wrapper — on a narrow container the matrix scrolls horizontally
   instead of squishing its columns below readability (the same convention as
   the data table). Wrap the grid in this on any width-constrained surface. */
.plan-compare-scroll { overflow-x: auto; }
.plan-compare {
  display: grid;
  /* Keeps the columns readable; below this the wrapper scrolls. */
  min-width: 30rem;
  grid-template-columns: 1.3fr repeat(var(--pc-cols, 3), minmax(0, 1fr));
  /* Rows are made EXPLICIT (header + --pc-rows feature rows) so the highlight
     band's grid-row: 1 / -1 resolves to the full height — an absolutely-placed
     item only sees explicit line numbers, and implicit auto-rows wouldn't count. */
  grid-template-rows: auto repeat(var(--pc-rows, 6), auto);
  position: relative;
  align-items: stretch;
}
/* The highlight band — sits behind the featured column's cells, top to bottom.
 * position:absolute so it's placed INTO its grid area (the featured column,
 * all rows) without occupying a track for auto-placement — otherwise it would
 * block the featured tier's own header + cells from landing in that column. */
.plan-compare__hl {
  position: absolute;
  inset: 0;
  grid-row: 1 / -1;
  grid-column: var(--pc-hl, 2) / span 1;
  background: var(--k-surface-raised, var(--k-surface));
  border: 2px solid var(--k-primary);
  border-radius: var(--k-radius-md);
  z-index: 0;
}
.plan-compare__corner,
.plan-compare__head,
.plan-compare__feat,
.plan-compare__cell { position: relative; z-index: 1; }
/* Header row — one cell per tier: name, price, CTA. */
.plan-compare__corner { padding: var(--k-s-12) var(--k-s-10); display: flex; align-items: flex-end; }
.plan-compare__eyebrow {
  font-size: var(--k-type-eyebrow); font-weight: var(--k-weight-semibold);
  letter-spacing: var(--k-track-eyebrow); text-transform: uppercase; color: var(--k-fg-muted);
}
.plan-compare__head {
  display: flex; flex-direction: column; align-items: center; gap: var(--k-s-6);
  padding: var(--k-s-12) var(--k-s-10); text-align: center;
}
.plan-compare__name {
  font-size: var(--k-type-small); font-weight: var(--k-weight-semibold);
  letter-spacing: var(--k-track-eyebrow); text-transform: uppercase; color: var(--k-fg-muted);
}
.plan-compare__price { display: flex; align-items: baseline; gap: var(--k-s-2); }
.plan-compare__amount {
  font-size: var(--k-type-h3); font-weight: var(--k-weight-semibold);
  font-family: var(--k-font-display); font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em; color: var(--k-fg);
}
.plan-compare__per { font-size: var(--k-type-eyebrow); color: var(--k-fg-muted); }
.plan-compare__head .btn { width: 100%; margin-top: var(--k-s-2); }
/* Feature rows — label in the rail, a value under each tier; hairline between. */
.plan-compare__feat {
  display: flex; align-items: center;
  padding: var(--k-s-10);
  font-size: var(--k-type-small); color: var(--k-fg);
  border-top: var(--k-hairline, 1px solid var(--k-border));
}
.plan-compare__cell {
  display: flex; align-items: center; justify-content: center;
  padding: var(--k-s-10);
  font-size: var(--k-type-small); font-variant-numeric: tabular-nums; color: var(--k-fg);
  border-top: var(--k-hairline, 1px solid var(--k-border));
  text-align: center;
}
/* Cell marks — a success check for "included", a quiet dash for "not". */
.plan-compare__yes { color: var(--k-success); display: inline-flex; }
.plan-compare__yes svg { width: var(--k-icon-sm); height: var(--k-icon-sm); }
.plan-compare__no { color: var(--k-fg-faint); }