{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "field",
  "type": "registry:ui",
  "title": "Field",
  "description": "Wraps one form control with a label, help text and an error message, and wires up the accessibility attributes.",
  "dependencies": [],
  "registryDependencies": [
    "https://blank.vageshwar.dev/r/theme.json",
    "https://blank.vageshwar.dev/r/utils.json"
  ],
  "files": [
    {
      "path": "ui/field.tsx",
      "type": "registry:ui",
      "content": "// BlankUI: field. Managed by BlankUI. Read field.md before editing. Controls read their wiring from this context.\nimport { createContext, useContext, useId, useLayoutEffect, useState, type ReactNode } from \"react\"\nimport { cn } from \"@/lib/utils\"\n\ninterface FieldContextValue {\n  controlId: string\n  /** Lets a control that sets its own `id` point the label at it. */\n  setControlId: (id: string | undefined) => void\n  descriptionId: string | undefined\n  errorId: string | undefined\n  invalid: boolean\n  required: boolean\n}\n\nconst FieldContext = createContext<FieldContextValue | null>(null)\n\nexport interface FieldProps {\n  /** Visible label for the control. Always required, even if it seems obvious. */\n  label: ReactNode\n  /** Help text shown under the label. */\n  description?: ReactNode\n  /** Error message. When set, the control is marked invalid and the message is announced. */\n  error?: ReactNode\n  /** Marks the control as required and shows an indicator next to the label. */\n  required?: boolean\n  /** `vertical` puts the label above the control. Use `horizontal` for Checkbox. Defaults to `vertical`. */\n  orientation?: \"vertical\" | \"horizontal\"\n  /** Id for the control. Generated when not set. */\n  id?: string\n  className?: string\n  /** Exactly one control: Input, Textarea, Select or Checkbox. */\n  children: ReactNode\n}\n\n/**\n * Wraps one form control with a label, help text and an error message, and wires them up\n * (id, htmlFor, aria-describedby, aria-invalid, required). Works with any form library.\n *\n * @example\n * <Field label=\"Email\" description=\"We never share it.\" error={errors.email?.message} required>\n *   <Input type=\"email\" {...register(\"email\")} />\n * </Field>\n */\nexport function Field({\n  label,\n  description,\n  error,\n  required = false,\n  orientation = \"vertical\",\n  id,\n  className,\n  children,\n}: FieldProps) {\n  const autoId = useId()\n  const [childId, setControlId] = useState<string | undefined>(undefined)\n  const controlId = childId ?? id ?? `field${autoId.replace(/:/g, \"\")}`\n  const descriptionId = description ? `${controlId}-description` : undefined\n  const errorId = error ? `${controlId}-error` : undefined\n  const invalid = Boolean(error)\n\n  const labelNode = (\n    <label htmlFor={controlId} className=\"text-sm leading-none font-medium text-foreground\">\n      {label}\n      {required && (\n        <span className=\"ml-0.5 text-destructive\" aria-hidden=\"true\">\n          *\n        </span>\n      )}\n    </label>\n  )\n  const descriptionNode = description && (\n    <p id={descriptionId} className=\"text-sm text-muted-foreground\">\n      {description}\n    </p>\n  )\n  const errorNode = error && (\n    <p id={errorId} className=\"text-sm font-medium text-destructive\">\n      {error}\n    </p>\n  )\n\n  return (\n    <FieldContext value={{ controlId, setControlId, descriptionId, errorId, invalid, required }}>\n      <div\n        data-slot=\"field\"\n        data-invalid={invalid || undefined}\n        className={cn(\"flex flex-col gap-2\", className)}\n      >\n        {orientation === \"vertical\" ? (\n          <>\n            {labelNode}\n            {children}\n            {descriptionNode}\n          </>\n        ) : (\n          <div className=\"flex items-start gap-3\">\n            {children}\n            <div className=\"flex flex-col gap-1.5\">\n              {labelNode}\n              {descriptionNode}\n            </div>\n          </div>\n        )}\n        {errorNode}\n      </div>\n    </FieldContext>\n  )\n}\n\ninterface ControlA11yProps {\n  id?: string\n  required?: boolean\n  \"aria-describedby\"?: string\n  \"aria-invalid\"?: boolean | \"true\" | \"false\" | \"grammar\" | \"spelling\"\n}\n\n/**\n * Merge Field wiring into a control's props. Props set directly on the control win.\n * Used by Input, Textarea, Select and Checkbox. Use it when you build a new control.\n */\nexport function useFieldControl<P extends ControlA11yProps>(props: P): P {\n  const field = useContext(FieldContext)\n  const ownId = props.id\n  const setControlId = field?.setControlId\n  useLayoutEffect(() => {\n    if (!setControlId || !ownId) return\n    setControlId(ownId)\n    return () => setControlId(undefined)\n  }, [setControlId, ownId])\n  if (!field) return props\n  const describedBy = [props[\"aria-describedby\"], field.descriptionId, field.errorId]\n    .filter(Boolean)\n    .join(\" \")\n  return {\n    ...props,\n    id: props.id ?? field.controlId,\n    required: props.required ?? (field.required || undefined),\n    \"aria-describedby\": describedBy || undefined,\n    \"aria-invalid\": props[\"aria-invalid\"] ?? (field.invalid || undefined),\n  }\n}\n"
    },
    {
      "path": "ui/field.md",
      "type": "registry:ui",
      "content": "---\nname: field\ntitle: Field\ndescription: Wraps one form control with a label, help text and an error message, and wires up the accessibility attributes.\ncategory: form\nwhenToUse:\n  - Every form control that needs a visible label. That is almost all of them.\n  - Showing validation errors from any form library (react-hook-form, TanStack Form, server actions, plain state).\nwhenNotToUse:\n  - when: A search box in a toolbar with no visible label.\n    use: an Input with aria-label\nrelated: [input, textarea, select, checkbox, layout]\nrequiredParts: [Field, useFieldControl]\nantiPatterns:\n  - avoid: '<label htmlFor=\"email\">Email</label><Input id=\"email\" />'\n    instead: '<Field label=\"Email\"><Input /></Field>'\n  - avoid: '{error && <p className=\"text-red-500\">{error}</p>} under an input'\n    instead: '<Field label=\"Email\" error={error}>'\n  - avoid: \"Setting aria-invalid or aria-describedby by hand\"\n    instead: \"Field sets them from the error and description props\"\n  - avoid: \"Placeholder text instead of a label\"\n    instead: \"A label prop, plus a placeholder only for an example value\"\n---\n\n# Field\n\nField renders the label, help text and error for one control, and passes `id`, `aria-describedby`, `aria-invalid` and `required` to the control inside it. Input, Textarea, Select and Checkbox all read this automatically.\n\n## Import\n\n```tsx\nimport { Field } from \"@/components/ui/field\"\n```\n\n## Example\n\n```tsx\n<form onSubmit={handleSubmit}>\n  <Stack gap={4}>\n    <Field label=\"Email\" description=\"We never share your email.\" error={errors.email} required>\n      <Input type=\"email\" name=\"email\" />\n    </Field>\n    <Field label=\"Plan\">\n      <Select name=\"plan\" defaultValue=\"pro\">\n        <SelectTrigger>\n          <SelectValue />\n        </SelectTrigger>\n        <SelectContent>\n          <SelectItem value=\"free\">Free</SelectItem>\n          <SelectItem value=\"pro\">Pro</SelectItem>\n        </SelectContent>\n      </Select>\n    </Field>\n    <Field label=\"I agree to the terms\" orientation=\"horizontal\">\n      <Checkbox name=\"terms\" />\n    </Field>\n    <Button type=\"submit\">Create account</Button>\n  </Stack>\n</form>\n```\n\n## With react-hook-form\n\n```tsx\nconst { register, formState: { errors } } = useForm<FormValues>()\n\n<Field label=\"Name\" error={errors.name?.message} required>\n  <Input {...register(\"name\", { required: \"Name is required\" })} />\n</Field>\n```\n\n## Props\n\n- `label` (required): visible label text.\n- `description`: help text under the label.\n- `error`: error message. Marks the control invalid.\n- `required`: marks the control required and shows `*`.\n- `orientation`: `vertical` (default) or `horizontal`. Use `horizontal` for Checkbox.\n- `id`: control id. Generated when not set.\n\n## Building a new control\n\nCall `useFieldControl(props)` in your control and spread the result onto the element. It adds the Field wiring and keeps any props you set directly.\n"
    }
  ],
  "docs": "Read components/ui/field.md before using Field. Docs: https://blank.vageshwar.dev/docs/field",
  "meta": {
    "category": "form",
    "requiredParts": [
      "Field",
      "useFieldControl"
    ],
    "related": [
      "input",
      "textarea",
      "select",
      "checkbox",
      "layout"
    ]
  }
}
