{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "feature-grid",
  "type": "registry:block",
  "title": "Feature grid",
  "description": "A features section: a grid of feature cards, two to four per row. A whole card can be a link while its accessible name stays short.",
  "categories": [
    "section"
  ],
  "dependencies": [
    "lucide-react@^1.47.0"
  ],
  "registryDependencies": [
    "https://facadeui.dev/r/container.json",
    "https://facadeui.dev/r/feature-icon.json",
    "https://facadeui.dev/r/heading.json",
    "https://facadeui.dev/r/section.json",
    "https://facadeui.dev/r/section-header.json",
    "https://facadeui.dev/r/types.json",
    "https://facadeui.dev/r/utils.json"
  ],
  "files": [
    {
      "path": "src/sections/feature-grid.tsx",
      "type": "registry:component",
      "target": "components/sections/feature-grid.tsx",
      "content": "/**\n * FeatureGrid — a grid of feature cards, two to four across.\n *\n * Each item may carry an icon, a title, body copy, and an optional link. When an\n * item has an `href`, the whole card becomes clickable through a stretched\n * overlay rather than by wrapping the card in an anchor.\n *\n * a11y: the stretched-link pattern is used deliberately. Wrapping a card in an\n * `<a>` puts every word of body copy inside the link text, so the accessible\n * name becomes a paragraph; putting the anchor on the title and stretching its\n * hit area with a pseudo-element keeps the name short while making the whole\n * card clickable. The focus ring follows the card, not the title, via\n * `has-[a:focus-visible]`.\n *\n * Dependencies: lucide-react, react, @/lib/types, @/lib/utils,\n * @/components/ui/container, @/components/ui/feature-icon, @/components/ui/heading,\n * @/components/ui/section, @/components/ui/section-header.\n */\n\nimport { ArrowRightIcon } from \"lucide-react\"\nimport type { ElementType } from \"react\"\n\nimport type {\n  HeadingLevel,\n  IconComponent,\n  LinkComponent,\n  ListSlotProps,\n  SectionBaseProps,\n} from \"@/lib/types\"\nimport { cn, slugId } from \"@/lib/utils\"\nimport { Container } from \"@/components/ui/container\"\nimport { FeatureIcon } from \"@/components/ui/feature-icon\"\nimport { Heading } from \"@/components/ui/heading\"\nimport { Section } from \"@/components/ui/section\"\nimport { SectionHeader } from \"@/components/ui/section-header\"\n\nexport interface FeatureItem {\n  title: string\n  description: string\n  icon?: IconComponent\n  /** Makes the card clickable. The accessible name stays the title. */\n  href?: string\n  /** Label for the affordance under the copy. Defaults to \"Learn more\". */\n  linkLabel?: string\n  external?: boolean\n}\n\nexport interface FeatureGridProps extends SectionBaseProps, ListSlotProps {\n  items: FeatureItem[]\n  title?: string\n  eyebrow?: string\n  description?: string\n  link?: LinkComponent\n  columns?: 2 | 3 | 4\n  itemHeadingLevel?: HeadingLevel\n  /** `card` adds a border and background. `plain` has neither. */\n  variant?: \"card\" | \"plain\"\n  iconVariant?: \"soft\" | \"solid\" | \"outline\" | \"plain\"\n  align?: \"start\" | \"center\"\n}\n\nconst columnClasses = {\n  2: \"sm:grid-cols-2\",\n  3: \"sm:grid-cols-2 lg:grid-cols-3\",\n  4: \"sm:grid-cols-2 lg:grid-cols-4\",\n} as const\n\nexport function FeatureGrid({\n  items,\n  title,\n  eyebrow,\n  description,\n  link,\n  columns = 3,\n  itemHeadingLevel,\n  variant = \"card\",\n  iconVariant = \"soft\",\n  align = \"start\",\n  listAs,\n  itemAs,\n  headingLevel = 2,\n  as,\n  spacing = \"md\",\n  className,\n  id,\n}: FeatureGridProps) {\n  const List = (listAs ?? \"ul\") as ElementType\n  const Item = (itemAs ?? \"li\") as ElementType\n  const Link = (link ?? \"a\") as ElementType\n  const titleId = title ? (id ? `${id}-title` : slugId(title)) : undefined\n  const itemLevel = itemHeadingLevel ?? (Math.min(headingLevel + 1, 6) as HeadingLevel)\n\n  return (\n    <Section\n      as={as ?? (title ? \"section\" : \"div\")}\n      spacing={spacing}\n      labelledBy={titleId}\n      id={id}\n      className={className}\n    >\n      <Container className=\"flex flex-col gap-14\">\n        {title ? (\n          <SectionHeader\n            align={align}\n            eyebrow={eyebrow}\n            title={title}\n            description={description}\n            headingLevel={headingLevel}\n            titleId={titleId}\n          />\n        ) : null}\n\n        <List className={cn(\"grid gap-6\", columnClasses[columns])}>\n          {items.map((item) => (\n            <Item\n              key={item.title}\n              className={cn(\n                \"relative flex flex-col gap-4\",\n                variant === \"card\" &&\n                  \"bg-card text-card-foreground rounded-xl border p-6\",\n                item.href &&\n                  \"duration-facade-fast ease-facade-out hover:border-ring has-[a:focus-visible]:ring-ring has-[a:focus-visible]:ring-offset-background transition-colors has-[a:focus-visible]:ring-2 has-[a:focus-visible]:ring-offset-2\",\n              )}\n            >\n              {item.icon ? <FeatureIcon icon={item.icon} variant={iconVariant} /> : null}\n\n              <Heading level={itemLevel} className=\"text-foreground font-semibold\">\n                {item.href ? (\n                  <Link\n                    href={item.href}\n                    {...(item.external\n                      ? { target: \"_blank\", rel: \"noopener noreferrer\" }\n                      : {})}\n                    // The overlay makes the card clickable while the accessible\n                    // name stays just the title.\n                    className=\"outline-none after:absolute after:inset-0 after:content-['']\"\n                  >\n                    {item.title}\n                    {item.external ? (\n                      <span className=\"sr-only\"> (opens in a new tab)</span>\n                    ) : null}\n                  </Link>\n                ) : (\n                  item.title\n                )}\n              </Heading>\n\n              <p className=\"text-muted-foreground flex-1 text-pretty text-sm\">\n                {item.description}\n              </p>\n\n              {item.href ? (\n                <p\n                  aria-hidden\n                  className=\"text-foreground flex items-center gap-1.5 text-sm font-medium\"\n                >\n                  {item.linkLabel ?? \"Learn more\"}\n                  <ArrowRightIcon className=\"size-4\" />\n                </p>\n              ) : null}\n            </Item>\n          ))}\n        </List>\n      </Container>\n    </Section>\n  )\n}\n"
    }
  ],
  "docs": "Import styles/facade-tokens.css in your global stylesheet, after @import \"tailwindcss\". Set headingLevel to fit the page outline (sections default to h2). Pass next/link and next/image through the link and image props. Docs: https://facadeui.dev/components/feature-grid"
}
