ComponentsSite blocks
A plan × feature detail table, or a two-column “us vs. them”. A real <table> with a caption, scoped row and column headers, grouped rows with inset band headings and hidden Yes/No text behind every icon. The header row sticks; the highlighted plan gets a faint accent band. On narrow containers a radio plan picker shows one column at a time — CSS only.
Native table + radios + :has() blocks/site/comparison-table.tsx, blocks/site/registry-2.ts, styles/blocks.csstheme · material · accent · density · font
Compare
Every plan, line by line
| Feature | Solo$29 / monthStart | Studio$79 / monthStart | GroupCustomTalk to sales |
|---|---|---|---|
| Booking | |||
| Calendars | 1 | Up to 8 | Unlimited |
| Deposits and prepaymentCard payments through iyzico; refunds from the dashboard. | No | Yes | Yes |
| Waiting list | No | Yes | Yes |
| Messaging | |||
| WhatsApp reminders | 300 / mo | 2,000 / mo | Unlimited |
| SMS fallback | No | Yes | Yes |
| Account | |||
| Single sign-on | No | No | Yes |
| Audit logWho moved, cancelled or refunded what, and when. | No | 30 days | 1 year |
| Account manager | No | No | Yes |
Installation
$ pnpm add @vamos-ui/react
The whole kit as one package — get updates by bumping the version. Add the styles to globals.css: @import "@vamos-ui/react/styles.css";
Usage
import { ComparisonTable } from "@vamos-ui/react/blocks";
<ComparisonTable
title="Every plan, *line by line*"
columns={[{ name: "Solo" }, { name: "Studio", highlight: true }, { name: "Group" }]}
groups={[{ title: "Booking", rows: [
{ feature: "Calendars", values: ["1", "Up to 8", "Unlimited"] },
{ feature: "Deposits", hint: "Card payments through iyzico.", values: [false, true, true] },
] }]}
/>Examples
Versus (agency vs. template)
Two columns everywhere, the end column muted. Extra columns are ignored.
Why custom
A built site vs. a template
| Feature | Built with Vamos | Theme marketplace template |
|---|---|---|
| Loads in under 2 s on 4G | Yes | No |
| Booking flow | Built in, with deposits | Third-party plugin |
| Turkish and English URLs | Yes | Paid add-on |
| KVKK-ready consent banner | Yes | No |
| Who fixes it at 9 pm on a Friday | We do | A support forum |
Phone width: plan picker
Below 42rem container width a fieldset of radios picks the visible plan (highlighted plan preselected); arrow keys switch plans.
Compare memberships
| Feature | Day pass₺1,450 | Monthly₺4,200 / mo | Yearly₺39,000 / yr |
|---|---|---|---|
| Included | |||
| Hammam and sauna | Yes | Yes | Yes |
| Pool hours | 10:00–18:00 | All day | All day |
| Massage discount | No | 10% | 20% |
| Guest passes | No | 1 / month | 2 / month |
API
ComparisonTable
| Prop | Type | Default |
|---|---|---|
varianttable: 2–5 plans with picker; versus: two columns. | "table" | "versus" | "table" |
columns2–5 column headers; more are dropped (dev warning). | { name; note?; cta?; highlight? }[] | — |
groupstrue → check + “Yes”, false → dash + “No”, null → “—”, string → the value. | { title?; rows: { feature; hint?; values: (boolean | string | null)[] }[] }[] | — |
captionTable caption for screen readers. Default: the title. | string | — |
eyebrow / title / leadSection header. | ReactNode | — |
labels / localeYes, No, Feature, Show plan… | Partial<Site2Labels> / string | — |
theme / anchor / id / className / headingLevel / components / editModeShared block props. | BlockBaseProps | — |