{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "home-layout",
  "title": "Home layout",
  "description": "HomeLayout: a layout for landing pages with a navbar (links, navigation menus, search, theme switch) and a mobile menu, plus DefaultNotFound for 404 pages.",
  "dependencies": [
    "@base-ui/react",
    "class-variance-authority",
    "cn",
    "@fuma-translate/react@^1.0.2",
    "fumadocs-core@^16.17.8",
    "lucide-react"
  ],
  "registryDependencies": [
    "button",
    "collapsible",
    "@docscn/layout-shared",
    "@docscn/theme-switch",
    "@docscn/search-trigger",
    "@docscn/docs-utils",
    "@docscn/i18n-context",
    "@docscn/language-select"
  ],
  "files": [
    {
      "path": "registry/base/docs/layouts/home/index.tsx",
      "content": "// Adapted from Fumadocs UI (https://github.com/fuma-nama/fumadocs)\n// Copyright (c) 2023 Fuma, MIT License\n\"use client\";\n\nimport { type ComponentProps, createContext, use } from \"react\";\nimport {\n  type BaseLayoutProps,\n  type LinkItemType,\n  type NavOptions,\n  useLinkItems,\n} from \"../shared\";\nimport { Container } from \"./slots/container\";\nimport { Header } from \"./slots/header\";\n\nexport interface HomeLayoutProps\n  extends BaseLayoutProps, Omit<ComponentProps<\"main\">, \"children\"> {\n  nav?: Nav;\n}\n\ninterface Nav extends NavOptions {\n  /**\n   * Open mobile menu when hovering the trigger\n   */\n  enableHoverToOpen?: boolean;\n}\n\nconst LayoutContext = createContext<{\n  props: Pick<HomeLayoutProps, \"nav\" | \"themeSwitch\" | \"searchToggle\">;\n  navItems: LinkItemType[];\n  menuItems: LinkItemType[];\n} | null>(null);\n\nexport function useHomeLayout() {\n  const context = use(LayoutContext);\n  if (!context)\n    throw new Error(\n      \"Please use this component under <HomeLayout /> (`components/docs/layouts/home`).\",\n    );\n  return context;\n}\n\nexport function HomeLayout(props: HomeLayoutProps) {\n  const {\n    nav,\n    children,\n    githubUrl,\n    links,\n    themeSwitch,\n    searchToggle,\n    ...rest\n  } = props;\n  const linkItems = useLinkItems({ githubUrl, links });\n\n  return (\n    <LayoutContext\n      value={{\n        props: { nav, themeSwitch, searchToggle },\n        ...linkItems,\n      }}\n    >\n      <Container {...rest}>\n        {(nav?.enabled ?? true) && <Header />}\n        {children}\n      </Container>\n    </LayoutContext>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/docs/layouts/home/index.tsx"
    },
    {
      "path": "registry/base/docs/layouts/home/navbar.tsx",
      "content": "// Adapted from Fumadocs UI (https://github.com/fuma-nama/fumadocs)\n// Copyright (c) 2023 Fuma, MIT License\n\"use client\";\nimport type { ComponentProps } from \"react\";\nimport Link, { type LinkProps } from \"fumadocs-core/link\";\nimport { NavigationMenu as Primitive } from \"@base-ui/react/navigation-menu\";\nimport { cn } from \"cn\";\nimport { navItemVariants } from \"./slots/header\";\n\nexport function NavbarMenu({\n  className,\n  children,\n  ...props\n}: ComponentProps<typeof Primitive.Item>) {\n  return (\n    <Primitive.Item\n      className={(s) =>\n        cn(\n          \"list-none\",\n          typeof className === \"function\" ? className(s) : className,\n        )\n      }\n      {...props}\n    >\n      {children}\n    </Primitive.Item>\n  );\n}\n\nexport function NavbarMenuContent({\n  className,\n  ...props\n}: ComponentProps<typeof Primitive.Content>) {\n  return (\n    <Primitive.Content\n      {...props}\n      className={(s) =>\n        cn(\n          \"h-full w-(--anchor-width) max-w-(--available-width) p-3\",\n          \"transition-[opacity,transform,translate] duration-(--duration) ease-(--easing)\",\n          \"data-ending-style:opacity-0 data-starting-style:opacity-0\",\n          \"data-starting-style:data-[activation-direction=left]:-translate-x-1/2\",\n          \"data-starting-style:data-[activation-direction=right]:translate-x-1/2\",\n          \"data-ending-style:data-[activation-direction=left]:translate-x-1/2\",\n          \"data-ending-style:data-[activation-direction=right]:-translate-x-1/2\",\n          \"grid grid-cols-1 gap-2 md:grid-cols-2 lg:grid-cols-3\",\n          typeof className === \"function\" ? className(s) : className,\n        )\n      }\n    />\n  );\n}\n\nexport function NavbarMenuTrigger({\n  className,\n  children,\n  ...props\n}: ComponentProps<typeof Primitive.Trigger>) {\n  return (\n    <Primitive.Trigger\n      {...props}\n      className={(s) =>\n        cn(\n          navItemVariants(),\n          \"rounded-md text-sm\",\n          typeof className === \"function\" ? className(s) : className,\n        )\n      }\n    >\n      {children}\n    </Primitive.Trigger>\n  );\n}\n\nexport function NavbarMenuLink(props: LinkProps) {\n  return (\n    <Primitive.Link\n      render={\n        <Link\n          {...props}\n          className={cn(\n            \"flex flex-col gap-2 rounded-lg border bg-card p-3 transition-colors hover:bg-accent/80 hover:text-accent-foreground\",\n            props.className,\n          )}\n        >\n          {props.children}\n        </Link>\n      }\n    />\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/docs/layouts/home/navbar.tsx"
    },
    {
      "path": "registry/base/docs/layouts/home/not-found.tsx",
      "content": "// Adapted from Fumadocs UI (https://github.com/fuma-nama/fumadocs)\n// Copyright (c) 2023 Fuma, MIT License\n\"use client\";\n\nimport { useTranslations } from \"@fuma-translate/react\";\nimport { buttonVariants } from \"@/components/ui/button\";\nimport { cn } from \"cn\";\nimport Link from \"fumadocs-core/link\";\nimport { HomeIcon } from \"lucide-react\";\n\n/**\n * the default not found page content, please make your own if you want to customize it.\n */\nexport function DefaultNotFound() {\n  const t = useTranslations({ note: \"404 not found page\" });\n\n  return (\n    <div className=\"flex flex-1 flex-col items-center justify-center gap-4 px-8 text-center\">\n      <h1 className=\"text-6xl font-bold text-muted-foreground\">404</h1>\n      <h2 className=\"text-2xl font-semibold\">{t(\"Page Not Found\")}</h2>\n      <p className=\"max-w-md text-muted-foreground\">\n        {t(\n          \"The page you are looking for might have been removed, had its name changed, or is temporarily unavailable.\",\n        )}\n      </p>\n      <Link\n        href=\"/\"\n        className={cn(\n          buttonVariants({\n            className: \"mt-4 gap-1.5\",\n            variant: \"default\",\n          }),\n        )}\n      >\n        <HomeIcon className=\"size-4\" />\n        {t(\"Back to Home\")}\n      </Link>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/docs/layouts/home/not-found.tsx"
    },
    {
      "path": "registry/base/docs/layouts/home/slots/header.tsx",
      "content": "// Adapted from Fumadocs UI (https://github.com/fuma-nama/fumadocs)\n// Copyright (c) 2023 Fuma, MIT License\n\"use client\";\n\nimport { useTranslations } from \"@fuma-translate/react\";\nimport {\n  type ComponentProps,\n  createContext,\n  Fragment,\n  use,\n  useEffect,\n  useEffectEvent,\n  useMemo,\n  useRef,\n  useState,\n} from \"react\";\nimport { cva } from \"class-variance-authority\";\nimport Link from \"fumadocs-core/link\";\nimport { NavigationMenu as Primitive } from \"@base-ui/react/navigation-menu\";\nimport { cn } from \"cn\";\nimport { type LinkItemType, LinkItem, NavTitle } from \"../../shared\";\nimport {\n  FullSearchTrigger,\n  SearchTrigger,\n} from \"../../shared/slots/search-trigger\";\nimport {\n  LanguageSelect,\n  LanguageSelectText,\n} from \"../../shared/slots/language-select\";\nimport { ThemeSwitch } from \"../../shared/slots/theme-switch\";\nimport { buttonVariants } from \"@/components/ui/button\";\nimport { ChevronDown, Languages } from \"lucide-react\";\nimport { useI18n } from \"../../../contexts/i18n\";\nimport { useIsScrollTop } from \"../../../utils/use-is-scroll-top\";\nimport { useHomeLayout } from \"..\";\nimport {\n  Collapsible,\n  CollapsibleContent,\n  CollapsibleTrigger,\n} from \"@/components/ui/collapsible\";\nimport { mergeRefs } from \"../../../utils/merge-refs\";\n\nexport const navItemVariants = cva(\"[&_svg]:size-4\", {\n  variants: {\n    variant: {\n      main: \"inline-flex items-center gap-1 p-2 text-muted-foreground transition-colors hover:text-accent-foreground data-[active=true]:text-foreground\",\n      button: buttonVariants({\n        variant: \"secondary\",\n        className: \"gap-1.5\",\n      }),\n      icon: buttonVariants({\n        variant: \"ghost\",\n        size: \"icon\",\n      }),\n    },\n  },\n  defaultVariants: {\n    variant: \"main\",\n  },\n});\n\nconst MobileNavigationMenuContext = createContext<{\n  setOpen: (v: boolean) => void;\n} | null>(null);\n\nexport function Header({ ref, className, ...props }: ComponentProps<\"header\">) {\n  const {\n    navItems,\n    menuItems,\n    props: { nav, themeSwitch = {}, searchToggle = {} },\n  } = useHomeLayout();\n  const { enabled: themeSwitchEnabled = true, ...themeSwitchProps } =\n    themeSwitch;\n  const { enabled: searchEnabled = true } = searchToggle;\n  const headerRef = useRef<HTMLElement>(null);\n  const listRef = useRef<HTMLUListElement>(null);\n  const [open, setOpen] = useState(false);\n  const t = useTranslations({ note: \"home layout header\" });\n  const { locales = [] } = useI18n();\n  const languageSelect = locales.length > 1;\n  const transparentMode = nav?.transparentMode ?? \"none\";\n  const isTop = useIsScrollTop({ enabled: transparentMode === \"top\" }) ?? true;\n  const isNavTransparent =\n    transparentMode === \"top\" ? isTop : transparentMode === \"always\";\n\n  const onClick = useEffectEvent((e: Event) => {\n    const element = headerRef.current;\n    if (!open || !element) return;\n    if (element !== e.target && !element.contains(e.target as HTMLElement)) {\n      setOpen(false);\n    }\n  });\n\n  useEffect(() => {\n    window.addEventListener(\"click\", onClick);\n\n    return () => {\n      window.removeEventListener(\"click\", onClick);\n    };\n  }, []);\n\n  const list = (\n    <Primitive.List\n      ref={listRef}\n      // defaults to <ul>, but its children (nav title, link groups, controls) are not <li> —\n      // render a <div> for valid list semantics, like the Radix UI variant does with `asChild`\n      render={<div />}\n      className=\"mx-auto flex h-14 w-full max-w-[1400px] items-center px-4\"\n    >\n      <NavTitle\n        nav={nav}\n        className=\"inline-flex items-center gap-2.5 font-semibold\"\n      />\n      {nav?.children}\n      <ul className=\"flex flex-row items-center gap-2 px-6 max-sm:hidden\">\n        {navItems\n          .filter((item) => !isSecondary(item))\n          .map((item, i) => (\n            <NavigationMenuLinkItem key={i} item={item} className=\"text-sm\" />\n          ))}\n      </ul>\n      <div className=\"flex flex-1 flex-row items-center justify-end gap-1.5 max-lg:hidden\">\n        {searchEnabled && (\n          <FullSearchTrigger\n            hideIfDisabled\n            {...searchToggle.full}\n            className={cn(\"w-full max-w-[240px]\", searchToggle.full?.className)}\n          />\n        )}\n        {themeSwitchEnabled && <ThemeSwitch {...themeSwitchProps} />}\n        {languageSelect && (\n          <LanguageSelect className=\"text-muted-foreground\">\n            <Languages />\n          </LanguageSelect>\n        )}\n        <ul className=\"flex flex-row items-center gap-2 empty:hidden\">\n          {navItems.filter(isSecondary).map((item, i) => (\n            <NavigationMenuLinkItem\n              key={i}\n              className={cn(\n                item.type === \"icon\" && \"-mx-1 first:ms-0 last:me-0\",\n              )}\n              item={item}\n            />\n          ))}\n        </ul>\n      </div>\n      <div className=\"ms-auto -me-1.5 flex flex-row items-center lg:hidden\">\n        {searchEnabled && <SearchTrigger hideIfDisabled {...searchToggle.sm} />}\n        <CollapsibleTrigger\n          aria-label={t(\"Toggle Menu\", { note: \"aria-label\" })}\n          className={cn(\n            buttonVariants({\n              size: \"icon\",\n              variant: \"ghost\",\n            }),\n          )}\n          onPointerEnter={\n            nav?.enableHoverToOpen\n              ? () => {\n                  setOpen(true);\n                }\n              : undefined\n          }\n        >\n          <ChevronDown\n            className={cn(\"transition-transform\", open && \"rotate-180\")}\n          />\n        </CollapsibleTrigger>\n      </div>\n    </Primitive.List>\n  );\n\n  return (\n    <Collapsible\n      open={open}\n      onOpenChange={setOpen}\n      render={\n        <header\n          id=\"nd-nav\"\n          {...props}\n          ref={mergeRefs(headerRef, ref)}\n          className={cn(\n            \"sticky top-(--docs-banner-height,0px) z-40 h-14\",\n            className,\n          )}\n        >\n          <Primitive.Root\n            className={(s) =>\n              cn(\n                \"border-b backdrop-blur-lg transition-[box-shadow,background-color,border-radius]\",\n                open && \"max-lg:rounded-b-2xl max-lg:shadow-lg\",\n                (open || !isNavTransparent || s.open) && \"bg-background/80\",\n              )\n            }\n          >\n            {list}\n            <CollapsibleContent className=\"mx-auto max-w-[1400px] lg:hidden\">\n              <div className=\"flex flex-col p-4 pt-2 sm:flex-row sm:items-center sm:justify-end\">\n                <MobileNavigationMenuContext\n                  value={useMemo(() => ({ setOpen }), [])}\n                >\n                  {menuItems\n                    .filter((item) => !isSecondary(item))\n                    .map((item, i) => (\n                      <MobileNavigationMenuLinkItem\n                        key={i}\n                        item={item}\n                        className=\"sm:hidden\"\n                      />\n                    ))}\n                  <div className=\"-ms-1.5 flex flex-row items-center gap-2 max-sm:mt-2\">\n                    {menuItems.filter(isSecondary).map((item, i) => (\n                      <MobileNavigationMenuLinkItem\n                        key={i}\n                        item={item}\n                        className={cn(\n                          item.type === \"icon\" && \"-mx-1 first:ms-0\",\n                        )}\n                      />\n                    ))}\n                    <div role=\"separator\" className=\"flex-1\" />\n                    {languageSelect && (\n                      <LanguageSelect className=\"text-muted-foreground\">\n                        <Languages />\n                        <LanguageSelectText />\n                        <ChevronDown className=\"size-3\" />\n                      </LanguageSelect>\n                    )}\n                    {themeSwitchEnabled && (\n                      <ThemeSwitch {...themeSwitchProps} />\n                    )}\n                  </div>\n                </MobileNavigationMenuContext>\n              </div>\n            </CollapsibleContent>\n            <Primitive.Portal>\n              <Primitive.Positioner\n                side=\"bottom\"\n                anchor={listRef}\n                collisionPadding={{ top: 5, bottom: 5 }}\n                className=\"z-40 box-border h-(--positioner-height) w-(--anchor-width) max-w-(--available-width) duration-(--duration) ease-(--easing) before:absolute before:content-[''] data-instant:transition-none data-[side=bottom]:before:top-[-10px] data-[side=bottom]:before:right-0 data-[side=bottom]:before:left-0 data-[side=bottom]:before:h-2.5 data-[side=left]:before:top-0 data-[side=left]:before:right-[-10px] data-[side=left]:before:bottom-0 data-[side=left]:before:w-2.5 data-[side=right]:before:top-0 data-[side=right]:before:bottom-0 data-[side=right]:before:left-[-10px] data-[side=right]:before:w-2.5 data-[side=top]:before:right-0 data-[side=top]:before:bottom-[-10px] data-[side=top]:before:left-0 data-[side=top]:before:h-2.5\"\n                style={{\n                  [\"--duration\" as string]: \"0.35s\",\n                  [\"--easing\" as string]: \"cubic-bezier(0.22, 1, 0.36, 1)\",\n                }}\n              >\n                <Primitive.Popup className=\"relative h-(--popup-height) w-full rounded-xl border bg-popover/80 text-popover-foreground shadow-lg backdrop-blur-md transition-[opacity,width,height] duration-(--duration) ease-(--easing) data-ending-style:opacity-0 data-ending-style:duration-150 data-starting-style:opacity-0\">\n                  <Primitive.Viewport className=\"relative size-full overflow-hidden\" />\n                </Primitive.Popup>\n              </Primitive.Positioner>\n            </Primitive.Portal>\n          </Primitive.Root>\n        </header>\n      }\n    />\n  );\n}\n\nfunction isSecondary(item: LinkItemType): boolean {\n  if (\"secondary\" in item && item.secondary != null) return item.secondary;\n\n  return item.type === \"icon\";\n}\n\nfunction NavigationMenuLinkItem({\n  item,\n  className,\n  ...props\n}: {\n  item: LinkItemType;\n  className?: string;\n}) {\n  if (item.type === \"custom\") return item.children;\n\n  if (item.type === \"menu\") {\n    const children = item.items.map((child, j) => {\n      if (child.type === \"custom\") {\n        return <Fragment key={j}>{child.children}</Fragment>;\n      }\n\n      const {\n        banner = child.icon ? (\n          <div className=\"w-fit rounded-md border bg-muted p-1 [&_svg]:size-4\">\n            {child.icon}\n          </div>\n        ) : null,\n        ...rest\n      } = child.menu ?? {};\n\n      return (\n        <Primitive.Link\n          key={`${j}-${child.url}`}\n          render={\n            <Link\n              href={child.url}\n              external={child.external}\n              {...rest}\n              className={cn(\n                \"flex flex-col gap-2 rounded-lg border bg-card p-3 transition-colors hover:bg-accent/80 hover:text-accent-foreground\",\n                rest.className,\n              )}\n            >\n              {rest.children ?? (\n                <>\n                  {banner}\n                  <p className=\"text-base font-medium\">{child.text}</p>\n                  <p className=\"text-sm text-muted-foreground empty:hidden\">\n                    {child.description}\n                  </p>\n                </>\n              )}\n            </Link>\n          }\n        />\n      );\n    });\n\n    return (\n      <Primitive.Item className={cn(\"list-none\", className)} {...props}>\n        <Primitive.Trigger className={cn(navItemVariants(), \"rounded-md\")}>\n          {item.url ? (\n            <Link href={item.url} external={item.external}>\n              {item.text}\n            </Link>\n          ) : (\n            item.text\n          )}\n        </Primitive.Trigger>\n        <Primitive.Content\n          className={cn(\n            \"h-full w-(--anchor-width) max-w-(--available-width) p-3\",\n            \"transition-[opacity,transform,translate] duration-(--duration) ease-(--easing)\",\n            \"data-ending-style:opacity-0 data-starting-style:opacity-0\",\n            \"data-starting-style:data-[activation-direction=left]:-translate-x-1/2\",\n            \"data-starting-style:data-[activation-direction=right]:translate-x-1/2\",\n            \"data-ending-style:data-[activation-direction=left]:translate-x-1/2\",\n            \"data-ending-style:data-[activation-direction=right]:-translate-x-1/2\",\n            \"grid grid-cols-1 gap-2 md:grid-cols-2 lg:grid-cols-3\",\n          )}\n        >\n          {children}\n        </Primitive.Content>\n      </Primitive.Item>\n    );\n  }\n\n  return (\n    <Primitive.Item className={cn(\"list-none\", className)} {...props}>\n      <Primitive.Link\n        render={\n          <LinkItem\n            item={item}\n            aria-label={item.type === \"icon\" ? item.label : undefined}\n            className={cn(navItemVariants({ variant: item.type }))}\n          >\n            {item.type === \"icon\" ? item.icon : item.text}\n          </LinkItem>\n        }\n      />\n    </Primitive.Item>\n  );\n}\n\nfunction MobileNavigationMenuLinkItem({\n  item,\n  ...props\n}: {\n  item: LinkItemType;\n  className?: string;\n}) {\n  const { setOpen } = use(MobileNavigationMenuContext)!;\n\n  if (item.type === \"custom\")\n    return <div className={cn(\"grid\", props.className)}>{item.children}</div>;\n\n  if (item.type === \"menu\") {\n    return (\n      <div className={cn(\"mb-4 flex flex-col\", props.className)}>\n        <p className=\"mb-1 text-sm text-muted-foreground\">\n          {item.url ? (\n            <Link\n              href={item.url}\n              external={item.external}\n              onClick={() => setOpen(false)}\n            >\n              {item.icon}\n              {item.text}\n            </Link>\n          ) : (\n            <>\n              {item.icon}\n              {item.text}\n            </>\n          )}\n        </p>\n        {item.items.map((child, i) => (\n          <MobileNavigationMenuLinkItem key={i} item={child} />\n        ))}\n      </div>\n    );\n  }\n\n  return (\n    <LinkItem\n      item={item}\n      className={cn(\n        {\n          main: \"inline-flex items-center gap-2 py-1.5 transition-colors hover:text-popover-foreground/50 data-[active=true]:font-medium data-[active=true]:text-foreground [&_svg]:size-4\",\n          icon: buttonVariants({\n            size: \"icon\",\n            variant: \"ghost\",\n          }),\n          button: buttonVariants({\n            variant: \"secondary\",\n            className: \"gap-1.5 [&_svg]:size-4\",\n          }),\n        }[item.type ?? \"main\"],\n        props.className,\n      )}\n      aria-label={item.type === \"icon\" ? item.label : undefined}\n      onClick={() => setOpen(false)}\n    >\n      {item.icon}\n      {item.type === \"icon\" ? undefined : item.text}\n    </LinkItem>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/docs/layouts/home/slots/header.tsx"
    },
    {
      "path": "registry/base/docs/layouts/home/slots/container.tsx",
      "content": "// Adapted from Fumadocs UI (https://github.com/fuma-nama/fumadocs)\n// Copyright (c) 2023 Fuma, MIT License\n\"use client\";\n\nimport type { ComponentProps } from \"react\";\nimport { cn } from \"cn\";\n\nexport function Container(props: ComponentProps<\"main\">) {\n  return (\n    <main\n      id=\"nd-home-layout\"\n      {...props}\n      className={cn(\"flex flex-1 flex-col\", props.className)}\n    />\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/docs/layouts/home/slots/container.tsx"
    }
  ],
  "type": "registry:component"
}