{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "form",
  "type": "registry:ui",
  "title": "Form",
  "description": "@interlace/ui — form primitive (shadcn-compatible).",
  "dependencies": [
    "@base-ui/react"
  ],
  "registryDependencies": [
    "theme"
  ],
  "files": [
    {
      "path": "registry/interlace-ui/form.tsx",
      "target": "components/ui/form.tsx",
      "type": "registry:ui",
      "content": "/**\n * @interlace/ui — Form\n *\n * A composition primitive over `@base-ui/react/field`. The form ecosystem owns\n * a long tail of subtle a11y wiring — label/control association via generated\n * ids, `aria-describedby` for description + error, `aria-invalid` flipping on\n * validity, focus management when validation fails. Base UI's `Field.*` parts\n * already encode that contract; this file is the DS skin (R13: ecosystem\n * first — wrap, never re-implement).\n *\n * Two layers ship here:\n *\n *   1. `Form` — a server-component `<form>` wrapper with a server-safe\n *      `asChild` seam (R10). Forwards `action` / `method` / `onSubmit` /\n *      `className` / `...rest` so it composes with both React Server Action\n *      forms (`action={serverAction}`) and classic POST forms.\n *   2. `Field` / `FieldLabel` / `FieldControl` / `FieldDescription` /\n *      `FieldError` / `FieldValidity` — DS-styled re-exports of the Base UI\n *      `Field.*` parts. `Field` (= `Field.Root`) and `FieldControl` are pure\n *      passthrough (Base UI owns their structure + styling hooks); the other\n *      three add token-only Tailwind classes for the canonical label /\n *      description / error type ramps.\n *\n * ## Anatomy\n *\n *   <Form action={serverAction}>\n *     <Field name=\"email\">\n *       <FieldLabel>Email</FieldLabel>\n *       <FieldControl render={<Input type=\"email\" required />} />\n *       <FieldDescription>We'll never share it.</FieldDescription>\n *       <FieldError />\n *     </Field>\n *     <Button type=\"submit\">Sign in</Button>\n *   </Form>\n *\n * ## MIN_VIEWPORT — 320\n *\n * Forms are the LAST surface that may degrade on a narrow screen: a sign-in,\n * search, or contact form must work on a 320 CSS-px iPhone SE. The\n * sub-primitives (Input / Textarea / FieldLabel / FieldError) inherit the\n * 320 floor; this root mirrors it so audits keyed off `data-min-viewport`\n * see the same value at the form scope.\n *\n * | Rule | Concept                          | Where in this file                                                 |\n * | ---- | -------------------------------- | ------------------------------------------------------------------ |\n * | R4   | Extends native el                | `Form` extends `React.ComponentProps<'form'>`                      |\n * | R6   | data-slot on every part          | `data-slot=\"form\"` + Base UI parts emit their own slot attrs        |\n * | R7   | className merged + ...rest       | `cn(className)` + `{...props}` on every part                       |\n * | R9   | Native onSubmit stays native     | `onSubmit` / `action` / `method` pass through `{...props}`         |\n * | R10  | Composition seam                 | `Form` accepts `asChild` via a server-safe React.cloneElement slot |\n * | R13  | Ecosystem first                  | Base UI `Field.*` owns id wiring + ARIA + validity state           |\n * | R14  | Declares min viewport            | `data-min-viewport={String(MIN_VIEWPORT)}` + exported const        |\n * | R18  | Tailwind only                    | Zero inline `style`; classes only                                  |\n * | R19  | Tokens only                      | `text-ui` / `text-ui-sm` / `text-muted-foreground` / `text-destructive` |\n * | R20  | AA contrast                      | description / error tokens are AA-cleared in foundation             |\n * | R25  | Server component                 | No hooks, no `'use client'`; Base UI `Field.*` is the client part  |\n * | R26  | A11y from native el + headless   | Native `<form>` + Base UI `Field.*` ARIA wiring                    |\n */\n\nimport * as React from 'react';\nimport { Field as BaseField } from '@base-ui/react/field';\n\nimport { cn } from '@/lib/utils';\n\n/** Smallest viable viewport (CSS px) for this primitive. */\nexport const MIN_VIEWPORT = 320 as const;\n\n// ─────────────────────────────────────────────────────────────────\n// Form (root <form>) — server component with a server-safe asChild seam.\n// ─────────────────────────────────────────────────────────────────\n\ninterface FormProps extends React.ComponentProps<'form'> {\n  /**\n   * Render as the passed child element (e.g. a framework-specific `<Form>`\n   * from `react-router` / `remix-run`). Server-safe slot: when set and a\n   * single React element child is provided, we `cloneElement` and merge our\n   * `data-slot` / `data-min-viewport` / `className` onto it. Not Base UI\n   * `useRender` (which would flip this tree to a client boundary).\n   */\n  asChild?: boolean;\n}\n\n/** Form root. Server component (no hooks, no `'use client'`). */\nconst Form = React.forwardRef<HTMLFormElement, FormProps>(function Form(\n  { asChild, className, children, ...props },\n  ref,\n) {\n  if (asChild && React.isValidElement(children)) {\n    const child = children as React.ReactElement<{ className?: string }>;\n    return React.cloneElement(child, {\n      ...props,\n      ...child.props,\n      ref,\n      'data-slot': 'form',\n      'data-min-viewport': String(MIN_VIEWPORT),\n      className: cn(className, child.props.className),\n    } as React.HTMLAttributes<HTMLElement>);\n  }\n\n  return (\n    <form\n      ref={ref}\n      data-slot=\"form\"\n      data-min-viewport={String(MIN_VIEWPORT)}\n      className={cn(className)}\n      {...props}\n    >\n      {children}\n    </form>\n  );\n});\nForm.displayName = 'Form';\n\n// ─────────────────────────────────────────────────────────────────\n// Field (= Base UI Field.Root) — passthrough; Base UI owns structure.\n// ─────────────────────────────────────────────────────────────────\n\nconst Field = React.forwardRef<\n  HTMLDivElement,\n  React.ComponentProps<typeof BaseField.Root>\n>(function Field({ className, ...props }, ref) {\n  return (\n    <BaseField.Root\n      ref={ref}\n      data-slot=\"field\"\n      className={cn(className)}\n      {...props}\n    />\n  );\n});\nField.displayName = 'Field';\n\n// ─────────────────────────────────────────────────────────────────\n// FieldLabel — DS-styled label using --text-ui.\n// ─────────────────────────────────────────────────────────────────\n\nconst FieldLabel = React.forwardRef<\n  HTMLLabelElement,\n  React.ComponentProps<typeof BaseField.Label>\n>(function FieldLabel({ className, ...props }, ref) {\n  return (\n    <BaseField.Label\n      ref={ref}\n      data-slot=\"field-label\"\n      className={cn(\n        'block font-body text-ui font-medium leading-none',\n        className,\n      )}\n      {...props}\n    />\n  );\n});\nFieldLabel.displayName = 'FieldLabel';\n\n// ─────────────────────────────────────────────────────────────────\n// FieldControl — passthrough; the rendered input owns its own visual.\n// ─────────────────────────────────────────────────────────────────\n\nconst FieldControl = React.forwardRef<\n  HTMLInputElement,\n  React.ComponentProps<typeof BaseField.Control>\n>(function FieldControl({ className, ...props }, ref) {\n  return (\n    <BaseField.Control\n      ref={ref}\n      data-slot=\"field-control\"\n      className={cn(className)}\n      {...props}\n    />\n  );\n});\nFieldControl.displayName = 'FieldControl';\n\n// ─────────────────────────────────────────────────────────────────\n// FieldDescription — DS-styled helper text (--text-ui-sm + muted).\n// ─────────────────────────────────────────────────────────────────\n\nconst FieldDescription = React.forwardRef<\n  HTMLParagraphElement,\n  React.ComponentProps<typeof BaseField.Description>\n>(function FieldDescription({ className, ...props }, ref) {\n  return (\n    <BaseField.Description\n      ref={ref}\n      data-slot=\"field-description\"\n      className={cn('font-body text-ui-sm text-muted-foreground', className)}\n      {...props}\n    />\n  );\n});\nFieldDescription.displayName = 'FieldDescription';\n\n// ─────────────────────────────────────────────────────────────────\n// FieldError — DS-styled error text (--text-ui-sm + destructive).\n// ─────────────────────────────────────────────────────────────────\n\nconst FieldError = React.forwardRef<\n  HTMLParagraphElement,\n  React.ComponentProps<typeof BaseField.Error>\n>(function FieldError({ className, ...props }, ref) {\n  return (\n    <BaseField.Error\n      ref={ref}\n      data-slot=\"field-error\"\n      className={cn('font-body text-ui-sm text-destructive', className)}\n      {...props}\n    />\n  );\n});\nFieldError.displayName = 'FieldError';\n\n// ─────────────────────────────────────────────────────────────────\n// FieldValidity — render-prop helper from Base UI; pure re-export.\n// ─────────────────────────────────────────────────────────────────\n\nconst FieldValidity = BaseField.Validity;\n\nexport {\n  Form,\n  Field,\n  FieldLabel,\n  FieldControl,\n  FieldDescription,\n  FieldError,\n  FieldValidity,\n};\nexport type { FormProps };\n"
    }
  ]
}
