Exclusivebeta
Origami — <mv-origami>
Panels that fold like paper. Fold variant: an accordion on native <details> whose content is sliced into strips that unfold zigzag-style in 3D, with light and shadow on every fold, glints on the edges, paper grain, a spring bounce and creases that stay visible once open. Letter variant: a sealed envelope that opens, its letter slides out, comes to the front and unfolds in three panels, inline or in a modal dialog, for an announcement or an invitation.
| Category | Data display |
|---|---|
| Type | Web Component (<mv-origami>) |
| Status | beta |
| Keywords | exclusive, accordion, disclosure, details, faq, fold, paper, 3d, letter, envelope, announcement, modal, invitation |
When to use
- A showcase or brand page wants an accordion whose panels unfold like folded paper in 3D
- An announcement or invitation should arrive as a sealed envelope that opens into a letter
- A special message should open in a modal letter with focus trap and fold back on close
Avoid when
- A plain, fast disclosure for everyday settings or help content is needed → use Accordion instead
- Users open and close panels frequently, like in a dense app; the fold animation slows repeated use
- A standard confirmation or form must appear in a modal → use Dialog instead
Install
node scripts/add.mjs origami --out ./src/marvelousAI agent with the Marvelous UI MCP server: install_components({ slugs: ["origami"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).
Files copied (dependencies included): tokens/tokens.css, core/base.css, core/dom.js, core/element.js, core/motion.js, core/observe.js, components/origami/origami.js, components/origami/origami.css.
Usage
Canonical markup — start from it and customize with attributes, data-* and CSS variables:
<div id="og-demo" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,22rem),1fr));gap:2rem 2.5rem;width:min(100%,62rem);margin-inline:auto;align-items:start">
<style>
#og-demo h3 { margin:0 0 .75rem; font-size:.75rem; font-weight:600; letter-spacing:.08em; text-transform:uppercase; color:var(--mv-fg-muted) }
#og-demo .mv-origami-sheet p { margin:0 0 .6rem }
#og-demo .mv-origami-sheet ul { margin:.25rem 0 .6rem; padding-left:1.1rem }
#og-demo .og-letter h4 { margin:0 0 .15rem; font-size:1.375rem; letter-spacing:-.01em }
#og-demo .og-letter .og-date { margin:0 0 1.1rem; color:var(--mv-fg-muted); font-size:.75rem; letter-spacing:.04em; text-transform:uppercase }
#og-demo .og-letter p { margin:0 0 .75rem }
#og-demo .og-letter .og-where { display:grid; grid-template-columns:auto 1fr; gap:.2rem .9rem; margin:1rem 0; padding:.75rem .9rem; border-radius:var(--mv-radius-md); background:color-mix(in oklab, var(--mv-accent) 7%, transparent); font-size:.8125rem }
#og-demo .og-letter .og-where dt { color:var(--mv-fg-muted) }
#og-demo .og-letter .og-where dd { margin:0; font-weight:500 }
#og-demo .og-sign { margin:1rem 0 1.25rem; font:italic 500 1.125rem/1.2 "Segoe Script","Bradley Hand",cursive }
#og-demo .og-actions { display:flex; gap:.5rem; flex-wrap:wrap }
#og-demo .og-hint { margin:.9rem 0 0; text-align:center; color:var(--mv-fg-muted); font-size:.75rem }
</style>
<section aria-labelledby="og-faq-t">
<h3 id="og-faq-t">Frequently asked questions</h3>
<mv-origami id="og-faq" type="single">
<details>
<summary>How long does shipping take?</summary>
<p>Orders placed before 2 p.m. ship the same day from our Brooklyn studio.</p>
<ul>
<li>Standard: 3–5 business days, free over $60</li>
<li>Express: next-day delivery by 1 p.m.</li>
<li>Pickup point: 48 hours, at 8,000+ locations nationwide</li>
</ul>
<p>You’ll get a tracking link by email as soon as your order ships.</p>
</details>
<details>
<summary>Can I return an item?</summary>
<p>Yes, you have 30 days after delivery to change your mind. Returns are free at any pickup point: print the label from your account, drop off the package, done.</p>
<p>Refunds are issued to the original payment method within 5 business days of the package reaching the studio.</p>
</details>
<details>
<summary>Where are your lamps made?</summary>
<p>Every shade is shaped by hand in our Brooklyn studio. The ceramic bases come from a family-run pottery in Vermont, and the fabric cords are woven in North Carolina.</p>
</details>
<details>
<summary>Do you offer a warranty?</summary>
<p>Every lamp comes with a 5-year warranty on parts and labor. If something breaks, we repair it instead of replacing it — that’s craftsmanship too.</p>
</details>
</mv-origami>
</section>
<section aria-labelledby="og-letter-t">
<h3 id="og-letter-t">Announcement</h3>
<mv-origami variant="letter" id="og-letter" label="News from the studio">
<div class="mv-origami-address"><small>Nomad Studio · Brooklyn</small>For you, Priya</div>
<article class="mv-origami-letter og-letter">
<h4>We’re moving!</h4>
<p class="og-date">Brooklyn, Sep 23</p>
<p>Dear Priya,</p>
<p>After six wonderful years on Bergen Street, the studio is growing. We’ll finally have room to welcome you while we work, and to run lampshade-making classes on Saturdays.</p>
<dl class="og-where">
<dt>Address</dt><dd>14 Water Street, Brooklyn</dd>
<dt>Grand opening</dt><dd>Saturday, Oct 18, from 11 a.m.</dd>
</dl>
<p>We’d love to raise a glass with you that day.</p>
<p class="og-sign">Lena & the whole team</p>
<div class="og-actions">
<button class="mv-button" type="button">I’ll be there</button>
<button class="mv-button" data-variant="ghost" type="button" data-origami-close>Fold the letter</button>
</div>
</article>
</mv-origami>
<p class="og-hint">Click the envelope · <button class="mv-button" data-variant="link" data-size="sm" type="button" data-origami-open="og-modal">Open in a dialog</button></p>
<mv-origami variant="letter" id="og-modal" modal label="Grand opening invitation">
<div class="mv-origami-address"><small>Nomad Studio · Brooklyn</small>For you, Priya</div>
<article class="mv-origami-letter og-letter">
<h4>You’re invited</h4>
<p class="og-date">Saturday, Oct 18 · 11 a.m.</p>
<p>Come see the new studio at 14 Water Street: live shade-making demos, coffee from the roaster next door, and 15% off the whole collection for the occasion.</p>
<p class="og-sign">See you soon, Lena</p>
<div class="og-actions">
<button class="mv-button" type="button" data-origami-close>Thanks, see you Saturday!</button>
</div>
</article>
</mv-origami>
</section>
<script type="module">
await customElements.whenDefined("mv-origami");
const faq = document.getElementById("og-faq");
// Show the fold once, a moment after the page appears.
setTimeout(() => faq.toggle(0, true), 500);
</script>
</div>API
Attributes
| Name | Type | Default | Description |
|---|---|---|---|
variant | fold | letter | fold | Folded accordion, or a letter in its envelope. |
folds | number | auto (≈ 1 fold / 52 px, 3 to 10) | fold: number of paper strips. |
type | single | multiple | multiple | fold: single folds the other panels when one opens (animated, unlike the native name attribute). |
open | boolean | letter: letter unfolded (on load: opened without animation). | |
modal | boolean | letter: the envelope opens in a modal <dialog> (Escape or a backdrop click folds it back). | |
label | string | A letter for you | letter: name of the envelope (button, back of the letter, dialog). |
data-origami-close | boolean | On a button inside the letter: folds it back into the envelope. | |
data-origami-open | id | On a button anywhere: opens the letter <mv-origami id="…">. |
Methods
| Name | Description |
|---|---|
toggle(index | details, force?) | fold: opens / folds a panel; letter: toggles the letter. |
show() / hide() | letter: unfolds / folds. |
Events
| Name | Description |
|---|---|
mv-toggle | Start of an opening or closing. detail: { open, item? }. |
Content structure
| Name | Description |
|---|---|
details > summary + content | fold: everything after the <summary> becomes the sheet (.mv-origami-sheet, created if missing). |
.mv-origami-letter | letter: the letter content (real, accessible content). |
.mv-origami-address | letter: handwritten text on the back of the envelope (<small> for the sender). |
CSS classes
| Name | Description |
|---|---|
mv-origami-item / -panel / -sheet | fold: <details> element, animated container and paper sheet. |
mv-origami-stage / -strip / -face / -shade | fold: cloned strips (aria-hidden) while folding. |
mv-origami-desk / -envelope / -env-* | letter: scene, open button and envelope parts (back, flap, pocket, seal). |
mv-origami-folded / -leaf-* | letter: letter folded in three during the animation. |
CSS variables
| Name | Default | Description |
|---|---|---|
--mv-origami-paper | warm off-white / deep gray | Paper color. |
--mv-origami-ink | var(--mv-fg) | Text color on the paper. |
--mv-origami-crease | Stroke of the creases that stay visible. | |
--mv-origami-texture | Paper grain (image, none to remove it). | |
--mv-origami-envelope / --mv-origami-seal | Envelope kraft paper, seal wax. | |
--mv-origami-letter-width | 34rem | Letter width. |
--mv-origami-perspective | 1800px | 3D depth of the fold. |
--mv-origami-script | Handwriting font for the address. |
Accessibility
fold: native <details>/<summary> semantics (Enter, Space, find-in-page opens the right panel); the animated strips are aria-hidden, inert clones with no duplicate ids, and the real content takes over as soon as the paper lies flat. letter: the envelope is a real button (aria-expanded, aria-controls); once unfolded, the letter (a region named by its heading) receives focus; “Fold” returns focus to the envelope; in modal mode, a native <dialog> with focus trap, Escape and focus return. Reduced motion: instant opening, the creases stay drawn on the paper as a static cue. Loops only run during an animation.