{
  "$schema": "https://mian.ro/schema/v2/registry.json",
  "schemaVersion": "2.0.0",
  "generatedAt": "2026-10-02T13:14:06.957Z",
  "id": "password-input",
  "name": "Password field",
  "category": "Forms",
  "description": "A password input with a show/hide toggle and an optional four-step strength meter.",
  "tags": [
    "password",
    "show password",
    "reveal",
    "strength meter",
    "sign up",
    "credentials"
  ],
  "requiresJs": true,
  "license": "LicenseRef-MIAN-1.0",
  "composition": {
    "role": null,
    "intents": [
      "password field",
      "show password",
      "password strength",
      "new password"
    ],
    "prominence": null,
    "density": null,
    "worksAfter": [],
    "worksBefore": [],
    "alternatives": [],
    "avoid": null
  },
  "url": "https://mian.ro/components/password-input",
  "markdownUrl": "https://mian.ro/ai/components/password-input.md",
  "classes": [
    {
      "name": "mn-password",
      "description": "Add to an mn-input-group that holds the input and the toggle."
    },
    {
      "name": "mn-password-toggle",
      "description": "The show/hide button. Put both icons inside: .mn-password-show and .mn-password-hide."
    },
    {
      "name": "mn-password-meter[data-mn-for=\"#id\"]",
      "description": "Four <i> segments and a <span> for the label."
    }
  ],
  "accessibility": [
    "The toggle is a real button with a fixed label (“Show password”); aria-pressed=\"true\" says the password is visible.",
    "The strength label is linked with aria-describedby and announced politely as it changes. Colour is never the only signal.",
    "Use autocomplete=\"new-password\" for sign-up and \"current-password\" for sign-in."
  ],
  "examples": [
    {
      "title": "With strength meter",
      "description": null,
      "html": "<div class=\"mn-field\" style=\"max-width:22rem\">\n  <label class=\"mn-label\" for=\"pw-1\">New password</label>\n  <div class=\"mn-input-group mn-password\">\n    <input class=\"mn-input\" id=\"pw-1\" type=\"password\" autocomplete=\"new-password\" value=\"noodles\">\n    <button class=\"mn-password-toggle\" type=\"button\" aria-label=\"Show password\" aria-pressed=\"false\"><span class=\"mn-password-show\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7S2 12 2 12Z\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/></svg></span><span class=\"mn-password-hide\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M3 3l18 18\"/><path d=\"M10.6 5.1A10.4 10.4 0 0 1 12 5c6.5 0 10 7 10 7a17.6 17.6 0 0 1-3.2 4.2M6.6 6.6C3.8 8.4 2 12 2 12s3.5 7 10 7a9.7 9.7 0 0 0 5.4-1.6\"/><path d=\"M9.9 9.9a3 3 0 0 0 4.2 4.2\"/></svg></span></button>\n  </div>\n  <div class=\"mn-password-meter\" data-mn-for=\"#pw-1\"><i></i><i></i><i></i><i></i><span></span></div>\n</div>"
    }
  ],
  "css": ".mn-password { position: relative; }\n.mn-password-toggle { display: grid; place-items: center; width: var(--mn-size-md); flex: none; border: 0; background: none; color: var(--mn-muted); cursor: pointer; }\n.mn-password-toggle:hover { color: var(--mn-fg); }\n.mn-password-toggle:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--mn-ring); border-radius: var(--mn-radius-field); }\n.mn-password-toggle svg { width: 1.05rem; height: 1.05rem; }\n.mn-password-toggle .mn-password-hide, .mn-password-toggle[aria-pressed=\"true\"] .mn-password-show { display: none; }\n.mn-password-toggle[aria-pressed=\"true\"] .mn-password-hide { display: contents; }\n.mn-password-meter { --_s: 0; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 4px; margin-top: .5rem; }\n.mn-password-meter > i { height: 4px; border-radius: 999px; background: var(--mn-surface-2); transition: background-color var(--mn-duration) var(--mn-ease); }\n.mn-password-meter > i:nth-child(-n + 4) { background: var(--mn-surface-2); }\n.mn-password-meter[data-score=\"1\"] > i:nth-child(-n + 1) { background: var(--mn-danger); }\n.mn-password-meter[data-score=\"2\"] > i:nth-child(-n + 2) { background: var(--mn-warning); }\n.mn-password-meter[data-score=\"3\"] > i:nth-child(-n + 3) { background: var(--mn-info); }\n.mn-password-meter[data-score=\"4\"] > i:nth-child(-n + 4) { background: var(--mn-success); }\n.mn-password-meter > span { grid-column: 1 / -1; font-size: calc(var(--mn-text-sm) - 1px); color: var(--mn-muted); }",
  "js": "/* <div class=\"mn-input-group mn-password\"><input type=\"password\" …>\n     <button class=\"mn-password-toggle\">…</button></div>\n   <div class=\"mn-password-meter\" data-mn-for=\"#id\"><i></i><i></i><i></i><i></i><span></span></div>\n   The toggle shows and hides the password; the meter scores it 0–4. */\nconst passwordScore = (v) => {\n  if (!v) return 0;\n  let s = 0;\n  if (v.length >= 8) s++;\n  if (v.length >= 12) s++;\n  if (/[a-z]/.test(v) && /[A-Z]/.test(v)) s++;\n  if (/\\d/.test(v)) s++;\n  if (/[^A-Za-z0-9]/.test(v)) s++;\n  if (v.length < 8) s = Math.min(s, 1);\n  return Math.max(1, Math.min(4, s));\n};\nconst PASSWORD_WORDS = ['', 'Weak', 'Fair', 'Good', 'Strong'];\nregister('password-input', (root) => {\n  each(root, '.mn-password', 'Pw', (w) => {\n    const input = w.querySelector('input');\n    const t = w.querySelector('.mn-password-toggle');\n    if (!input) return;\n    id(input, 'mn-pw');\n    if (t) {\n      t.type = 'button';\n      t.setAttribute('aria-controls', input.id);\n      t.setAttribute('aria-pressed', 'false');\n      if (!t.getAttribute('aria-label')) t.setAttribute('aria-label', 'Show password');\n      t.addEventListener('click', () => {\n        const show = input.type === 'password';\n        input.type = show ? 'text' : 'password';\n        // The label stays \"Show password\"; aria-pressed carries the state.\n        t.setAttribute('aria-pressed', String(show));\n      });\n    }\n    const meter = document.querySelector(`.mn-password-meter[data-mn-for=\"#${CSS.escape(input.id)}\"]`);\n    if (!meter) return;\n    const label = meter.querySelector('span');\n    if (label) { label.setAttribute('aria-live', 'polite'); id(label, 'mn-pw-strength'); input.setAttribute('aria-describedby', [input.getAttribute('aria-describedby'), label.id].filter(Boolean).join(' ')); }\n    const update = () => {\n      const s = passwordScore(input.value);\n      meter.dataset.score = String(s);\n      if (label) label.textContent = input.value ? `Strength: ${PASSWORD_WORDS[s]}` : '';\n      emit(input, 'strength', { score: s });\n    };\n    input.addEventListener('input', update);\n    update();\n  });\n});",
  "install": {
    "css": "https://mian.ro/dist/mian.css",
    "js": "https://mian.ro/dist/mian.js",
    "registryItem": "https://mian.ro/r/password-input.json"
  }
}
