{
  "$schema": "https://mian.ro/schema/v2/registry.json",
  "schemaVersion": "2.0.0",
  "generatedAt": "2026-10-02T13:14:06.957Z",
  "id": "progress",
  "name": "Progress",
  "category": "Feedback",
  "description": "Linear progress, determinate from aria-valuenow, or indeterminate without it.",
  "tags": [
    "progress",
    "progress bar",
    "loading",
    "upload",
    "percent"
  ],
  "requiresJs": true,
  "license": "LicenseRef-MIAN-1.0",
  "composition": {
    "role": null,
    "intents": [
      "progress bar",
      "upload progress",
      "loading bar",
      "completion"
    ],
    "prominence": null,
    "density": null,
    "worksAfter": [],
    "worksBefore": [],
    "alternatives": [],
    "avoid": null
  },
  "url": "https://mian.ro/components/progress",
  "markdownUrl": "https://mian.ro/ai/components/progress.md",
  "classes": [
    {
      "name": "mn-progress",
      "description": "Bar. mian.js reads aria-valuenow/aria-valuemax and keeps it in sync."
    },
    {
      "name": "mn-progress-sm · mn-progress-lg",
      "description": "Sizes."
    },
    {
      "name": "mn-progress-label",
      "description": "Label row above the bar."
    }
  ],
  "accessibility": [
    "role=\"progressbar\" with aria-valuenow, and an accessible name. Omit aria-valuenow for indeterminate."
  ],
  "examples": [
    {
      "title": "Determinate",
      "description": null,
      "html": "<div style=\"max-width:26rem\">\n  <div class=\"mn-progress-label\"><span id=\"up-l\">Uploading report.pdf</span><span>64%</span></div>\n  <div class=\"mn-progress\" role=\"progressbar\" aria-labelledby=\"up-l\" aria-valuenow=\"64\"></div>\n</div>"
    },
    {
      "title": "Colours, sizes, indeterminate",
      "description": null,
      "html": "<div class=\"mn-stack\" style=\"max-width:26rem\">\n  <div class=\"mn-progress mn-progress-sm mn-success\" role=\"progressbar\" aria-label=\"Storage\" aria-valuenow=\"30\"></div>\n  <div class=\"mn-progress mn-warning\" role=\"progressbar\" aria-label=\"Quota\" aria-valuenow=\"78\"></div>\n  <div class=\"mn-progress mn-progress-lg mn-danger\" role=\"progressbar\" aria-label=\"Disk\" aria-valuenow=\"95\"></div>\n  <div class=\"mn-progress\" role=\"progressbar\" aria-label=\"Loading\"></div>\n</div>"
    }
  ],
  "css": ".mn-progress {\n  --_v: 0%;\n  --_p: var(--_c, var(--mn-primary));\n  position: relative; display: block; width: 100%; height: .5rem;\n  border-radius: 999px; background: var(--mn-surface-2); overflow: hidden;\n  box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--mn-input) 45%, transparent);\n}\n.mn-progress::after {\n  content: \"\"; position: absolute; inset: 0 auto 0 0; width: var(--_v);\n  border-radius: inherit; background: var(--_p);\n  transition: width 300ms var(--mn-ease);\n}\n.mn-progress:not([aria-valuenow])::after { width: auto; left: 0; right: 100%; animation: mn-indeterminate 1.4s var(--mn-ease) infinite; }\n.mn-progress-sm { height: .3rem; } .mn-progress-lg { height: .8rem; }\n.mn-progress-label { display: flex; justify-content: space-between; margin-bottom: .45rem; font-size: var(--mn-text-sm); }\n.mn-progress-label span:last-child { color: var(--mn-muted); font-variant-numeric: tabular-nums; }\n/* The sweep never leaves the track, so skins may drop overflow: hidden. */\n@keyframes mn-indeterminate { 0% { left: 0; right: 100%; } 50% { left: 15%; right: 25%; } 100% { left: 100%; right: 0; } }\n@media (prefers-reduced-motion: reduce) { .mn-progress:not([aria-valuenow])::after { animation: none; left: 0; right: 0; opacity: .35; } }",
  "js": "const paintProgress = (el) => {\n  const v = +el.getAttribute('aria-valuenow'), max = +(el.getAttribute('aria-valuemax') || 100);\n  if (isNaN(v)) return;\n  const pct = Math.max(0, Math.min(100, (v / max) * 100));\n  if (el.classList.contains('mn-radial')) el.style.setProperty('--_v', pct);\n  else el.style.setProperty('--_v', pct + '%');\n};\nregister('progress', (root) => {\n  each(root, '.mn-progress, .mn-radial', 'Prg', (el) => {\n    if (!el.getAttribute('role')) el.setAttribute('role', 'progressbar');\n    if (el.hasAttribute('aria-valuenow') && !el.hasAttribute('aria-valuemin')) el.setAttribute('aria-valuemin', '0');\n    if (el.hasAttribute('aria-valuenow') && !el.hasAttribute('aria-valuemax')) el.setAttribute('aria-valuemax', '100');\n    paintProgress(el);\n  });\n});",
  "install": {
    "css": "https://mian.ro/dist/mian.css",
    "js": "https://mian.ro/dist/mian.js",
    "registryItem": "https://mian.ro/r/progress.json"
  }
}
