{
  "$schema": "https://mian.ro/schema/v2/registry.json",
  "schemaVersion": "2.0.0",
  "generatedAt": "2026-10-02T13:14:06.957Z",
  "id": "slides",
  "name": "Slides",
  "category": "Sections",
  "description": "A presentation website: full-height slides that snap, with arrow-key navigation, a counter and progress.",
  "tags": [
    "slides",
    "presentation",
    "deck",
    "pitch deck",
    "keynote",
    "slideshow",
    "story",
    "scrollytelling",
    "one page"
  ],
  "requiresJs": true,
  "license": "LicenseRef-MIAN-1.0",
  "composition": {
    "role": "frame",
    "intents": [
      "presentation website",
      "pitch deck",
      "keynote",
      "slides",
      "investor deck",
      "one idea per screen"
    ],
    "prominence": "high",
    "density": "low",
    "worksAfter": [],
    "worksBefore": [],
    "alternatives": [
      "article"
    ],
    "avoid": "Paragraphs on slides; decks that only work with a clicker."
  },
  "url": "https://mian.ro/components/slides",
  "markdownUrl": "https://mian.ro/ai/components/slides.md",
  "classes": [
    {
      "name": "mn-deck",
      "description": "The wrapper for the slides, the bar and the progress line."
    },
    {
      "name": "mn-slides",
      "description": "The scrolling deck (focusable, with an aria-label). --h sets its height (default 100dvh)."
    },
    {
      "name": "mn-slide",
      "description": "A slide; mn-slide-center centres it."
    },
    {
      "name": "mn-deck-bar",
      "description": "Counter ([data-mn-slide-count]) and prev/next buttons (data-mn-slide=\"prev|next\")."
    },
    {
      "name": "mn-deck-progress",
      "description": "A thin progress line at the top."
    }
  ],
  "accessibility": [
    "The deck is focusable with an aria-label; arrow keys, Page Up/Down, Space, Home and End move between slides.",
    "Each slide gets aria-roledescription=\"slide\" and a “3 of 8” label; the counter is visible text.",
    "Scroll snapping stays usable with a mouse wheel, touch and assistive tech: nothing is hidden."
  ],
  "examples": [
    {
      "title": "Deck",
      "description": null,
      "html": "<div class=\"mn-deck\" style=\"border:1px solid var(--mn-border);border-radius:var(--mn-radius-box);overflow:hidden\">\n  <div class=\"mn-deck-progress\"></div>\n  <div class=\"mn-slides\" tabindex=\"0\" aria-label=\"Ledger, Series A\" style=\"--h:22rem\">\n    <section class=\"mn-slide mn-slide-center\"><p class=\"mn-eyebrow\">Series A · 2026</p><h3 class=\"mn-title\">Close the books in four days.</h3><p class=\"mn-intro\">Ledger, for finance teams of 5 to 500.</p></section>\n    <section class=\"mn-slide\"><p class=\"mn-eyebrow\">The problem</p><h3 class=\"mn-title\">Month-end takes 14 days. 10 of them are data entry.</h3></section>\n    <section class=\"mn-slide\"><dl class=\"mn-figures\" style=\"--cols:3\"><div><dt>Customers</dt><dd>1,400</dd></div><div><dt>ARR</dt><dd>€8.2 M</dd></div><div><dt>Net retention</dt><dd>131%</dd></div></dl></section>\n  </div>\n  <div class=\"mn-deck-bar\"><span data-mn-slide-count>1 / 3</span><button class=\"mn-btn mn-btn-ghost mn-btn-icon mn-btn-sm\" type=\"button\" data-mn-slide=\"prev\" aria-label=\"Previous slide\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"m15 6-6 6 6 6\"/></svg></button><button class=\"mn-btn mn-btn-ghost mn-btn-icon mn-btn-sm\" type=\"button\" data-mn-slide=\"next\" aria-label=\"Next slide\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"m9 6 6 6-6 6\"/></svg></button></div>\n</div>"
    }
  ],
  "css": ".mn-deck { position: relative; }\n.mn-slides { container: mn / inline-size; height: var(--h, 100dvh); overflow-y: auto; overscroll-behavior: contain; scroll-snap-type: y mandatory; scroll-behavior: smooth; scrollbar-width: none; }\n.mn-slides::-webkit-scrollbar { display: none; }\n.mn-slides:focus-visible { outline: 2px solid var(--mn-ring); outline-offset: -2px; }\n.mn-slide { display: grid; align-content: center; gap: 1.5rem; min-height: 100%; padding: clamp(1.5rem, 7cqi, 6rem); scroll-snap-align: start; scroll-snap-stop: always; }\n.mn-slide > * { margin: 0; }\n.mn-slide-center { justify-items: center; text-align: center; }\n.mn-deck-bar { position: absolute; right: 1rem; bottom: 1rem; z-index: 2; display: flex; align-items: center; gap: .35rem; padding: .3rem .3rem .3rem .75rem; border-radius: 999px; background: color-mix(in srgb, var(--mn-surface) 88%, transparent); box-shadow: var(--mn-shadow-md); color: var(--mn-muted); font: 500 .8125rem/1 var(--mn-font); font-variant-numeric: tabular-nums; -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); }\n.mn-deck-progress { position: absolute; top: 0; left: 0; right: 0; z-index: 2; height: 3px; background: color-mix(in oklab, var(--mn-fg) 8%, transparent); }\n.mn-deck-progress::after { content: \"\"; position: absolute; inset: 0 auto 0 0; width: var(--_v, 0%); background: var(--mn-primary); transition: width 300ms var(--mn-ease); }\n@media (prefers-reduced-motion: reduce) { .mn-slides { scroll-behavior: auto; } }",
  "js": "/* <div class=\"mn-deck\"><div class=\"mn-slides\" tabindex=\"0\" aria-label=\"…\">\n   <section class=\"mn-slide\">…</section>…</div></div>\n   Arrow keys, Page Up/Down, Space, Home and End move between slides; a\n   counter ([data-mn-slide-count]) and a progress bar (.mn-deck-progress)\n   follow the slide in view; buttons with data-mn-slide=\"next|prev\" step.\n   Emits mn:change { index, total } on the deck. */\nregister('slides', (root) => {\n  each(root, '.mn-slides', 'Deck', (deck) => {\n    const slides = () => [...deck.querySelectorAll(':scope > .mn-slide')];\n    const wrap = deck.closest('.mn-deck') || deck.parentElement;\n    let index = 0;\n    const horizontal = getComputedStyle(deck).scrollSnapType.startsWith('x');\n    const go = (i) => {\n      const s = slides(); i = Math.max(0, Math.min(s.length - 1, i));\n      const t = s[i]; if (!t) return;\n      deck.scrollTo({ [horizontal ? 'left' : 'top']: horizontal ? t.offsetLeft - deck.offsetLeft : t.offsetTop - deck.offsetTop, behavior: matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth' });\n    };\n    const update = (i) => {\n      index = i; const total = slides().length;\n      slides().forEach((s, k) => { s.setAttribute('aria-roledescription', 'slide'); s.setAttribute('aria-label', `${k + 1} of ${total}`); });\n      wrap.querySelectorAll('[data-mn-slide-count]').forEach((c) => { c.textContent = `${i + 1} / ${total}`; });\n      wrap.querySelector('.mn-deck-progress')?.style.setProperty('--_v', `${((i + 1) / total) * 100}%`);\n      wrap.querySelectorAll('[data-mn-slide=\"prev\"]').forEach((b) => { b.disabled = i === 0; });\n      wrap.querySelectorAll('[data-mn-slide=\"next\"]').forEach((b) => { b.disabled = i === total - 1; });\n      emit(deck, 'change', { index: i, total });\n    };\n    if ('IntersectionObserver' in window) {\n      const io = new IntersectionObserver((entries) => {\n        for (const en of entries) if (en.isIntersecting && en.intersectionRatio >= .6) update(slides().indexOf(en.target));\n      }, { root: deck, threshold: [.6] });\n      slides().forEach((s) => io.observe(s));\n    }\n    deck.addEventListener('keydown', (e) => {\n      if (e.target !== deck && e.target.closest('input, textarea, select, [contenteditable]')) return;\n      const map = { ArrowDown: 1, ArrowRight: 1, PageDown: 1, ' ': 1, ArrowUp: -1, ArrowLeft: -1, PageUp: -1 };\n      if (e.key in map) { e.preventDefault(); go(index + (e.shiftKey && e.key === ' ' ? -1 : map[e.key])); }\n      else if (e.key === 'Home') { e.preventDefault(); go(0); }\n      else if (e.key === 'End') { e.preventDefault(); go(slides().length - 1); }\n    });\n    wrap.addEventListener('click', (e) => {\n      const b = e.target.closest('[data-mn-slide]'); if (!b) return;\n      go(index + (b.dataset.mnSlide === 'next' ? 1 : -1));\n    });\n    update(0);\n  });\n});",
  "install": {
    "css": "https://mian.ro/dist/mian.css",
    "js": "https://mian.ro/dist/mian.js",
    "registryItem": "https://mian.ro/r/slides.json"
  }
}
