{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "collection-grid",
  "title": "Collection Grid",
  "author": "ecomcn <hello@ecomcn.dev>",
  "description": "Three collections as a magazine spread: one dominant tile spanning both rows on a 7/5 split, two supporting it, the type scaling with the tile. On a phone the lead stays full width and tall and the other two pair up beneath it, smaller — never three identical cards. Ruled 1px apart; a scrim keeps the words legible on any photo. Renders on the server.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "src/registry/ecomcn/collection-grid/collection-grid.tsx",
      "content": "import * as React from \"react\"\nimport { ArrowRight } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/*\n * Three collections as a magazine spread: one dominant tile, two\n * supporting.\n *\n *   <CollectionGrid tiles={[lead, second, third]} title=\"The edit\" />\n *\n * A 12-column grid split 7 / 5: the lead tile spans both rows on the left,\n * the other two stack on the right, and the type scale follows the tile —\n * the big one gets the big heading. On a phone it never collapses into\n * three identical cards: the lead stays full width and tall, and the two\n * supporting tiles pair up beneath it, smaller, with smaller type. Tiles\n * are ruled 1px apart over your border colour. No state, no \"use client\":\n * it renders on the server.\n */\n\nexport interface CollectionTile {\n  id: string\n  title: string\n  href: string\n  /** \"Autumn edit\", \"New in\". */\n  eyebrow?: string\n  /** Shown on the lead tile only — the others are too small for it. */\n  description?: string\n  /** \"Shop the edit\", by default. */\n  cta?: string\n  /** Already rendered, so the block never assumes next/image. It fills the tile. */\n  image: React.ReactNode\n}\n\nexport interface CollectionGridProps extends Omit<React.HTMLAttributes<HTMLElement>, \"title\"> {\n  /** The first is the lead; the next two support it. More are ignored. */\n  tiles: CollectionTile[]\n  title?: React.ReactNode\n}\n\nexport function CollectionGrid({ tiles, title, className, ...props }: CollectionGridProps) {\n  const [lead, ...rest] = tiles.slice(0, 3)\n  const headingId = React.useId()\n  if (!lead) return null\n\n  return (\n    <section\n      data-slot=\"collection-grid\"\n      aria-labelledby={title ? headingId : undefined}\n      className={cn(\"flex flex-col gap-5\", className)}\n      {...props}\n    >\n      {title ? (\n        <h2 id={headingId} className=\"border-b pb-4 text-2xl\">\n          {title}\n        </h2>\n      ) : null}\n      <div className=\"grid grid-cols-2 gap-px border bg-border lg:grid-cols-12 lg:grid-rows-2\">\n        <Tile tile={lead} size=\"lead\" className=\"col-span-2 aspect-[4/5] sm:aspect-[16/11] lg:col-span-7 lg:row-span-2 lg:aspect-auto\" />\n        {rest.map((tile) => (\n          <Tile key={tile.id} tile={tile} size=\"support\" className=\"col-span-1 aspect-square lg:col-span-5 lg:aspect-[16/10]\" />\n        ))}\n      </div>\n    </section>\n  )\n}\n\nfunction Tile({ tile, size, className }: { tile: CollectionTile; size: \"lead\" | \"support\"; className?: string }) {\n  const lead = size === \"lead\"\n  return (\n    <a\n      href={tile.href}\n      data-slot=\"collection-grid-tile\"\n      data-size={size}\n      className={cn(\n        \"group relative isolate flex flex-col justify-end overflow-hidden bg-secondary text-white outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset\",\n        className\n      )}\n    >\n      <span className=\"absolute inset-0 -z-10 transition-transform duration-700 ease-out group-hover:scale-[1.03] [&>img]:size-full [&>img]:object-cover\">\n        {tile.image}\n      </span>\n      {/* A scrim under the words, so they read on any photo. */}\n      <span aria-hidden className=\"absolute inset-x-0 bottom-0 -z-10 h-2/3 bg-gradient-to-t from-black/65 via-black/25 to-transparent\" />\n      <span className={cn(\"flex flex-col\", lead ? \"gap-3 p-6 sm:p-8 lg:p-10\" : \"gap-1.5 p-4 sm:p-6\")}>\n        {tile.eyebrow ? (\n          <span className={cn(\"tracking-[0.16em] text-white/80 uppercase\", lead ? \"text-xs\" : \"text-[10.5px]\")}>{tile.eyebrow}</span>\n        ) : null}\n        <span\n          className={cn(\n            \"font-serif leading-[1.02] tracking-tight text-balance\",\n            lead ? \"text-4xl sm:text-5xl lg:text-6xl\" : \"text-xl sm:text-2xl lg:text-3xl\"\n          )}\n        >\n          {tile.title}\n        </span>\n        {lead && tile.description ? <span className=\"max-w-sm text-sm leading-relaxed text-white/85\">{tile.description}</span> : null}\n        <span className={cn(\"inline-flex items-center gap-2 underline-offset-4 group-hover:underline\", lead ? \"mt-2 text-sm\" : \"text-[12.5px] max-sm:sr-only\")}>\n          {tile.cta ?? \"Shop the edit\"}\n          <ArrowRight aria-hidden className=\"size-3.5 transition-transform group-hover:translate-x-0.5\" />\n        </span>\n      </span>\n    </a>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/ecomcn/collection-grid.tsx"
    }
  ],
  "categories": [
    "ecommerce",
    "marketing"
  ],
  "type": "registry:block"
}