{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "input",
  "type": "registry:ui",
  "title": "Input",
  "description": "A roomy, slightly rounded text field on the 32, 44 and 56 scale, with a label above and a hint or an error below. Focus and error turn the edge on the instant tier, and the box never moves.",
  "registryDependencies": [
    "https://littmd.vercel.app/r/litt.json",
    "https://littmd.vercel.app/r/motion.json"
  ],
  "files": [
    {
      "path": "registry/litt/ui/input.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n// @litt/input 2026.11.09. What changed: https://littmd.vercel.app/changelog.md\n\n/**\n * Input: the text field (components.md, \"Input\"). Three parts, one look.\n *\n * - Input is the bare control, for inline forms where the label lives elsewhere.\n * - TextField is Input with a label above and a hint or error below, all wired up.\n * - Textarea is the same field for longer text (a spec extension: same edge, focus,\n *   error and label as Input, but taller).\n *\n * - Sizes sit on the shared control scale (R9): sm 32, md 44, lg 56, with the control\n *   radius. Text is 13, 15 and 16px, and 16px on coarse pointers so iOS never zooms.\n *   On a touch screen the sm input grows to 44px, like the small targets in Header and List.\n * - Material (R3): the cut, the shallowest sunken step, because a field is what you type into. Its floor\n *   (--litt-cut: white in light, a step below the surfaces in dark), the control line inside it (--litt-line-control, at\n *   3:1, so a field reads as a field) and the top wall's shade on the floor (--litt-cut-shade). Never a grey floor,\n *   which reads as disabled. The line is its own layer, so focus and an error swap only its color: focus turns\n *   it --litt-focus and adds --litt-focus-ring (:focus-visible only), an error turns it --litt-danger and keeps the ring on\n *   focus. Disabled lies flat on surface-2 with the plain --litt-line hairline and no shade: nothing to type.\n *   Under Device (data-material=\"device\") the field is LCD glass in the mono, through the --litt-field tokens.\n * - Bodies (proposals/worlds.md): the contract below (the input, its label, aria-invalid, aria-describedby) resolves once\n *   into InputBodyProps, TextareaBodyProps and FieldBodyProps, and the body the material picks (byWorld) draws them.\n *   Press is the cut and its plate, here. Flat is the grey pill (bodies/input.flat.tsx). Device is LCD glass set in a bezel\n *   well, the label engraved on the body above it, the hint and the error dim LCD lines, an error with a lamp in\n *   --litt-danger (bodies/input.device.tsx).\n * - Press, the instrument: the field is a cut sunk into the surface it sits on, which is the plate already. Its wall is\n *   the shade (--litt-slot-shade), its floor deeper (--litt-field-shade stacked on it) and its lower wall catches the light\n *   (the slot-shade's lit line), never a drawn outline. The label sits on the surface above, the hint or error under.\n *   A host's className (a width) lands on the cut.\n * - Flat, the page: a grey pill with nothing standing and no frame; the label in ink, the hint plain grey words, an\n *   error words in --litt-danger with the pill's fill tinting (bodies/input.flat.tsx).\n * - Typed text at the text weight and the label at the title weight (R8). The edge fades on the\n *   instant tier, the box never moves.\n * - No hover state: the spec and the specimen have none, and a text field that shifts\n *   its fill under the cursor reads as pressed.\n */\nimport { useId, type InputHTMLAttributes, type ReactNode, type Ref, type TextareaHTMLAttributes } from \"react\";\nimport { byWorld, useMaterial } from \"../../lib/litt/motion\";\nimport { FieldDeviceBody, InputDeviceBody, TextareaDeviceBody } from \"./bodies/input.device\";\nimport { FieldFlatBody, InputFlatBody, TextareaFlatBody } from \"./bodies/input.flat\";\n\nexport type InputSize = \"sm\" | \"md\" | \"lg\";\n\n/* The cut every field shares: floor, line, shade, focus, error, disabled. */\nconst field =\n  \"w-full min-w-0 rounded-litt-control bg-litt-field font-normal text-litt-field-ink placeholder:text-litt-field-ink-subtle \" +\n  \"pointer-coarse:text-(length:--litt-control-text-lg) \" +\n  \"shadow-[var(--litt-slot-shade),var(--litt-field-shade)] \" +\n  \"transition-[box-shadow] duration-(--litt-dur-instant) ease-litt \" +\n  \"focus-visible:outline-none focus-visible:shadow-[var(--litt-slot-shade),var(--litt-field-shade),var(--litt-focus-ring)] \" +\n  \"aria-invalid:shadow-[inset_0_0_0_1px_var(--litt-danger),var(--litt-slot-shade),var(--litt-field-shade)] \" +\n  \"aria-invalid:focus-visible:shadow-[inset_0_0_0_1px_var(--litt-danger),var(--litt-slot-shade),var(--litt-field-shade),var(--litt-focus-ring)] \" +\n  \"disabled:cursor-not-allowed disabled:bg-litt-surface-2 disabled:text-litt-fg-disabled disabled:placeholder:text-litt-fg-disabled \" +\n  \"disabled:shadow-[inset_0_0_0_1px_var(--litt-line)] \" +\n  /* Forced colors (Windows high contrast) drops box-shadow, so the edge becomes a real border and focus gets an outline. */\n  \"forced-colors:border forced-colors:focus-visible:outline-2 forced-colors:focus-visible:outline-solid forced-colors:focus-visible:outline-offset-2\";\n\nconst inputSizes: Record<InputSize, string> = {\n  sm: \"h-litt-control-sm px-litt-control-pad-sm text-litt-small pointer-coarse:h-litt-control-md\",\n  md: \"h-litt-control-md px-litt-control-pad-md text-litt-body\",\n  lg: \"h-litt-control-lg px-litt-control-pad-lg text-(length:--litt-control-text-lg)\",\n};\n\n/* A textarea has no fixed height, so its size sets the air above and below the text instead. */\nconst textareaSizes: Record<InputSize, string> = {\n  sm: \"px-litt-control-pad-sm py-2 text-litt-small\",\n  md: \"px-litt-control-pad-md py-3 text-litt-body\",\n  lg: \"px-litt-control-pad-lg py-4 text-(length:--litt-control-text-lg)\",\n};\n\nexport type InputProps = Omit<InputHTMLAttributes<HTMLInputElement>, \"size\"> & {\n  /** Height on the shared control scale: sm is 32, md is 44, lg is 56. Use lg next to an lg button, and sm in tables and toolbars. */\n  size?: InputSize;\n  /** Draws the error edge and sets aria-invalid. The bare Input shows no message, so link yours with aria-describedby. */\n  invalid?: boolean;\n  /** Geist Mono, for code a person types: a slug, a path, a command. A domain or a number stays in the sans. */\n  mono?: boolean;\n  /** The underlying <input>, for focus or a form library. */\n  ref?: Ref<HTMLInputElement>;\n};\n\n/** What a body draws for the bare input: its size, its state, and every input attribute (the ref included) to put on the element. */\nexport type InputBodyProps = {\n  size: InputSize;\n  invalid: boolean;\n  mono: boolean;\n  className: string;\n  control: Omit<InputHTMLAttributes<HTMLInputElement>, \"size\"> & { ref?: Ref<HTMLInputElement> };\n};\n\n/** What a body draws for the textarea: the same, with the textarea's attributes. */\nexport type TextareaBodyProps = {\n  size: InputSize;\n  invalid: boolean;\n  mono: boolean;\n  className: string;\n  control: TextareaHTMLAttributes<HTMLTextAreaElement> & { ref?: Ref<HTMLTextAreaElement> };\n};\n\n/** What a body draws around a labelled field: the label, the control (children), and the hint or the error under it. */\nexport type FieldBodyProps = {\n  id: string;\n  noteId: string;\n  label: ReactNode;\n  note: ReactNode;\n  error: ReactNode;\n  disabled: boolean | undefined;\n  children: ReactNode;\n};\n\n/* Press, the cut: today's input. */\nfunction PressInput({ size, invalid, mono, className, control }: InputBodyProps) {\n  return (\n    <input\n      data-body=\"press\"\n      aria-invalid={invalid || undefined}\n      className={[field, inputSizes[size], mono ? \"font-litt-mono\" : \"font-litt-field\", className].join(\" \")}\n      {...control}\n    />\n  );\n}\n\n/**\n * The bare input, with no label. Use it in an inline form, such as an email field beside its submit button, and give it\n * an aria-label, because a placeholder is not a label. Every other input attribute goes through.\n */\nexport function Input({ size = \"md\", invalid = false, mono = false, className = \"\", ...rest }: InputProps) {\n  const Body = byWorld(useMaterial(), { press: PressInput, flat: InputFlatBody, device: InputDeviceBody });\n  return <Body size={size} invalid={invalid} mono={mono} className={className} control={rest} />;\n}\n\n/** The ids and text a labelled field needs: the control's id, the note under it, and what that note says. */\nfunction useFieldParts(idProp: string | undefined, help: ReactNode, error: ReactNode, describedBy: string | undefined) {\n  const auto = useId();\n  const id = idProp ?? auto;\n  const note = error ?? help;\n  const noteId = `${id}-note`;\n  return { id, note, noteId, describedBy: [describedBy, note ? noteId : undefined].filter(Boolean).join(\" \") || undefined };\n}\n\n/* Press: the surface the field sits on is the plate already, so the label sits on it above the cut, the hint or the error under. */\nfunction PressField({ id, noteId, label, note, error, disabled, children }: FieldBodyProps) {\n  return (\n    <div data-body=\"press\" className=\"grid content-start gap-2\">\n      <label htmlFor={id} className={\"text-litt-small font-litt-title \" + (disabled ? \"text-litt-fg-disabled\" : \"text-litt-fg\")}>\n        {label}\n      </label>\n      {children}\n      {note && (\n        <p id={noteId} className={\"text-litt-small \" + (error ? \"text-litt-danger\" : \"text-litt-fg-subtle\")}>\n          {note}\n        </p>\n      )}\n    </div>\n  );\n}\n\nfunction Field(props: FieldBodyProps) {\n  const Body = byWorld(useMaterial(), { press: PressField, flat: FieldFlatBody, device: FieldDeviceBody });\n  return <Body {...props} />;\n}\n\nexport type TextFieldProps = Omit<InputProps, \"invalid\"> & {\n  /** The label above the field. Required, because a placeholder is not a label. Clicking it focuses the input. */\n  label: ReactNode;\n  /** A hint under the field. An error replaces it. */\n  help?: ReactNode;\n  /** Shown in danger color and linked to the input with aria-describedby. Also sets aria-invalid. Say how to fix it. */\n  error?: ReactNode;\n};\n\n/** A labelled text input. The label, the hint and the error are wired to the input for you; every other input attribute goes through. */\nexport function TextField({\n  label,\n  help,\n  error,\n  size = \"md\",\n  mono = false,\n  id: idProp,\n  \"aria-describedby\": describedByProp,\n  className = \"\",\n  ...rest\n}: TextFieldProps) {\n  const { id, note, noteId, describedBy } = useFieldParts(idProp, help, error, describedByProp);\n  return (\n    <Field id={id} noteId={noteId} label={label} note={note} error={error} disabled={rest.disabled}>\n      <Input id={id} size={size} mono={mono} invalid={!!error} aria-describedby={describedBy} className={className} {...rest} />\n    </Field>\n  );\n}\n\nexport type TextareaProps = Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, \"id\"> & {\n  /** The label above the field. Required, because a placeholder is not a label. Clicking it focuses the textarea. */\n  label: ReactNode;\n  /** A hint under the field. An error replaces it. */\n  help?: ReactNode;\n  /** Shown in danger color and linked to the textarea with aria-describedby. Also sets aria-invalid. Say how to fix it. */\n  error?: ReactNode;\n  /** Sets the air around the text and its size, like Input: sm is 13px, md is 14px, lg is 16px. The height comes from rows. */\n  size?: InputSize;\n  /** Geist Mono, for code. */\n  mono?: boolean;\n  /** The id of the textarea, when you need your own. */\n  id?: string;\n  /** The underlying <textarea>, for focus or a form library. */\n  ref?: Ref<HTMLTextAreaElement>;\n};\n\n/**\n * A labelled field for longer text, such as a project brief. It has the same edge, focus, error and label as TextField.\n * The height comes from rows, and people can drag it taller.\n */\nexport function Textarea({\n  label,\n  help,\n  error,\n  size = \"md\",\n  mono = false,\n  rows = 4,\n  id: idProp,\n  \"aria-describedby\": describedByProp,\n  className = \"\",\n  ...rest\n}: TextareaProps) {\n  const { id, note, noteId, describedBy } = useFieldParts(idProp, help, error, describedByProp);\n  return (\n    <Field id={id} noteId={noteId} label={label} note={note} error={error} disabled={rest.disabled}>\n      <TextareaControl\n        size={size}\n        invalid={!!error}\n        mono={mono}\n        className={className}\n        control={{ id, rows, \"aria-describedby\": describedBy, ...rest }}\n      />\n    </Field>\n  );\n}\n\n/* Press, the cut: today's textarea. */\nfunction PressTextarea({ size, invalid, mono, className, control }: TextareaBodyProps) {\n  return (\n    <textarea\n      data-body=\"press\"\n      aria-invalid={invalid || undefined}\n      className={[field, \"block resize-y\", textareaSizes[size], mono ? \"font-litt-mono\" : \"font-litt-field\", className].join(\" \")}\n      {...control}\n    />\n  );\n}\n\nfunction TextareaControl(props: TextareaBodyProps) {\n  const Body = byWorld(useMaterial(), { press: PressTextarea, flat: TextareaFlatBody, device: TextareaDeviceBody });\n  return <Body {...props} />;\n}\n",
      "target": "components/litt/input.tsx"
    },
    {
      "path": "registry/litt/ui/bodies/input.device.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n// @litt/input 2026.11.09. What changed: https://littmd.vercel.app/changelog.md\n\n/**\n * Input, the Device body (proposals/worlds.md, Bodies; material.md, 8): LCD glass set in a bezel well.\n *\n * - The well is the bezel around the glass: --litt-tray with --litt-tray-shade, 2px of body between the glass and the wall, at\n *   the control height (32, 44, 56), so the field takes the room it takes in Press. The glass is --litt-field with\n *   --litt-field-shade in the mono (font-litt-field), its corners the well's less those 2px, so the two stay concentric at every\n *   shape. Typed text is --litt-field-ink, the placeholder --litt-field-ink-subtle.\n * - Focus and an error are said on the bezel, not the glass: focus-visible draws --litt-focus-ring around the well, an error\n *   rings it in --litt-danger (and keeps the focus ring on focus). They switch in three steps on the instant tier, never a\n *   fade, and the box never moves. Disabled lies flat: a surface-2 well with a hairline and dimmed glass.\n * - A textarea is the same well and glass with a taller pane; its height comes from rows and it resizes vertically.\n * - The label is engraved on the body above the well (litt-legend: capitals, tracked, --litt-engrave). The hint is a dim\n *   LCD line under it (--litt-field, --litt-field-ink-subtle). An error replaces it on the same glass, its words in --litt-field-ink\n *   beside a lamp in --litt-danger. The lamp never says the error alone: the words do, and aria-invalid and\n *   aria-describedby on the control say it to a screen reader.\n * - Forced colors drops shadows and fills, so the well and the glass get real borders, focus an outline, and the lamp\n *   is a system highlight.\n * - The contract (the real input or textarea, its label, aria-invalid, aria-describedby) is input.tsx's.\n */\nimport type { CSSProperties } from \"react\";\nimport type { FieldBodyProps, InputBodyProps, InputSize, TextareaBodyProps } from \"../input\";\n\n/* The bezel. It carries the field's whole state through :has, because the element inside is the one that takes focus. */\nconst well =\n  \"min-w-0 rounded-litt-control bg-litt-tray p-0.5 shadow-(--litt-tray-shade) \" +\n  \"has-focus-visible:shadow-[var(--litt-tray-shade),var(--litt-focus-ring)] \" +\n  \"has-[[aria-invalid=true]]:shadow-[var(--litt-tray-shade),0_0_0_2px_var(--litt-danger)] \" +\n  \"has-[[aria-invalid=true]:focus-visible]:shadow-[var(--litt-tray-shade),0_0_0_2px_var(--litt-danger),var(--litt-focus-ring)] \" +\n  \"has-disabled:cursor-not-allowed has-disabled:bg-litt-surface-2 has-disabled:shadow-[inset_0_0_0_1px_var(--litt-line)] \" +\n  \"forced-colors:border forced-colors:has-focus-visible:outline-2 forced-colors:has-focus-visible:outline-solid forced-colors:has-focus-visible:outline-offset-2\";\n\n/* The ring switches in three steps on the instant tier, as the switch's detent does. */\nconst STEPS = \"steps(3, end)\";\nconst steps: CSSProperties = { transitionProperty: \"box-shadow\", transitionDuration: \"var(--litt-dur-instant)\", transitionTimingFunction: `${STEPS}` };\n\n/* The glass, in the mono. It fills the well's pane. */\nconst glass =\n  \"w-full min-w-0 rounded-[calc(var(--litt-radius-control)-2px)] bg-litt-field font-litt-field text-litt-field-ink placeholder:text-litt-field-ink-subtle \" +\n  \"pointer-coarse:text-(length:--litt-control-text-lg) shadow-(--litt-field-shade) outline-none \" +\n  \"disabled:cursor-not-allowed disabled:bg-transparent disabled:text-litt-fg-disabled disabled:placeholder:text-litt-fg-disabled disabled:shadow-none \" +\n  \"forced-colors:border\";\n\nconst wellSizes: Record<InputSize, string> = {\n  sm: \"h-litt-control-sm pointer-coarse:h-litt-control-md\",\n  md: \"h-litt-control-md\",\n  lg: \"h-litt-control-lg\",\n};\nconst inputSizes: Record<InputSize, string> = {\n  sm: \"px-[calc(var(--litt-control-pad-sm)-2px)] text-litt-small\",\n  md: \"px-[calc(var(--litt-control-pad-md)-2px)] text-litt-body\",\n  lg: \"px-[calc(var(--litt-control-pad-lg)-2px)] text-(length:--litt-control-text-lg)\",\n};\nconst textareaSizes: Record<InputSize, string> = {\n  sm: \"px-[calc(var(--litt-control-pad-sm)-2px)] py-1.5 text-litt-small\",\n  md: \"px-[calc(var(--litt-control-pad-md)-2px)] py-2.5 text-litt-body\",\n  lg: \"px-[calc(var(--litt-control-pad-lg)-2px)] py-3.5 text-(length:--litt-control-text-lg)\",\n};\n\n/** Device, the bare input: LCD glass in a bezel well. */\nexport function InputDeviceBody({ size, invalid, mono, className, control }: InputBodyProps) {\n  return (\n    <span data-body=\"device\" className={[\"flex w-full\", wellSizes[size], well, className].join(\" \")} style={steps}>\n      <input aria-invalid={invalid || undefined} className={[glass, \"h-full\", inputSizes[size], mono ? \"font-litt-mono\" : \"\"].join(\" \")} {...control} />\n    </span>\n  );\n}\n\n/** Device, the textarea: the same well and glass, taller. */\nexport function TextareaDeviceBody({ size, invalid, mono, className, control }: TextareaBodyProps) {\n  return (\n    <span data-body=\"device\" className={[\"block w-full\", well, className].join(\" \")} style={steps}>\n      <textarea aria-invalid={invalid || undefined} className={[glass, \"block resize-y\", textareaSizes[size], mono ? \"font-litt-mono\" : \"\"].join(\" \")} {...control} />\n    </span>\n  );\n}\n\n/** Device, the engraved label above the well, and the hint or error as a dim LCD line under it. */\nexport function FieldDeviceBody({ id, noteId, label, note, error, disabled, children }: FieldBodyProps) {\n  return (\n    <div data-body=\"device\" className=\"grid gap-2\">\n      <label htmlFor={id} className={\"litt-legend text-litt-caption font-litt-title \" + (disabled ? \"text-litt-fg-disabled\" : \"text-litt-fg\")}>\n        {label}\n      </label>\n      {children}\n      {note && (\n        <p\n          id={noteId}\n          className=\"flex max-w-full items-center gap-2 font-litt-field text-litt-caption\"\n        >\n          {/* The error's lamp sits on the body beside the glass, an indicator on the bezel, clear of the glass's curve. */}\n          {error && (\n            <span\n              aria-hidden=\"true\"\n              className=\"size-1.5 shrink-0 rounded-litt-pill bg-litt-danger shadow-[0_0_6px_var(--litt-danger)] forced-colors:bg-[Highlight]\"\n            />\n          )}\n          <span className={\"w-fit max-w-full rounded-litt-control bg-litt-field px-2.5 py-1 shadow-(--litt-field-shade) forced-colors:border \" + (error ? \"text-litt-field-ink\" : \"text-litt-field-ink-subtle\")}>{note}</span>\n        </p>\n      )}\n    </div>\n  );\n}\n",
      "target": "components/litt/bodies/input.device.tsx"
    },
    {
      "path": "registry/litt/ui/bodies/input.flat.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n// @litt/input 2026.11.09. What changed: https://littmd.vercel.app/changelog.md\n\n/**\n * Input, the Flat body (proposals/worlds.md, Bodies): ink on paper. Nothing stands and nothing is framed.\n *\n * - The field is a grey pill (--litt-cut is --litt-surface-3, at --litt-radius-control, which Flat sets to a pill) with no outline and no shade; a textarea can\n *   hold lines, so it rounds to --litt-radius-surface instead. Typed text is the text weight in --litt-field-ink.\n * - The label is words above in ink at the title weight (--litt-weight-title), the hint plain grey words below\n *   (--litt-fg-subtle). Both are set in from the edge by the pill's padding, so the words line up with the text typed\n *   inside it. No plate, no rule, no card: space is the only divider.\n * - Focus is the ink ring, the one line the field ever draws. An error draws no ring: its words are in --litt-danger and\n *   the pill's fill tints toward --litt-danger, on the color tier. The error never relies on the tint alone; the words and\n *   aria-invalid say it. On focus an invalid pill shows the same ink ring.\n * - Disabled lies flat on --litt-surface-2, its words --litt-fg-disabled.\n * - Forced colors drops fills and shadows, so the pill gets a real border and focus an outline.\n * - A host's className (a width) lands on the pill itself, which sits in a grid column at full width; the label and\n *   the note are not affected.\n */\nimport type { FieldBodyProps, InputBodyProps, InputSize, TextareaBodyProps } from \"../input\";\n\nconst flatField =\n  \"w-full min-w-0 bg-litt-field font-normal text-litt-field-ink placeholder:text-litt-field-ink-subtle \" +\n  \"pointer-coarse:text-(length:--litt-control-text-lg) shadow-none \" +\n  \"transition-[box-shadow,background-color] duration-(--litt-dur-instant) ease-litt \" +\n  \"focus-visible:outline-none focus-visible:shadow-(--litt-focus-ring) \" +\n  \"aria-invalid:bg-[color-mix(in_oklab,var(--litt-danger)_12%,var(--litt-cut))] \" +\n  \"disabled:cursor-not-allowed disabled:bg-litt-surface-2 disabled:text-litt-fg-disabled disabled:placeholder:text-litt-fg-disabled \" +\n  \"forced-colors:border forced-colors:focus-visible:outline-2 forced-colors:focus-visible:outline-solid forced-colors:focus-visible:outline-offset-2\";\n\n/* The text starts a little past the pill's curve, so the words clear the round end. */\nconst inputSizes: Record<InputSize, string> = {\n  sm: \"h-litt-control-sm px-[calc(var(--litt-control-pad-sm)+4px)] text-litt-small pointer-coarse:h-litt-control-md\",\n  md: \"h-litt-control-md px-[calc(var(--litt-control-pad-md)+4px)] text-litt-body\",\n  lg: \"h-litt-control-lg px-[calc(var(--litt-control-pad-lg)+4px)] text-(length:--litt-control-text-lg)\",\n};\n\nconst textareaSizes: Record<InputSize, string> = {\n  sm: \"px-litt-control-pad-sm py-2 text-litt-small\",\n  md: \"px-litt-control-pad-md py-3 text-litt-body\",\n  lg: \"px-litt-control-pad-lg py-4 text-(length:--litt-control-text-lg)\",\n};\n\n/** Flat, the grey pill: no outline, no shade. */\nexport function InputFlatBody({ size, invalid, mono, className, control }: InputBodyProps) {\n  return (\n    <input\n      data-body=\"flat\"\n      aria-invalid={invalid || undefined}\n      className={[flatField, \"rounded-litt-control\", inputSizes[size], mono ? \"font-litt-mono\" : \"font-litt-field\", className].join(\" \")}\n      {...control}\n    />\n  );\n}\n\n/** Flat, the grey field for longer text, rounded to the surface radius. */\nexport function TextareaFlatBody({ size, invalid, mono, className, control }: TextareaBodyProps) {\n  return (\n    <textarea\n      data-body=\"flat\"\n      aria-invalid={invalid || undefined}\n      className={[flatField, \"block resize-y rounded-litt-surface\", textareaSizes[size], mono ? \"font-litt-mono\" : \"font-litt-field\", className].join(\" \")}\n      {...control}\n    />\n  );\n}\n\n/** Flat, the label in ink above the pill, the hint in grey or the error in danger under it, set in to the typed text. */\nexport function FieldFlatBody({ id, noteId, label, note, error, disabled, children }: FieldBodyProps) {\n  return (\n    <div data-body=\"flat\" className=\"grid content-start gap-1.5\">\n      <label htmlFor={id} className={\"px-litt-control-pad-md text-litt-small font-litt-title \" + (disabled ? \"text-litt-fg-disabled\" : \"text-litt-fg\")}>\n        {label}\n      </label>\n      {children}\n      {note && (\n        <p id={noteId} className={\"px-litt-control-pad-md text-litt-small \" + (error ? \"text-litt-danger\" : \"text-litt-fg-subtle\")}>\n          {note}\n        </p>\n      )}\n    </div>\n  );\n}\n",
      "target": "components/litt/bodies/input.flat.tsx"
    }
  ],
  "docs": "Import TextField, Input and Textarea from components/litt/input. TextField wires the label, hint and error for you, and a bare Input needs an aria-label. Text is 16px on touch screens.",
  "meta": {
    "version": "2026.11.09",
    "released": "2026-11-09",
    "changelog": "https://littmd.vercel.app/changelog.md"
  }
}
