{
  "$schema": "https://mian.ro/schema/v2/registry.json",
  "schemaVersion": "2.0.0",
  "generatedAt": "2026-10-02T13:14:06.957Z",
  "id": "modal",
  "name": "Modal",
  "category": "Overlays",
  "description": "A dialog on the native <dialog> element: focus trap, Escape, inert background and focus return from the platform.",
  "tags": [
    "modal",
    "dialog",
    "popup",
    "confirm",
    "lightbox",
    "alert dialog"
  ],
  "requiresJs": true,
  "license": "LicenseRef-MIAN-1.0",
  "composition": {
    "role": null,
    "intents": [
      "dialog",
      "popup",
      "confirm dialog",
      "modal form",
      "lightbox"
    ],
    "prominence": null,
    "density": null,
    "worksAfter": [],
    "worksBefore": [],
    "alternatives": [],
    "avoid": null
  },
  "url": "https://mian.ro/components/modal",
  "markdownUrl": "https://mian.ro/ai/components/modal.md",
  "classes": [
    {
      "name": "dialog.mn-modal",
      "description": "The dialog."
    },
    {
      "name": "mn-modal-header · mn-modal-title · mn-modal-description · mn-modal-body · mn-modal-footer",
      "description": "Parts."
    },
    {
      "name": "mn-modal-sm · mn-modal-lg · mn-modal-full",
      "description": "Sizes."
    },
    {
      "name": "mn-modal-sheet",
      "description": "Becomes a bottom sheet on phones."
    },
    {
      "name": "data-mn-open=\"id\" · data-mn-close",
      "description": "Open and close triggers."
    },
    {
      "name": "data-mn-static",
      "description": "Ignore backdrop clicks and Escape, for flows that must be completed."
    },
    {
      "name": "mn-close",
      "description": "The × button."
    }
  ],
  "accessibility": [
    "showModal() makes everything else inert and traps focus. mian.js sets aria-labelledby from the title.",
    "For destructive confirmations add role=\"alertdialog\" and put focus on the least destructive button with autofocus."
  ],
  "examples": [
    {
      "title": "Edit profile",
      "description": null,
      "html": "<button class=\"mn-btn mn-btn-outline\" data-mn-open=\"dlg-profile\">Edit profile</button>\n<dialog class=\"mn-modal mn-modal-sheet\" id=\"dlg-profile\">\n  <div class=\"mn-modal-header\">\n    <div><h2 class=\"mn-modal-title\">Edit profile</h2><p class=\"mn-modal-description\">Make changes to your profile here. Click save when you're done.</p></div>\n    <button class=\"mn-close\" data-mn-close aria-label=\"Close\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M18 6 6 18M6 6l12 12\"/></svg></button>\n  </div>\n  <form class=\"mn-modal-body mn-form\" method=\"dialog\" id=\"dlg-profile-form\">\n    <div class=\"mn-field\"><label class=\"mn-label\" for=\"dp-n\">Name</label><input class=\"mn-input\" id=\"dp-n\" value=\"Ana Popescu\"></div>\n    <div class=\"mn-field\"><label class=\"mn-label\" for=\"dp-u\">Username</label><input class=\"mn-input\" id=\"dp-u\" value=\"@ana\"></div>\n  </form>\n  <div class=\"mn-modal-footer\"><button class=\"mn-btn mn-btn-outline\" data-mn-close>Cancel</button><button class=\"mn-btn\" form=\"dlg-profile-form\" value=\"save\">Save changes</button></div>\n</dialog>"
    },
    {
      "title": "Confirmation",
      "description": null,
      "html": "<button class=\"mn-btn mn-danger\" data-mn-open=\"dlg-del\">Delete account</button>\n<dialog class=\"mn-modal mn-modal-sm\" id=\"dlg-del\" role=\"alertdialog\" aria-describedby=\"dlg-del-d\">\n  <div class=\"mn-modal-header\"><h2 class=\"mn-modal-title\">Delete account?</h2></div>\n  <div class=\"mn-modal-body\" id=\"dlg-del-d\">This permanently deletes your account and all of its data. This cannot be undone.</div>\n  <div class=\"mn-modal-footer\"><button class=\"mn-btn mn-btn-outline\" data-mn-close autofocus>Cancel</button><button class=\"mn-btn mn-danger\" data-mn-close=\"delete\">Delete</button></div>\n</dialog>"
    }
  ],
  "css": ".mn-modal {\n  width: min(32rem, calc(100vw - 2rem)); max-height: calc(100dvh - 2rem);\n  margin: auto; padding: 0;\n  border: var(--mn-border-w) solid var(--mn-border); border-radius: var(--mn-radius-box);\n  background: var(--mn-popover); color: var(--mn-fg);\n  box-shadow: var(--mn-shadow-lg);\n  font-family: var(--mn-font);\n  overflow: auto; overscroll-behavior: contain;\n}\n.mn-modal[open] { display: flex; flex-direction: column; animation: var(--mn-enter) calc(var(--mn-enter-duration) + 40ms) var(--mn-ease-enter); }\n.mn-modal::backdrop { background: var(--mn-overlay); backdrop-filter: blur(2px); animation: mn-fade 200ms var(--mn-ease); }\n.mn-modal-sm { width: min(24rem, calc(100vw - 2rem)); }\n.mn-modal-lg { width: min(48rem, calc(100vw - 2rem)); }\n.mn-modal-full { width: calc(100vw - 2rem); height: calc(100dvh - 2rem); }\n.mn-modal-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; padding: 1.25rem 1.25rem 0; }\n.mn-modal-title { margin: 0; font: var(--mn-heading-weight) 1.125rem/1.3 var(--mn-font-heading); letter-spacing: -.01em; }\n.mn-modal-description { margin: .35rem 0 0; font-size: var(--mn-text-sm); color: var(--mn-muted); line-height: 1.5; }\n.mn-modal-body { padding: 1rem 1.25rem; font-size: var(--mn-text-sm); line-height: 1.6; overflow: auto; }\n.mn-modal-footer { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: .5rem; padding: 0 1.25rem 1.25rem; }\n.mn-close {\n  display: grid; place-items: center; flex: none; width: 2rem; height: 2rem; margin: -.35rem -.5rem 0 0;\n  border: 0; border-radius: var(--mn-radius-field); background: none; color: var(--mn-muted); cursor: pointer;\n}\n.mn-close:hover { background: var(--mn-hover); color: var(--mn-fg); }\n.mn-close:focus-visible { outline: none; box-shadow: var(--mn-focus); }\n.mn-close svg { width: 1rem; height: 1rem; }\n@keyframes mn-modal-in { from { opacity: 0; transform: translateY(8px) scale(.97); } }\n@media (max-width: 40rem) {\n  .mn-modal.mn-modal-sheet { width: 100vw; max-width: 100vw; margin: auto 0 0; border-radius: var(--mn-radius-box) var(--mn-radius-box) 0 0; border-bottom: 0; }\n  .mn-modal.mn-modal-sheet[open] { animation: mn-sheet-in 240ms var(--mn-ease); }\n}\n@keyframes mn-sheet-in { from { transform: translateY(100%); } }\nhtml:has(dialog[open].mn-modal, dialog[open].mn-drawer) { overflow: hidden; scrollbar-gutter: stable; }",
  "js": "/* Modals and drawers are native <dialog> elements: focus trapping, Escape,\n   inert background and focus return all come from the platform. */\nregister('dialog', (root) => {\n  each(root, 'dialog.mn-modal, dialog.mn-drawer', 'Dlg', (dlg) => {\n    const title = dlg.querySelector('.mn-modal-title, .mn-drawer-title, h1, h2, h3');\n    if (title && !dlg.hasAttribute('aria-labelledby')) dlg.setAttribute('aria-labelledby', id(title, 'mn-dlg-title'));\n    // Click on the backdrop closes, unless the dialog is marked static.\n    dlg.addEventListener('pointerdown', (e) => { dlg._downOnBackdrop = e.target === dlg; });\n    dlg.addEventListener('click', (e) => {\n      if (e.target !== dlg || !dlg._downOnBackdrop || dlg.hasAttribute('data-mn-static')) return;\n      const r = dlg.getBoundingClientRect();\n      const inside = e.clientX >= r.left && e.clientX <= r.right && e.clientY >= r.top && e.clientY <= r.bottom;\n      if (!inside) dlg.close('backdrop');\n    });\n    dlg.addEventListener('cancel', (e) => { if (dlg.hasAttribute('data-mn-static')) e.preventDefault(); });\n    dlg.addEventListener('close', () => emit(dlg, 'close', { returnValue: dlg.returnValue }));\n  });\n});\n\ndocument.addEventListener('click', (e) => {\n  const opener = e.target.closest('[data-mn-open]');\n  if (opener) {\n    const dlg = document.getElementById(opener.dataset.mnOpen);\n    if (dlg?.showModal && !dlg.open) { dlg.showModal(); emit(dlg, 'open'); }\n    return;\n  }\n  const closer = e.target.closest('[data-mn-close]');\n  if (closer) closer.closest('dialog')?.close(closer.dataset.mnClose || closer.value || '');\n});",
  "install": {
    "css": "https://mian.ro/dist/mian.css",
    "js": "https://mian.ro/dist/mian.js",
    "registryItem": "https://mian.ro/r/modal.json"
  }
}
