{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"frame","type":"registry:ui","title":"Frame","description":"","dependencies":["class-variance-authority"],"registryDependencies":[],"files":[{"path":"frame.tsx","type":"registry:ui","content":"import { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\n/**\n * CSS variable architecture for FramePanel theming:\n *\n * The Frame parent sets --frame-panel-bg and --frame-panel-border-color.\n * FramePanel consumes them directly via bg-(--frame-panel-bg) and\n * border-(--frame-panel-border-color). This means:\n *\n * - variant=\"inverse\" overrides those vars on Frame → all panels pick it up\n * - adds a direct utility on the element\n * which wins over bg-(--frame-panel-bg) by Tailwind source order — no\n * :not() or !important needed\n */\nconst frameVariants = cva(\n [\n \"relative flex flex-col bg-muted/50 gap-(--frame-gap) px-(--frame-px) py-(--frame-py) rounded-(--frame-radius)\",\n \"(--radius-xl)] [--frame-radius:var(--radius-xl)]\",\n \"(--radius-none)] (--radius-2xl)] (--radius-lg)] (--radius-none)]\",\n \"[--frame-gap:--spacing(0.75)] [--frame-px:--spacing(0.75)] [--frame-py:--spacing(0.75)] [--frame-panel-header-gap:0rem] [--frame-panel-footer-gap:--spacing(1)]\",\n \"[--frame-panel-px-adjust:0px] [--frame-panel-py-adjust:0px] [--frame-panel-header-px-adjust:0px] [--frame-panel-header-py-adjust:0px] [--frame-panel-footer-px-adjust:0px] [--frame-panel-footer-py-adjust:0px]\",\n \"[--frame-panel-px:calc(var(--frame-panel-px-base)_+_var(--frame-panel-px-adjust))] [--frame-panel-py:calc(var(--frame-panel-py-base)_+_var(--frame-panel-py-adjust))] [--frame-panel-header-px:calc(var(--frame-panel-header-px-base)_+_var(--frame-panel-header-px-adjust))] [--frame-panel-header-py:calc(var(--frame-panel-header-py-base)_+_var(--frame-panel-header-py-adjust))] [--frame-panel-footer-px:calc(var(--frame-panel-footer-px-base)_+_var(--frame-panel-footer-px-adjust))] [--frame-panel-footer-py:calc(var(--frame-panel-footer-py-base)_+_var(--frame-panel-footer-py-adjust))]\",\n \"(1)] (1)] (1.25)] (1.5)] (1.5)] (0.5)] (1)] (1)]\",\n // Default panel token values — overridden per-variant below\n \"[--frame-panel-bg:var(--color-card)] [--frame-panel-border-color:var(--color-border)] [--frame-border-color:var(--color-border)]\",\n // Concentric inner radius: the panel corner nests smoothly inside the frame\n // corner instead of matching it. The panel sits inset from the frame's outer\n // edge by the frame's 1px border + --frame-px padding, so its radius is\n // reduced by that same gap (radius − gap keeps the two arcs parallel). This\n // base value assumes the bordered default/inverse frame; `ghost` drops the\n // 1px border term and `dense` pins it back to the frame radius (its panels\n // are pulled flush to the edge).\n \"[--frame-panel-radius:calc(var(--frame-radius)_-_var(--frame-px)_-_1px)]\",\n ],\n {\n variants: {\n variant: {\n default: \"border border-[var(--frame-border-color)] bg-clip-padding\",\n inverse:\n \"[--frame-panel-bg:color-mix(in_oklch,var(--color-muted)_40%,transparent)] border border-[var(--frame-border-color)] bg-background bg-clip-padding\",\n // No frame border, so the panel is inset by --frame-px padding only.\n ghost: \"[--frame-panel-radius:calc(var(--frame-radius)_-_var(--frame-px))]\",\n },\n // Header/footer vertical rhythm is tighter than the panel body's, and\n // the gap widens as the frame grows: the bars read as chrome rather than\n // as another content block. py ladder is 0.5 / 1.5 / 2 / 2.5 against a\n // body py of 2 / 3.5 / 4 / 5. These vars are style-agnostic - no\n // style-*.css overrides them - so this single ladder drives all shadcn\n // styles. `px` is deliberately left level with the body so header,\n // content and footer stay left-aligned. `xs` holds at 0.5 (2px): it is\n // the practical floor, since anything lower stops reading as padding.\n spacing: {\n xs: \"[--frame-panel-px-base:--spacing(2)] [--frame-panel-py-base:--spacing(2)] [--frame-panel-header-px-base:--spacing(2)] [--frame-panel-header-py-base:--spacing(0.5)] [--frame-panel-footer-px-base:--spacing(2)] [--frame-panel-footer-py-base:--spacing(0.5)]\",\n sm: \"[--frame-panel-px-base:--spacing(3)] [--frame-panel-py-base:--spacing(3.5)] [--frame-panel-header-px-base:--spacing(3)] [--frame-panel-header-py-base:--spacing(1.5)] [--frame-panel-footer-px-base:--spacing(3)] [--frame-panel-footer-py-base:--spacing(1.5)]\",\n default:\n \"[--frame-panel-px-base:--spacing(4)] [--frame-panel-py-base:--spacing(4)] [--frame-panel-header-px-base:--spacing(4)] [--frame-panel-header-py-base:--spacing(2)] [--frame-panel-footer-px-base:--spacing(4)] [--frame-panel-footer-py-base:--spacing(2)]\",\n lg: \"[--frame-panel-px-base:--spacing(5)] [--frame-panel-py-base:--spacing(5)] [--frame-panel-header-px-base:--spacing(5)] [--frame-panel-header-py-base:--spacing(2.5)] [--frame-panel-footer-px-base:--spacing(5)] [--frame-panel-footer-py-base:--spacing(2.5)]\",\n },\n stacked: {\n true: [\n \"gap-0 *:has-[+[data-slot=frame-panel]]:rounded-b-none\",\n \"*:has-[+[data-slot=frame-panel]]:before:hidden\",\n \"*:[[data-slot=frame-panel]+[data-slot=frame-panel]]:rounded-t-none\",\n \"*:[[data-slot=frame-panel]+[data-slot=frame-panel]]:border-t-0\",\n ],\n false: [\n \"data-[spacing=sm]:*:[[data-slot=frame-panel]+[data-slot=frame-panel]]:mt-0.5\",\n \"data-[spacing=default]:*:[[data-slot=frame-panel]+[data-slot=frame-panel]]:mt-1\",\n \"data-[spacing=lg]:*:[[data-slot=frame-panel]+[data-slot=frame-panel]]:mt-2\",\n ],\n },\n dense: {\n // Positional rules must stay as parent selectors — cannot be expressed via CSS vars.\n // Padding is 0 and panels are pulled flush to the frame edge (-mx-px), so\n // their corners align with the frame radius rather than nesting inside it.\n true: \"p-0 gap-0 border-[var(--frame-border-color)] [--frame-panel-radius:var(--frame-radius)] [&_[data-slot=frame-panel]]:-mx-px [&_[data-slot=frame-panel]]:before:hidden [&_[data-slot=frame-panel]:last-child]:-mb-px [&:not(:has([data-slot=frame-panel-header]))_[data-slot=frame-panel]:is(:first-child)]:-mt-px\",\n false: \"\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n spacing: \"default\",\n stacked: false,\n dense: false,\n },\n }\n)\n\nfunction Frame({\n className,\n variant,\n spacing,\n stacked,\n dense,\n ...props\n}: React.ComponentProps<\"div\"> & VariantProps) {\n return (\n \n )\n}\n\nfunction FramePanel({\n className,\n fit,\n ...props\n}: React.ComponentProps<\"div\"> & { fit?: boolean }) {\n return (\n \n )\n}\n\nfunction FrameHeader({ className, ...props }: React.ComponentProps<\"header\">) {\n return (\n \n )\n}\n\nfunction FrameTitle({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n \n )\n}\n\nfunction FrameDescription({\n className,\n ...props\n}: React.ComponentProps<\"div\">) {\n return (\n \n )\n}\n\nfunction FrameFooter({ className, ...props }: React.ComponentProps<\"footer\">) {\n return (\n \n )\n}\n\nexport {\n Frame,\n FramePanel,\n FrameHeader,\n FrameTitle,\n FrameDescription,\n FrameFooter,\n frameVariants,\n}","target":"components/neui/frame.tsx"}]}