{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "feature-rows",
  "type": "registry:block",
  "title": "Feature rows",
  "description": "Rows of media and text that alternate sides. Only the layout alternates: in reading order, the text always comes first.",
  "categories": [
    "section"
  ],
  "dependencies": [
    "lucide-react@^1.47.0"
  ],
  "registryDependencies": [
    "https://facadeui.dev/r/button.json",
    "https://facadeui.dev/r/container.json",
    "https://facadeui.dev/r/eyebrow.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-rows.tsx",
      "type": "registry:component",
      "target": "components/sections/feature-rows.tsx",
      "content": "/**\n * FeatureRows — alternating rows of media and copy.\n *\n * The long-form counterpart to `FeatureGrid`: each row gets a headline, a\n * paragraph, an optional bullet list and its own media, with the media side\n * alternating down the page.\n *\n * a11y: the alternation is `lg:order-*` only. In DOM order the copy always comes\n * first, so reading order and tab order match on every row regardless of which\n * side the media lands on visually — flipping the DOM instead would make a\n * screen reader meet an unlabelled image before the heading that explains it.\n *\n * Each row's bullets are a real `<ul>`, and the check icons are decorative.\n *\n * Dependencies: lucide-react, react, @/lib/types, @/lib/utils,\n * @/components/ui/button, @/components/ui/container, @/components/ui/eyebrow,\n * @/components/ui/heading, @/components/ui/section, @/components/ui/section-header.\n */\n\nimport { CheckIcon } from \"lucide-react\"\nimport type { ElementType, ReactNode } from \"react\"\n\nimport type {\n  HeadingLevel,\n  LinkComponent,\n  ListSlotProps,\n  SectionBaseProps,\n} from \"@/lib/types\"\nimport { cn, slugId } from \"@/lib/utils\"\nimport { buttonVariants } from \"@/components/ui/button\"\nimport { Container } from \"@/components/ui/container\"\nimport { Eyebrow } from \"@/components/ui/eyebrow\"\nimport { Heading } from \"@/components/ui/heading\"\nimport { Section } from \"@/components/ui/section\"\nimport { SectionHeader } from \"@/components/ui/section-header\"\n\nexport interface FeatureRowItem {\n  title: string\n  description: string\n  /** The row's media. Pass `<Image />`, a video, a diagram. */\n  media?: ReactNode\n  eyebrow?: string\n  bullets?: string[]\n  /** Optional inline link under the copy. */\n  href?: string\n  linkLabel?: string\n  external?: boolean\n  /** Stable key when two rows share a title. */\n  id?: string\n}\n\nexport interface FeatureRowsProps extends SectionBaseProps, ListSlotProps {\n  items: FeatureRowItem[]\n  title?: string\n  eyebrow?: string\n  description?: string\n  link?: LinkComponent\n  itemHeadingLevel?: HeadingLevel\n  /** Which side the first row's media sits on at `lg` and up. */\n  mediaFirst?: boolean\n  align?: \"start\" | \"center\"\n}\n\nexport function FeatureRows({\n  items,\n  title,\n  eyebrow,\n  description,\n  link,\n  itemHeadingLevel,\n  mediaFirst = false,\n  align = \"start\",\n  listAs,\n  itemAs,\n  headingLevel = 2,\n  as,\n  spacing = \"md\",\n  className,\n  id,\n}: FeatureRowsProps) {\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-16\">\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=\"flex flex-col gap-16 sm:gap-24\">\n          {items.map((item, index) => {\n            // Visual alternation only; the DOM order below never changes.\n            const mediaOnLeft = index % 2 === (mediaFirst ? 0 : 1)\n\n            return (\n              <Item\n                key={item.id ?? item.title}\n                className=\"grid items-center gap-8 lg:grid-cols-2 lg:gap-16\"\n              >\n                <div className={cn(\"flex flex-col gap-5\", mediaOnLeft && \"lg:order-2\")}>\n                  {item.eyebrow ? <Eyebrow tone=\"primary\">{item.eyebrow}</Eyebrow> : null}\n\n                  <Heading\n                    level={itemLevel}\n                    className=\"text-display-sm text-balance font-semibold\"\n                  >\n                    {item.title}\n                  </Heading>\n\n                  <p className=\"text-muted-foreground text-pretty text-lg\">\n                    {item.description}\n                  </p>\n\n                  {item.bullets?.length ? (\n                    <ul className=\"flex flex-col gap-2.5\">\n                      {item.bullets.map((bullet) => (\n                        <li key={bullet} className=\"flex items-start gap-3 text-sm\">\n                          <CheckIcon\n                            aria-hidden\n                            focusable=\"false\"\n                            className=\"text-primary mt-0.5 size-4 shrink-0\"\n                          />\n                          <span className=\"text-pretty\">{bullet}</span>\n                        </li>\n                      ))}\n                    </ul>\n                  ) : null}\n\n                  {item.href ? (\n                    <Link\n                      href={item.href}\n                      {...(item.external\n                        ? { target: \"_blank\", rel: \"noopener noreferrer\" }\n                        : {})}\n                      className={cn(\n                        buttonVariants({ variant: \"outline\", size: \"sm\" }),\n                        \"w-fit\",\n                      )}\n                    >\n                      {item.linkLabel ?? `More about ${item.title}`}\n                      {item.external ? (\n                        <span className=\"sr-only\"> (opens in a new tab)</span>\n                      ) : null}\n                    </Link>\n                  ) : null}\n                </div>\n\n                {item.media ? (\n                  <div className={cn(\"min-w-0\", mediaOnLeft && \"lg:order-1\")}>\n                    {item.media}\n                  </div>\n                ) : null}\n              </Item>\n            )\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-rows"
}
