{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "pricing-table",
  "type": "registry:ui",
  "title": "Pricing Table",
  "description": "2-4 column pricing-tier grid. Each tier is a card with name, price, description, feature list, and a CTA button. The \"Most popular\" tier gets a highlight border. Server component.",
  "author": "ofri-peretz <https://github.com/ofri-peretz>",
  "categories": [
    "marketing",
    "pattern"
  ],
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "https://ds.interlace.tools/r/theme.json",
    "https://ds.interlace.tools/r/cn.json",
    "https://ds.interlace.tools/r/container.json",
    "https://ds.interlace.tools/r/grid.json",
    "https://ds.interlace.tools/r/skeleton.json",
    "https://ds.interlace.tools/r/stack.json",
    "https://ds.interlace.tools/r/typography.json"
  ],
  "files": [
    {
      "path": "registry/interlace-ui/patterns/pricing-table.tsx",
      "target": "components/ui/patterns/pricing-table.tsx",
      "type": "registry:ui",
      "content": "import * as React from 'react';\n\n// @interlace/pricing-table v1.2.0 — Interlace design system.\n// Docs, props and live preview: https://ds.interlace.tools/c/pricing-table\n// What changed since: https://ds.interlace.tools/c/pricing-table#history\n// Generated banner — keep it, the upgrade diff reads this version.\n\n/**\n * @interlace/ui — PricingTable\n *\n * 2-4 column pricing-tier grid. Each tier is a card with name, price,\n * description, feature list, and a CTA button. The \"Most popular\" tier\n * gets a highlight border. Server component.\n *\n * ## Anatomy\n *\n *   <section data-slot=\"pricing-table\" data-min-viewport=\"320\">\n *     <Container>\n *       <Grid>{tiers.map(<PricingCard/>)}</Grid>\n *     </Container>\n *   </section>\n *\n * | Rule | Concept                          | Where in this file                                          |\n * | ---- | -------------------------------- | ----------------------------------------------------------- |\n * | R4   | Extends native el                | `React.ComponentProps<'section'>` + PricingTable props      |\n * | R6   | data-slot on root                | `data-slot=\"pricing-table\"` + `data-slot=\"pricing-tier\"`    |\n * | R10  | Composition seam (tiers)         | Array<PricingTier>                                          |\n * | R14  | Declares min viewport            | `data-min-viewport={String(MIN_VIEWPORT)}` + exported const |\n * | R25  | Server component                 | No hooks                                                    |\n * | R26  | A11y                             | semantic <article>, <ul>, <button>                          |\n */\n\nimport { Check } from 'lucide-react';\n\nimport { cn } from '@/lib/utils';\nimport { Container } from '@/components/ui/container';\nimport { Grid } from '@/components/ui/grid';\nimport { Skeleton } from '@/components/ui/skeleton';\nimport { Stack } from '@/components/ui/stack';\nimport { Typography } from '@/components/ui/typography';\n\nexport const MIN_VIEWPORT = 320 as const;\n\ninterface PricingTier {\n  name: React.ReactNode;\n  price: React.ReactNode;\n  /** Optional sub-text after the price (e.g. \"/ month\"). */\n  pricePer?: React.ReactNode;\n  description?: React.ReactNode;\n  features?: React.ReactNode[];\n  /** CTA button. */\n  cta?: React.ReactNode;\n  /** Highlights the card with a brand-primary ring. Use on the \"Most popular\" tier. */\n  featured?: boolean;\n}\n\ninterface PricingTableProps extends Omit<React.ComponentProps<'section'>, 'title'> {\n  title?: React.ReactNode;\n  lead?: React.ReactNode;\n  tiers?: PricingTier[];\n  cols?: 2 | 3 | 4;\n  loading?: boolean;\n}\n\n/**\n * Mobile-first track counts per desktop `cols`. Written out statically because\n * Tailwind cannot scan a runtime-built `sm:grid-cols-${n}`.\n */\nconst TIER_GRID_COLS: Record<2 | 3 | 4, string> = {\n  2: 'grid-cols-1 sm:grid-cols-2',\n  3: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3',\n  4: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-4',\n};\n\nfunction PricingTable({\n  title,\n  lead,\n  tiers = [],\n  cols = 3,\n  loading,\n  className,\n  ...props\n}: PricingTableProps) {\n  return (\n    <section\n      data-slot=\"pricing-table\"\n      data-min-viewport={String(MIN_VIEWPORT)}\n      className={cn('py-xl', className)}\n      {...props}\n    >\n      <Container size=\"content\">\n        <Stack gap=\"lg\">\n          {(title || lead) && (\n            <Stack gap=\"sm\" align=\"center\" className=\"text-center\">\n              {title ? (\n                <Typography variant=\"h2\" as=\"h2\" className=\"text-balance\">\n                  {title}\n                </Typography>\n              ) : null}\n              {lead ? (\n                <Typography variant=\"long\" tone=\"muted\" className=\"max-w-prose\">\n                  {lead}\n                </Typography>\n              ) : null}\n            </Stack>\n          )}\n          {/* Responsive collapse: `cols` is a DESKTOP count. Left unqualified\n              it holds 3 tracks at every width — a 98px tier card on a 375px\n              phone, whose price and CTA button overflow the viewport. */}\n          <Grid cols={cols} gap=\"md\" className={TIER_GRID_COLS[cols]}>\n            {loading\n              ? Array.from({ length: cols }).map((_, i) => (\n                  <Skeleton key={i} variant=\"card\" label={null} />\n                ))\n              : tiers.map((tier, i) => (\n                  <article\n                    key={i}\n                    data-slot=\"pricing-tier\"\n                    data-featured={tier.featured ? '' : undefined}\n                    className={cn(\n                      'border-border bg-card text-card-foreground relative flex flex-col gap-md rounded-lg border p-md',\n                      tier.featured && 'border-primary ring-primary/40 ring-2',\n                    )}\n                  >\n                    {tier.featured ? (\n                      <span className=\"bg-primary text-primary-foreground absolute -top-3 right-md rounded-full px-sm py-xs text-xs font-semibold\">\n                        Most popular\n                      </span>\n                    ) : null}\n                    <Typography variant=\"h4\" as=\"h3\">\n                      {tier.name}\n                    </Typography>\n                    <div className=\"flex items-baseline gap-xs\">\n                      <Typography variant=\"h2\" as=\"span\" className=\"leading-none\">\n                        {tier.price}\n                      </Typography>\n                      {tier.pricePer ? (\n                        <Typography variant=\"ui\" as=\"span\" tone=\"muted\">\n                          {tier.pricePer}\n                        </Typography>\n                      ) : null}\n                    </div>\n                    {tier.description ? (\n                      <Typography variant=\"body\" tone=\"muted\">\n                        {tier.description}\n                      </Typography>\n                    ) : null}\n                    {tier.features && tier.features.length > 0 ? (\n                      <ul className=\"flex flex-col gap-xs text-sm\">\n                        {tier.features.map((feature, fi) => (\n                          <li key={fi} className=\"flex items-start gap-xs\">\n                            <Check\n                              aria-hidden\n                              className=\"text-primary mt-1 size-4 shrink-0\"\n                            />\n                            <span>{feature}</span>\n                          </li>\n                        ))}\n                      </ul>\n                    ) : null}\n                    {tier.cta ? <div className=\"mt-auto pt-md\">{tier.cta}</div> : null}\n                  </article>\n                ))}\n          </Grid>\n        </Stack>\n      </Container>\n    </section>\n  );\n}\nPricingTable.displayName = 'PricingTable';\n\nexport { PricingTable };\nexport type { PricingTableProps, PricingTier };\n"
    }
  ],
  "meta": {
    "tier": "pattern",
    "client": false,
    "minViewport": 320,
    "loading": true,
    "version": "1.2.0",
    "since": "1.0.0"
  },
  "docs": "## @interlace/pricing-table\n\nInstalled to `components/ui/patterns/pricing-table.tsx`.\n\n```tsx\nimport { /* … */ } from '@/components/ui/patterns/pricing-table';\n```\n\nProps, a11y contract, live preview and source: https://ds.interlace.tools/c/pricing-table\n\nRequires the `@interlace/theme` CSS baseline (installed automatically as a registry dependency)."
}
