{
  "$schema": "https://mian.ro/schema/v2/registry.json",
  "schemaVersion": "2.0.0",
  "generatedAt": "2026-10-02T13:14:06.957Z",
  "id": "duration-picker",
  "name": "Duration picker",
  "category": "Forms",
  "description": "Hours, minutes and seconds as three small fields that move on as you type.",
  "tags": [
    "duration",
    "time length",
    "timer",
    "hours minutes seconds",
    "cooking time",
    "video length",
    "time input"
  ],
  "requiresJs": true,
  "license": "LicenseRef-MIAN-1.0",
  "composition": {
    "role": null,
    "intents": [
      "duration input",
      "hours minutes seconds",
      "timer length",
      "time span"
    ],
    "prominence": null,
    "density": null,
    "worksAfter": [],
    "worksBefore": [],
    "alternatives": [
      "number-input",
      "time-slots"
    ],
    "avoid": null
  },
  "url": "https://mian.ro/components/duration-picker",
  "markdownUrl": "https://mian.ro/ai/components/duration-picker.md",
  "classes": [
    {
      "name": "mn-duration",
      "description": "The group. Add data-mn-duration."
    },
    {
      "name": "mn-duration-part",
      "description": "One field with its label under it."
    },
    {
      "name": "data-unit",
      "description": "On each input: h, m or s."
    },
    {
      "name": "mn-duration-sep",
      "description": "The colon between parts."
    }
  ],
  "accessibility": [
    "Each part is a labelled number input with its own range.",
    "Put the group in a <fieldset> with a <legend> that says what the duration is for.",
    "Typing two digits moves to the next part; arrow keys step the value.",
    "Listen for mn:change; detail has seconds and text."
  ],
  "examples": [
    {
      "title": "Timer",
      "description": null,
      "html": "<fieldset class=\"mn-fieldset\" style=\"border:0;padding:0;margin:0\"><legend class=\"mn-label\" style=\"margin-bottom:.5rem\">Simmer for</legend>\n  <div class=\"mn-duration\" data-mn-duration>\n    <div class=\"mn-duration-part\"><input id=\"du-h\" type=\"number\" min=\"0\" max=\"23\" value=\"0\" data-unit=\"h\"><label for=\"du-h\">Hours</label></div><span class=\"mn-duration-sep\" aria-hidden=\"true\">:</span>\n    <div class=\"mn-duration-part\"><input id=\"du-m\" type=\"number\" min=\"0\" max=\"59\" value=\"18\" data-unit=\"m\"><label for=\"du-m\">Minutes</label></div><span class=\"mn-duration-sep\" aria-hidden=\"true\">:</span>\n    <div class=\"mn-duration-part\"><input id=\"du-s\" type=\"number\" min=\"0\" max=\"59\" value=\"30\" data-unit=\"s\"><label for=\"du-s\">Seconds</label></div>\n  </div></fieldset>"
    }
  ],
  "css": ".mn-duration { display: inline-flex; align-items: stretch; gap: .35rem; min-width: 0; }\n.mn-duration-part { display: grid; grid-template-rows: auto auto; justify-items: center; gap: .2rem; }\n.mn-duration-part > input { width: 3.4rem; height: calc(var(--mn-size-lg) + 6px); padding: 0; border: var(--mn-border-w) solid var(--mn-input); border-radius: var(--mn-radius-field); background: var(--mn-surface); color: var(--mn-fg); font: 600 1.375rem/1 var(--mn-font); font-variant-numeric: tabular-nums; text-align: center; -moz-appearance: textfield; appearance: textfield; }\n.mn-duration-part > input::-webkit-outer-spin-button, .mn-duration-part > input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }\n.mn-duration-part > input:focus { outline: none; border-color: var(--mn-ring); box-shadow: 0 0 0 3px color-mix(in oklab, var(--mn-ring) 25%, transparent); }\n.mn-duration-part > label { color: var(--mn-muted); font-size: .6875rem; letter-spacing: .04em; text-transform: uppercase; }\n.mn-duration-sep { align-self: start; margin-top: calc((var(--mn-size-lg) + 6px) / 2 - .7rem); color: var(--mn-muted); font: 600 1.25rem/1 var(--mn-font); }\n\n/* Flowing gradients and poster type (see gradient and hero). */\n.mn-gradient-flow { background-image: none; background-color: color-mix(in oklab, var(--g2) 55%, var(--g3)); overflow: hidden; }\n.mn-gradient-flow::before { content: \"\"; position: absolute; inset: -30%; z-index: -2; filter: blur(clamp(28px, 6cqi, 70px)); pointer-events: none;\n  background:\n    radial-gradient(46% 50% at 24% 30%, var(--g1), transparent 72%),\n    radial-gradient(48% 54% at 78% 30%, var(--g2), transparent 72%),\n    radial-gradient(54% 58% at 54% 80%, var(--g3), transparent 74%),\n    radial-gradient(38% 42% at 16% 84%, var(--g4), transparent 72%);\n  animation: mn-flow 22s var(--mn-ease-in-out) infinite alternate; }\n.mn-gradient-flow.mn-gradient-still::before { animation: none; }\n@keyframes mn-flow { 0% { transform: rotate(0deg) scale(1) translate(0, 0); } 50% { transform: rotate(14deg) scale(1.12) translate(4%, -3%); } 100% { transform: rotate(-10deg) scale(1.06) translate(-4%, 4%); } }\n.mn-gradient-halo { --g1: #ff5005; --g2: #dbba95; --g3: #d0bce1; --g4: #ff8a4c; }\n.mn-gradient-lagoon { --g1: #00c2a8; --g2: #0a6cff; --g3: #9ef0d0; --g4: #0b3a7a; }\n.mn-gradient-citrus { --g1: #ffd000; --g2: #ff7a00; --g3: #b8e04a; --g4: #ffe9a8; }\n.mn-gradient-nightfall { --g1: #0b1b3a; --g2: #d9480f; --g3: #14325e; --g4: #f2a65a; }\n.mn-gridlines { background-image: linear-gradient(to right, color-mix(in oklab, var(--mn-fg) 9%, transparent) 1px, transparent 1px), linear-gradient(to bottom, color-mix(in oklab, var(--mn-fg) 9%, transparent) 1px, transparent 1px); background-size: var(--grid, 4rem) var(--grid, 4rem); }\n.mn-poster { margin: 0; font: var(--mn-heading-weight) clamp(2.75rem, 13.5cqi, 16rem)/.88 var(--mn-font-heading); letter-spacing: -.045em; text-wrap: balance; color: var(--mn-fg); }\n@media (prefers-reduced-motion: reduce) { .mn-gradient-flow::before { animation: none; } .mn-task > span::after { transition: none; } }",
  "js": "/* <div class=\"mn-duration\" data-mn-duration> … <input type=\"number\" min=\"0\" max=\"59\" data-unit=\"m\"> …\n   Keeps each part in range and two digits wide, moves on when a part is\n   full, arrow keys step. Emits mn:change { seconds, text }. */\nregister('duration-picker', (root) => each(root, '[data-mn-duration]', 'Dur', (el) => {\n  const parts = [...el.querySelectorAll('input[type=\"number\"]')];\n  const mult = { h: 3600, m: 60, s: 1 };\n  const pad = (i) => { const max = Number(i.max || 59), min = Number(i.min || 0); const v = Math.max(min, Math.min(max, Math.round(Number(i.value) || 0))); i.value = String(v).padStart(2, '0'); return v; };\n  const report = () => { const seconds = parts.reduce((a, i) => a + pad(i) * (mult[i.dataset.unit] || 1), 0); emit(el, 'change', { seconds, text: parts.map((i) => i.value).join(':') }); };\n  parts.forEach((i, n) => {\n    i.setAttribute('inputmode', 'numeric');\n    i.addEventListener('focus', () => i.select());\n    i.addEventListener('input', () => { if (i.value.length >= 2 && parts[n + 1]) parts[n + 1].focus(); });\n    i.addEventListener('change', report);\n    i.addEventListener('keydown', (e) => { if (e.key === 'Backspace' && i.value === '' && parts[n - 1]) parts[n - 1].focus(); });\n    pad(i);\n  });\n}));",
  "install": {
    "css": "https://mian.ro/dist/mian.css",
    "js": "https://mian.ro/dist/mian.js",
    "registryItem": "https://mian.ro/r/duration-picker.json"
  }
}
