{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "modal", "title": "Modal", "description": "A dialog overlay for focused interactions like confirmations, forms, and detail views.", "dependencies": [ "@base-ui/react", "class-variance-authority", "lucide-react" ], "registryDependencies": [ "button" ], "files": [ { "path": "packages/ui/src/modal.tsx", "content": "\"use client\";\n\nimport { Dialog as DialogPrimitive } from \"@base-ui/react/dialog\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { X as XIcon } from \"lucide-react\";\nimport * as React from \"react\";\nimport { Button } from \"./button\";\nimport { cn } from \"./utils\";\n\n// =============================================================================\n// Modal (Root)\n// =============================================================================\nfunction Modal({ ...props }: DialogPrimitive.Root.Props) {\n return ;\n}\n\n// =============================================================================\n// ModalTrigger\n// =============================================================================\nexport interface ModalTriggerProps extends DialogPrimitive.Trigger.Props {}\n\nfunction ModalTrigger({ className, ...props }: ModalTriggerProps) {\n return (\n \n );\n}\n\n// =============================================================================\n// ModalPortal\n// =============================================================================\nexport interface ModalPortalProps extends DialogPrimitive.Portal.Props {}\n\nfunction ModalPortal({ ...props }: ModalPortalProps) {\n return ;\n}\n\n// =============================================================================\n// ModalClose\n// =============================================================================\nexport interface ModalCloseProps extends DialogPrimitive.Close.Props {}\n\nfunction ModalClose({ className, ...props }: ModalCloseProps) {\n return (\n \n );\n}\n\n// =============================================================================\n// ModalOverlay\n// =============================================================================\nexport interface ModalOverlayProps extends DialogPrimitive.Backdrop.Props {}\n\nfunction ModalOverlay({ className, ...props }: ModalOverlayProps) {\n return (\n \n );\n}\n\n// =============================================================================\n// ModalContent Variants\n// =============================================================================\nconst modalContentVariants = cva(\n \"fixed top-[50%] left-[50%] z-[var(--z-modal)] grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg bg-background p-6 shadow-lg outline-none\",\n {\n variants: {\n size: {\n sm: \"sm:max-w-sm\",\n default: \"sm:max-w-lg\",\n lg: \"sm:max-w-2xl\",\n xl: \"sm:max-w-4xl\",\n full: \"sm:max-w-[calc(100vw-2rem)]\",\n },\n },\n defaultVariants: {\n size: \"default\",\n },\n }\n);\n\ntype ModalContentVariantsProps = VariantProps;\n\n// =============================================================================\n// ModalContent\n// =============================================================================\nexport interface ModalContentProps\n extends DialogPrimitive.Popup.Props,\n ModalContentVariantsProps {\n /**\n * Where scrolling happens when content exceeds the viewport.\n * - `\"outside\"` — the entire viewport scrolls, modal grows to fit (default)\n * - `\"inside\"` — content scrolls within the modal\n * @default \"outside\"\n */\n scrollBehavior?: \"inside\" | \"outside\";\n /**\n * Whether to show the close button in the top-right corner.\n * @default true\n */\n showCloseButton?: boolean;\n}\n\nfunction ModalContent({\n className,\n children,\n size = \"default\",\n showCloseButton = true,\n scrollBehavior = \"outside\",\n ...props\n}: ModalContentProps) {\n // When scrolling outside, focus the popup container itself on open\n // instead of the first tabbable element. Without this, Base UI focuses\n // the first button (often at the bottom of long content), and the browser\n // auto-scrolls the overflow wrapper to reveal it — jumping to the bottom.\n const popupRef = React.useRef(null);\n\n const closeButton = showCloseButton && (\n }\n >\n \n Close\n \n );\n\n const popup = (\n \n {children}\n {closeButton}\n \n );\n\n if (scrollBehavior === \"outside\") {\n return (\n \n \n \n
\n {popup}\n
\n \n
\n );\n }\n\n return (\n \n \n {popup}\n \n );\n}\n\n// =============================================================================\n// ModalHeader\n// =============================================================================\nexport interface ModalHeaderProps extends React.ComponentProps<\"div\"> {}\n\nfunction ModalHeader({ className, ...props }: ModalHeaderProps) {\n return (\n \n );\n}\n\n// =============================================================================\n// ModalFooter\n// =============================================================================\nexport interface ModalFooterProps extends React.ComponentProps<\"div\"> {\n /**\n * Whether to show a \"Close\" outline button at the end of the footer.\n * @default false\n */\n showCloseButton?: boolean;\n}\n\nfunction ModalFooter({\n className,\n showCloseButton = false,\n children,\n ...props\n}: ModalFooterProps) {\n return (\n \n {children}\n {showCloseButton && (\n }>\n Close\n \n )}\n \n );\n}\n\n// =============================================================================\n// ModalTitle\n// =============================================================================\nexport interface ModalTitleProps extends DialogPrimitive.Title.Props {}\n\nfunction ModalTitle({ className, ...props }: ModalTitleProps) {\n return (\n \n );\n}\n\n// =============================================================================\n// ModalDescription\n// =============================================================================\nexport interface ModalDescriptionProps\n extends DialogPrimitive.Description.Props {}\n\nfunction ModalDescription({ className, ...props }: ModalDescriptionProps) {\n return (\n \n );\n}\n\nexport type { ModalContentVariantsProps };\n// =============================================================================\n// Exports\n// =============================================================================\nexport {\n Modal,\n ModalClose,\n ModalContent,\n ModalDescription,\n ModalFooter,\n ModalHeader,\n ModalOverlay,\n ModalPortal,\n ModalTitle,\n ModalTrigger,\n modalContentVariants,\n};\n", "type": "registry:ui" } ], "type": "registry:ui" }