{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "card-list",
  "type": "registry:block",
  "title": "Card list",
  "description": "A grid of linked cards for blog posts, changelog entries or case studies. Dates are marked up so that software can read them.",
  "categories": [
    "section"
  ],
  "registryDependencies": [
    "https://facadeui.dev/r/badge.json",
    "https://facadeui.dev/r/container.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/card-list.tsx",
      "type": "registry:component",
      "target": "components/sections/card-list.tsx",
      "content": "/**\n * CardList — a grid of linked cards: blog posts, changelog entries, case studies.\n *\n * The generic \"list of things with an image, a title and a date\" section that\n * every marketing site needs and that does not warrant its own component per\n * content type.\n *\n * a11y: same stretched-link pattern as `FeatureGrid` — the anchor sits on the\n * title and its hit area covers the card, so the accessible name is the title\n * rather than the title plus the excerpt plus the date. Dates are `<time>` with\n * a machine-readable `dateTime`, and the human label is whatever you pass, so a\n * relative label like \"3 days ago\" stays unambiguous.\n *\n * Cover images are `alt=\"\"` by default: on a card whose title is right there,\n * the image is decoration. Pass `imageAlt` when it genuinely carries meaning.\n *\n * Dependencies: react, @/lib/types, @/lib/utils,\n * @/components/ui/badge, @/components/ui/container, @/components/ui/heading,\n * @/components/ui/section, @/components/ui/section-header.\n */\n\nimport type { ElementType } from \"react\"\n\nimport type {\n  HeadingLevel,\n  ImageComponent,\n  LinkComponent,\n  ListSlotProps,\n  SectionBaseProps,\n} from \"@/lib/types\"\nimport { cn, slugId } from \"@/lib/utils\"\nimport { Badge } from \"@/components/ui/badge\"\nimport { Container } from \"@/components/ui/container\"\nimport { Heading } from \"@/components/ui/heading\"\nimport { Section } from \"@/components/ui/section\"\nimport { SectionHeader } from \"@/components/ui/section-header\"\n\nexport interface CardItem {\n  title: string\n  href: string\n  description?: string\n  imageSrc?: string\n  /** Only set this when the image carries meaning the title does not. */\n  imageAlt?: string\n  /** ISO 8601, for the `datetime` attribute. */\n  dateTime?: string\n  /** Human label for the date, e.g. \"12 March 2026\" or \"3 days ago\". */\n  dateLabel?: string\n  tag?: string\n  external?: boolean\n  id?: string\n}\n\nexport interface CardListProps extends SectionBaseProps, ListSlotProps {\n  items: CardItem[]\n  title?: string\n  eyebrow?: string\n  description?: string\n  image?: ImageComponent\n  link?: LinkComponent\n  columns?: 2 | 3\n  /** `card` adds a border and background to each entry. `plain` is a simple list. */\n  variant?: \"card\" | \"plain\"\n  itemHeadingLevel?: HeadingLevel\n  align?: \"start\" | \"center\"\n}\n\nexport function CardList({\n  items,\n  title,\n  eyebrow,\n  description,\n  image,\n  link,\n  columns = 3,\n  variant = \"card\",\n  itemHeadingLevel,\n  align = \"start\",\n  listAs,\n  itemAs,\n  headingLevel = 2,\n  as,\n  spacing = \"md\",\n  className,\n  id,\n}: CardListProps) {\n  const List = (listAs ?? \"ul\") as ElementType\n  const Item = (itemAs ?? \"li\") as ElementType\n  const Image = (image ?? \"img\") 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\n          className={cn(\n            \"grid gap-6\",\n            columns === 2 ? \"sm:grid-cols-2\" : \"sm:grid-cols-2 lg:grid-cols-3\",\n          )}\n        >\n          {items.map((item) => (\n            <Item\n              key={item.id ?? item.href}\n              className={cn(\n                \"group relative flex flex-col gap-4 overflow-hidden\",\n                variant === \"card\" && \"bg-card text-card-foreground rounded-xl border\",\n                \"duration-facade-fast ease-facade-out transition-colors\",\n                variant === \"card\" && \"hover:border-ring\",\n                \"has-[a:focus-visible]:ring-ring has-[a:focus-visible]:ring-offset-background has-[a:focus-visible]:rounded-xl has-[a:focus-visible]:ring-2 has-[a:focus-visible]:ring-offset-2\",\n              )}\n            >\n              {item.imageSrc ? (\n                <Image\n                  src={item.imageSrc}\n                  alt={item.imageAlt ?? \"\"}\n                  loading=\"lazy\"\n                  decoding=\"async\"\n                  className={cn(\n                    \"bg-muted aspect-[16/9] w-full object-cover\",\n                    variant === \"plain\" && \"rounded-xl\",\n                  )}\n                />\n              ) : null}\n\n              <div\n                className={cn(\n                  \"flex flex-1 flex-col gap-3\",\n                  variant === \"card\" && \"p-6 pt-2\",\n                )}\n              >\n                {item.tag || item.dateLabel ? (\n                  <p className=\"text-muted-foreground flex flex-wrap items-center gap-3 text-sm\">\n                    {item.tag ? (\n                      <Badge variant=\"muted\" size=\"sm\">\n                        {item.tag}\n                      </Badge>\n                    ) : null}\n                    {item.dateLabel ? (\n                      <time dateTime={item.dateTime}>{item.dateLabel}</time>\n                    ) : null}\n                  </p>\n                ) : null}\n\n                <Heading\n                  level={itemLevel}\n                  className=\"text-foreground text-balance font-semibold\"\n                >\n                  <Link\n                    href={item.href}\n                    {...(item.external\n                      ? { target: \"_blank\", rel: \"noopener noreferrer\" }\n                      : {})}\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                </Heading>\n\n                {item.description ? (\n                  <p className=\"text-muted-foreground text-pretty text-sm\">\n                    {item.description}\n                  </p>\n                ) : null}\n              </div>\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/card-list"
}
