{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "input",
  "type": "registry:component",
  "title": "Input",
  "description": "Collects one line of text. 1px border, 2px focus ring, 12px label above.",
  "registryDependencies": [
    "https://vlak.dev/r/vlak-base.json",
    "https://vlak.dev/r/inter.json",
    "https://vlak.dev/r/vlak-lib.json",
    "https://vlak.dev/r/field.json"
  ],
  "dependencies": [
    "@stylexjs/stylex"
  ],
  "devDependencies": [
    "@stylexjs/babel-plugin"
  ],
  "docs": "Vlak leaves are StyleX. Compile them with @stylexjs/babel-plugin (Vite: @stylexjs/unplugin, Next: @stylexjs/nextjs-plugin). If you would rather not run a compiler, import @noorddev/vlak-react instead: it ships precompiled with one stylesheet.",
  "files": [
    {
      "path": "vlak/input.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport * as stylex from \"@stylexjs/stylex\";\nimport { vlak, mq } from \"./tokens.stylex\";\nimport { rs } from \"./rs\";\nimport { cx } from \"./cx\";\nimport { useFieldControl } from \"./field\";\n\nexport interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {\n  /** Label rendered above the field at 12px. */\n  label?: React.ReactNode;\n  /** Marks the field as validated: ink border and ink feedback text. */\n  ok?: boolean;\n  /** Quiet feedback line under the field. */\n  feedback?: React.ReactNode;\n  /** Hint under the field; it describes the control. */\n  hint?: React.ReactNode;\n  /** Error under the field; it describes the control and marks it invalid. */\n  error?: React.ReactNode;\n  /** Control only — no field stack. For Field / InputGroup. */\n  plain?: boolean;\n  /** Flush into an InputGroup: no own stroke. */\n  grouped?: boolean;\n}\n\nconst styles = stylex.create({\n  field: {\n    width: \"100%\",\n    display: \"flex\",\n    flexDirection: \"column\",\n    gap: \"0.5rem\",\n  },\n  label: {\n    fontSize: {\n      default: \"0.75rem\",\n      [mq.phone]: vlak.controlLabel,\n    },\n    fontWeight: 600,\n    color: vlak.gray,\n    letterSpacing: \"-0.01em\",\n    lineHeight: \"16px\",\n  },\n  input: {\n    boxSizing: \"border-box\",\n    height: vlak.controlH,\n    minHeight: {\n      default: null,\n      [mq.phone]: vlak.hit,\n    },\n    borderWidth: vlak.hairline,\n    borderStyle: \"solid\",\n    borderColor: {\n      default: vlak.controlBorder,\n      \":focus\": vlak.accent,\n    },\n    borderRadius: {\n      default: vlak.radiusSm,\n      [mq.phone]: vlak.radiusSm,\n    },\n    appearance: \"none\",\n    WebkitAppearance: \"none\",\n    backgroundColor: \"var(--bg)\",\n    color: \"var(--text)\",\n    caretColor: \"var(--text)\",\n    fontSize: {\n      default: \"0.875rem\",\n      [mq.phone]: \"1rem\",\n    },\n    lineHeight: `calc(${vlak.controlH} - 2px)`,\n    paddingInline: {\n      default: \"0.75rem\",\n      [mq.phone]: \"0.875rem\",\n    },\n    paddingBlock: 0,\n    outlineWidth: {\n      default: 0,\n      \":focus-visible\": 2,\n    },\n    outlineStyle: {\n      default: \"none\",\n      \":focus-visible\": \"solid\",\n    },\n    outlineColor: vlak.ink,\n    outlineOffset: 2,\n    fontFamily: \"inherit\",\n    \":-webkit-autofill\": {\n      WebkitTextFillColor: \"var(--text)\",\n      caretColor: \"var(--text)\",\n      backgroundColor: \"var(--bg)\",\n      boxShadow: \"inset 0 0 0 1000px var(--bg)\",\n    },\n  },\n  grouped: {\n    borderWidth: 0,\n    borderRadius: 0,\n    height: \"100%\",\n    minHeight: {\n      default: null,\n      [mq.phone]: vlak.hit,\n    },\n    flexGrow: 1,\n    minWidth: 0,\n    backgroundColor: \"var(--bg)\",\n    color: \"var(--text)\",\n    // The group draws the ring; its overflow would clip this one.\n    outlineWidth: {\n      default: 0,\n      \":focus-visible\": 0,\n    },\n    outlineStyle: {\n      default: \"none\",\n      \":focus-visible\": \"none\",\n    },\n  },\n  full: {\n    width: \"100%\",\n  },\n  ok: {\n    borderColor: vlak.ink,\n  },\n  invalid: {\n    borderColor: vlak.ink,\n  },\n  feedback: {\n    display: \"flex\",\n    alignItems: \"center\",\n    gap: \"0.5rem\",\n    fontSize: {\n      default: \"0.75rem\",\n      [mq.phone]: \"0.875rem\",\n    },\n    fontWeight: 500,\n    color: vlak.gray,\n    letterSpacing: \"-0.01em\",\n    lineHeight: \"16px\",\n    margin: 0,\n    minHeight: \"1rem\",\n  },\n  feedbackOk: {\n    color: vlak.ink,\n  },\n  feedbackError: {\n    color: vlak.ink,\n  },\n});\n\nexport const Input = React.forwardRef<HTMLInputElement, InputProps>(function Input(\n  { label, ok, feedback, hint, error, plain, grouped, className, style, id, ...props },\n  ref,\n) {\n  const autoId = React.useId();\n  const inputId = id ?? autoId;\n  const stacked = !plain && !grouped;\n  const hintId = stacked && hint != null ? `${inputId}-hint` : undefined;\n  const errorId = stacked && error != null ? `${inputId}-error` : undefined;\n  const field = useFieldControl(props);\n  const invalid = field.invalid || error != null;\n  const describedBy = cx(field[\"aria-describedby\"], hintId, errorId) || undefined;\n  const sx = rs([\"rs-input\", !grouped && \"rs-input-full\", ok && \"rs-input-ok\", invalid && \"rs-input-invalid\", className, grouped && \"rs-input-grouped\"], styles.input, !grouped && styles.full, ok && styles.ok, invalid && styles.invalid, grouped && styles.grouped);\n  const control = (\n    <input\n      ref={ref}\n      id={inputId}\n      {...props}\n      aria-describedby={describedBy}\n      aria-invalid={invalid ? true : field[\"aria-invalid\"]}\n      className={sx.className}\n      style={{ ...sx.style, ...style }}\n    />\n  );\n  if (!stacked) return control;\n  const stack = rs([\"rs-field\", \"rs-input-field\"], styles.field);\n  const lab = rs([\"rs-field-label\", \"rs-input-label\"], styles.label);\n  const fb = rs([\"rs-feedback\", ok && \"rs-feedback-ok\"], styles.feedback, ok && styles.feedbackOk);\n  const hintLine = rs([\"rs-feedback\"], styles.feedback);\n  const errorLine = rs([\"rs-feedback\", \"rs-feedback-error\"], styles.feedback, styles.feedbackError);\n  return (\n    <div className={stack.className} style={stack.style}>\n      {label != null && (\n        <label className={lab.className} style={lab.style} htmlFor={inputId}>\n          {label}\n        </label>\n      )}\n      {control}\n      {feedback != null && (\n        <span className={fb.className} style={fb.style}>\n          {feedback}\n        </span>\n      )}\n      {hint != null && (\n        <span id={hintId} className={hintLine.className} style={hintLine.style}>\n          {hint}\n        </span>\n      )}\n      {error != null && (\n        <span id={errorId} role=\"alert\" className={errorLine.className} style={errorLine.style}>\n          {error}\n        </span>\n      )}\n    </div>\n  );\n});\n",
      "type": "registry:component",
      "target": "components/vlak/input.tsx"
    },
    {
      "path": "vlak/styles/input.css",
      "content": "/* ── input: generated from packages/react/src/components/input.tsx ── */\n.rs-field.rs-input-field{width:100%;display:flex;flex-direction:column;gap:0.5rem}\n.rs-field-label.rs-input-label{font-size:0.75rem;font-weight:600;color:var(--text-secondary);letter-spacing:-0.01em;line-height:16px}\n@media (max-width: 640px){.rs-field-label.rs-input-label{font-size:var(--control-label)}}\n.rs-input{box-sizing:border-box;height:var(--control-h);border-width:1px;border-style:solid;border-color:var(--control-border);border-radius:var(--radius-sm);appearance:none;-webkit-appearance:none;background-color:var(--bg);color:var(--text);caret-color:var(--text);font-size:0.875rem;line-height:calc(var(--control-h) - 2px);padding-inline:0.75rem;padding-block:0;outline-width:0;outline-style:none;outline-color:var(--text);outline-offset:2px;font-family:inherit}\n.rs-input:focus{border-color:var(--accent)}\n.rs-input:focus-visible{outline-width:2px;outline-style:solid}\n.rs-input:-webkit-autofill{-webkit-text-fill-color:var(--text);caret-color:var(--text);background-color:var(--bg);box-shadow:inset 0 0 0 1000px var(--bg)}\n@media (max-width: 640px){.rs-input{min-height:var(--hit);border-radius:var(--radius-sm);font-size:1rem;padding-inline:0.875rem}}\n.rs-input-grouped{border-width:0;border-radius:0;height:100%;flex-grow:1;min-width:0;background-color:var(--bg);color:var(--text);outline-width:0;outline-style:none}\n.rs-input-grouped:focus-visible{outline-width:0;outline-style:none}\n@media (max-width: 640px){.rs-input-grouped{min-height:var(--hit)}}\n.rs-input-full{width:100%}\n.rs-input-ok{border-color:var(--text)}\n.rs-input-invalid{border-color:var(--text)}\n.rs-feedback{display:flex;align-items:center;gap:0.5rem;font-size:0.75rem;font-weight:500;color:var(--text-secondary);letter-spacing:-0.01em;line-height:16px;margin:0;min-height:1rem}\n@media (max-width: 640px){.rs-feedback{font-size:0.875rem}}\n.rs-feedback-ok{color:var(--text)}\n.rs-feedback-error{color:var(--text)}\n",
      "type": "registry:file",
      "target": "styles/vlak/input.css"
    }
  ],
  "meta": {
    "vlak": {
      "category": "forms",
      "classes": [
        "rs-field",
        "rs-field-label",
        "rs-input",
        "rs-input-full",
        "rs-input-ok",
        "rs-input-invalid",
        "rs-feedback",
        "rs-feedback-ok",
        "rs-feedback-error",
        "rs-input-field",
        "rs-input-grouped",
        "rs-input-label"
      ],
      "snippet": "<div class=\"rs-field\"><span class=\"rs-field-label\">E-mail</span><input class=\"rs-input rs-input-full\" /></div>",
      "cssOnly": false,
      "registryDependencies": [
        "vlak-lib",
        "field"
      ],
      "aliases": [
        "Input",
        "Text field",
        "TextField",
        "Text input"
      ],
      "example": "import { Input } from \"@noorddev/vlak-react\";\n\n<Input label=\"E-mail\" type=\"email\" placeholder=\"you@example.com\" hint=\"We never share it.\" />\n<Input label=\"Name\" error=\"Name is required.\" />\n<Input label=\"Handle\" ok feedback=\"Available\" />",
      "usage": {
        "use": [
          "Single-line text, e-mail, number, password, and search fields.",
          "label, hint, and error together; the component wires them to the control."
        ],
        "avoid": [
          "Long text; use Textarea.",
          "A fixed set of options; use Select or NativeSelect."
        ]
      },
      "keyboard": [
        {
          "keys": "Tab",
          "does": "Moves focus to the field"
        },
        {
          "keys": "Enter",
          "does": "Submits the enclosing form"
        }
      ],
      "a11y": [
        "Renders a native <input> with a generated id; label points at it with htmlFor.",
        "hint and error are linked through aria-describedby; error also sets aria-invalid and renders with role=\"alert\".",
        "Inside Field, pass plain so Field's hint and error describe the control instead."
      ]
    }
  }
}
