{
  "$schema": "https://mian.ro/schema/v2/registry.json",
  "schemaVersion": "2.0.0",
  "generatedAt": "2026-10-02T13:14:06.957Z",
  "id": "file-input",
  "name": "File input",
  "category": "Forms",
  "description": "A styled native file picker, and a drag-and-drop zone that wraps one.",
  "tags": [
    "file",
    "upload",
    "dropzone",
    "drag and drop",
    "attachment"
  ],
  "requiresJs": true,
  "license": "LicenseRef-MIAN-1.0",
  "composition": {
    "role": null,
    "intents": [
      "file upload",
      "drag and drop upload",
      "attach file",
      "image upload"
    ],
    "prominence": null,
    "density": null,
    "worksAfter": [],
    "worksBefore": [],
    "alternatives": [],
    "avoid": null
  },
  "url": "https://mian.ro/components/file-input",
  "markdownUrl": "https://mian.ro/ai/components/file-input.md",
  "classes": [
    {
      "name": "mn-file",
      "description": "On an <input type=\"file\">."
    },
    {
      "name": "mn-dropzone",
      "description": "A <label> containing a file input. Accepts drops; shows the chosen names in [data-mn-file-name]."
    }
  ],
  "accessibility": [
    "The dropzone is a label for a real file input, so it is keyboard and screen-reader operable. Dragging is an enhancement, not the only path."
  ],
  "examples": [
    {
      "title": "Basic",
      "description": null,
      "html": "<div class=\"mn-field\" style=\"max-width:26rem\">\n  <label class=\"mn-label\" for=\"file-1\">Avatar</label>\n  <input class=\"mn-file\" id=\"file-1\" type=\"file\" accept=\"image/*\">\n</div>"
    },
    {
      "title": "Dropzone",
      "description": null,
      "html": "<label class=\"mn-dropzone\" style=\"max-width:26rem\">\n  <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M12 16V4M7 9l5-5 5 5\"/><path d=\"M4 16v3a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-3\"/></svg>\n  <span><strong>Click to upload</strong> or drag and drop</span>\n  <span class=\"mn-small\" data-mn-file-name=\"PNG, JPG or PDF up to 10MB\">PNG, JPG or PDF up to 10MB</span>\n  <input type=\"file\" multiple>\n</label>"
    }
  ],
  "css": ".mn-file {\n  display: block; width: 100%;\n  border: var(--mn-border-w) solid var(--mn-input); border-radius: var(--mn-radius-field);\n  background: var(--mn-surface); color: var(--mn-muted);\n  font: 400 var(--mn-text-sm)/1 var(--mn-font);\n  box-shadow: var(--mn-shadow-sm);\n  cursor: pointer;\n}\n.mn-file::file-selector-button {\n  height: calc(var(--mn-size-md) - 2px); margin-inline-end: .75rem; padding: 0 .9rem;\n  border: 0; border-inline-end: var(--mn-border-w) solid var(--mn-input);\n  background: var(--mn-surface-2); color: var(--mn-fg);\n  font: 500 var(--mn-text-sm)/1 var(--mn-font); cursor: pointer;\n}\n.mn-file:hover::file-selector-button { background: color-mix(in oklab, var(--mn-surface-2), var(--mn-fg) 6%); }\n.mn-file:focus-visible { outline: none; box-shadow: var(--mn-focus); }\n.mn-dropzone {\n  display: grid; place-items: center; gap: .5rem; padding: 2rem 1rem;\n  border: 2px dashed var(--mn-input); border-radius: var(--mn-radius-box);\n  background: var(--mn-surface); color: var(--mn-muted); text-align: center;\n  font-size: var(--mn-text-sm); cursor: pointer;\n  transition: border-color var(--mn-duration) var(--mn-ease), background-color var(--mn-duration) var(--mn-ease);\n}\n.mn-dropzone:hover, .mn-dropzone[data-over] { border-color: var(--mn-ring); background: var(--mn-hover); }\n.mn-dropzone:has(input:focus-visible) { box-shadow: var(--mn-focus); }\n.mn-dropzone svg { width: 1.75rem; height: 1.75rem; }\n.mn-dropzone strong { color: var(--mn-fg); font-weight: 600; }\n.mn-dropzone input { position: absolute; width: 1px; height: 1px; opacity: 0; }",
  "js": "register('file-input', (root) => {\n  each(root, '.mn-dropzone', 'Drop', (z) => {\n    const input = z.querySelector('input[type=\"file\"]');\n    const label = z.querySelector('[data-mn-file-name]');\n    if (!input) return;\n    const show = () => {\n      if (!label) return;\n      const f = [...input.files];\n      label.textContent = f.length ? f.map((x) => x.name).join(', ') : label.dataset.mnFileName || '';\n    };\n    input.addEventListener('change', show);\n    ['dragenter', 'dragover'].forEach((t) => z.addEventListener(t, (e) => { e.preventDefault(); z.dataset.over = ''; }));\n    ['dragleave', 'drop'].forEach((t) => z.addEventListener(t, () => delete z.dataset.over));\n    z.addEventListener('drop', (e) => { e.preventDefault(); if (e.dataTransfer?.files?.length) { input.files = e.dataTransfer.files; show(); input.dispatchEvent(new Event('change', { bubbles: true })); } });\n  });\n});",
  "install": {
    "css": "https://mian.ro/dist/mian.css",
    "js": "https://mian.ro/dist/mian.js",
    "registryItem": "https://mian.ro/r/file-input.json"
  }
}
