beta

Card Stack <mv-card-stack>

Pila de tarjetas para deslizar: cada tarjeta reposa con su propia ligera inclinación y se endereza cuando se aparta la de arriba; la tarjeta superior gira alrededor del punto por el que la agarraste, una insignia de veredicto y un tinte en el borde crecen con el tirón mientras se llena el botón correspondiente, los lanzamientos conservan la inercia del gesto y next() mete la tarjeta debajo de la pila; botones, teclas de flecha, deshacer, bucle o fin de pila, y reproducción automática.

Categoría3D
TipoWeb Component (<mv-card-stack>)
Estadobeta
Keywordscard-stack, swipe, deck, pile, fling, drag, testimonials, stack

When to use

  • Users must accept or skip items one at a time, as in matching, curation or onboarding choices
  • Testimonials or highlights should cycle as a tactile, slightly tilted pile with autoplay
  • Swipe decisions need undo, arrow-key control and announced verdicts

Avoid when

  • Items should be browsed or compared freely rather than decided on one by one → use Carousel instead
  • Cards should stack as the page scrolls, with no swipe gesture → use Scroll Stack instead

Instalación

node scripts/add.mjs card-stack --out ./src/marvelous

Agente de IA con el servidor MCP de Marvelous UI: install_components({ slugs: ["card-stack"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).

Archivos copiados (dependencias incluidas): tokens/tokens.css, core/base.css, core/dom.js, core/element.js, core/motion.js, core/pointer.js, components/card-stack/card-stack.js, components/card-stack/card-stack.css.

Uso

Marcado de referencia: parte de él y personalízalo con atributos, data-* y variables CSS:

<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(20rem,1fr));gap:3rem;align-items:start;width:100%;max-width:52rem">
  <div style="display:grid;gap:1rem;justify-items:center">
    <mv-card-stack id="cs-demo" label="Weekend ideas" accept-label="I’m in" reject-label="Skip">
    <article>
      <div aria-hidden="true" style="aspect-ratio:4/3;background:radial-gradient(circle at 30% 24%,#ecfeff 0 8%,transparent 8.5%),linear-gradient(150deg,transparent 57%,#0f766e 57.3%),linear-gradient(200deg,transparent 67%,#134e4a 67.3%),linear-gradient(180deg,#0ea5e9,#67e8f9 62%,#ecfeff)"></div>
      <div style="padding:1rem 1.1rem 1.2rem;display:grid;gap:.35rem">
        <h4 style="margin:0;font:700 1.15rem/1.2 var(--mv-font-sans);letter-spacing:-.01em">Banff</h4>
        <p style="margin:0;font-size:.85rem;color:var(--mv-fg-muted)">Canada · 2 days · from $180</p>
        <p style="margin:.35rem 0 0;display:flex;flex-wrap:wrap;gap:.35rem"><span style="padding:.15rem .55rem;border-radius:999px;background:var(--mv-bg-muted);font-size:.75rem">Kayaking</span><span style="padding:.15rem .55rem;border-radius:999px;background:var(--mv-bg-muted);font-size:.75rem">Hiking</span><span style="padding:.15rem .55rem;border-radius:999px;background:var(--mv-bg-muted);font-size:.75rem">Camping</span></p>
      </div>
    </article>
    <article>
      <div aria-hidden="true" style="aspect-ratio:4/3;background:radial-gradient(circle at 41% 30%,#f8fafc 0 8%,transparent 8.5%),linear-gradient(157deg,transparent 57%,#e2e8f0 57.3%),linear-gradient(194deg,transparent 67%,#475569 67.3%),linear-gradient(180deg,#64748b,#bae6fd 62%,#f8fafc)"></div>
      <div style="padding:1rem 1.1rem 1.2rem;display:grid;gap:.35rem">
        <h4 style="margin:0;font:700 1.15rem/1.2 var(--mv-font-sans);letter-spacing:-.01em">Cliffs of Moher</h4>
        <p style="margin:0;font-size:.85rem;color:var(--mv-fg-muted)">Ireland · 1 day · from $65</p>
        <p style="margin:.35rem 0 0;display:flex;flex-wrap:wrap;gap:.35rem"><span style="padding:.15rem .55rem;border-radius:999px;background:var(--mv-bg-muted);font-size:.75rem">Cliffs</span><span style="padding:.15rem .55rem;border-radius:999px;background:var(--mv-bg-muted);font-size:.75rem">Cycling</span><span style="padding:.15rem .55rem;border-radius:999px;background:var(--mv-bg-muted);font-size:.75rem">Seafood</span></p>
      </div>
    </article>
    <article>
      <div aria-hidden="true" style="aspect-ratio:4/3;background:radial-gradient(circle at 52% 36%,#fdba74 0 8%,transparent 8.5%),linear-gradient(164deg,transparent 57%,#4c1d95 57.3%),linear-gradient(188deg,transparent 67%,#1e1b4b 67.3%),linear-gradient(180deg,#6d28d9,#f472b6 62%,#fdba74)"></div>
      <div style="padding:1rem 1.1rem 1.2rem;display:grid;gap:.35rem">
        <h4 style="margin:0;font:700 1.15rem/1.2 var(--mv-font-sans);letter-spacing:-.01em">Lake Como</h4>
        <p style="margin:0;font-size:.85rem;color:var(--mv-fg-muted)">Italy · 3 days · from $240</p>
        <p style="margin:.35rem 0 0;display:flex;flex-wrap:wrap;gap:.35rem"><span style="padding:.15rem .55rem;border-radius:999px;background:var(--mv-bg-muted);font-size:.75rem">Lake</span><span style="padding:.15rem .55rem;border-radius:999px;background:var(--mv-bg-muted);font-size:.75rem">Paddleboarding</span><span style="padding:.15rem .55rem;border-radius:999px;background:var(--mv-bg-muted);font-size:.75rem">Gelato</span></p>
      </div>
    </article>
    <article>
      <div aria-hidden="true" style="aspect-ratio:4/3;background:radial-gradient(circle at 63% 24%,#fff7ed 0 8%,transparent 8.5%),linear-gradient(171deg,transparent 57%,#7c2d12 57.3%),linear-gradient(182deg,transparent 67%,#431407 67.3%),linear-gradient(180deg,#b45309,#fcd34d 62%,#fff7ed)"></div>
      <div style="padding:1rem 1.1rem 1.2rem;display:grid;gap:.35rem">
        <h4 style="margin:0;font:700 1.15rem/1.2 var(--mv-font-sans);letter-spacing:-.01em">Napa Valley</h4>
        <p style="margin:0;font-size:.85rem;color:var(--mv-fg-muted)">California · 2 days · from $210</p>
        <p style="margin:.35rem 0 0;display:flex;flex-wrap:wrap;gap:.35rem"><span style="padding:.15rem .55rem;border-radius:999px;background:var(--mv-bg-muted);font-size:.75rem">Vineyards</span><span style="padding:.15rem .55rem;border-radius:999px;background:var(--mv-bg-muted);font-size:.75rem">Wine tasting</span></p>
      </div>
    </article>
    <article>
      <div aria-hidden="true" style="aspect-ratio:4/3;background:radial-gradient(circle at 74% 30%,#dbeafe 0 8%,transparent 8.5%),linear-gradient(178deg,transparent 57%,#1e3a8a 57.3%),linear-gradient(176deg,transparent 67%,#172554 67.3%),linear-gradient(180deg,#1d4ed8,#60a5fa 62%,#dbeafe)"></div>
      <div style="padding:1rem 1.1rem 1.2rem;display:grid;gap:.35rem">
        <h4 style="margin:0;font:700 1.15rem/1.2 var(--mv-font-sans);letter-spacing:-.01em">Algarve Coast</h4>
        <p style="margin:0;font-size:.85rem;color:var(--mv-fg-muted)">Portugal · 3 days · from $150</p>
        <p style="margin:.35rem 0 0;display:flex;flex-wrap:wrap;gap:.35rem"><span style="padding:.15rem .55rem;border-radius:999px;background:var(--mv-bg-muted);font-size:.75rem">Surfing</span><span style="padding:.15rem .55rem;border-radius:999px;background:var(--mv-bg-muted);font-size:.75rem">Coastal trails</span></p>
      </div>
    </article>
    </mv-card-stack>
    <p id="cs-demo-out" style="margin:0;min-height:1.3em;font-size:.85rem;color:var(--mv-fg-muted)">Swipe the card, or use ←/→ on your keyboard (Backspace to undo).</p>
  </div>
  <div style="display:grid;gap:1rem;justify-items:center;align-self:center">
    <mv-card-stack label="Testimonials" autoplay="3800" no-controls style="--mv-card-stack-width:22rem;--mv-card-stack-offset:12px">
    <figure style="margin:0;padding:1.3rem 1.4rem;display:grid;gap:1rem;min-height:11rem;align-content:space-between">
      <blockquote style="margin:0;font-size:1rem;line-height:1.5">“We cut the time it takes to lay out our campaigns in half.”</blockquote>
      <figcaption style="display:flex;gap:.7rem;align-items:center"><span aria-hidden="true" style="width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,#f472b6,#8b5cf6)"></span><span><strong style="display:block;font-size:.9rem">Maya Chen</strong><small style="color:var(--mv-fg-muted)">Art Director, Northwind Studio</small></span></figcaption>
    </figure>
    <figure style="margin:0;padding:1.3rem 1.4rem;display:grid;gap:1rem;min-height:11rem;align-content:space-between">
      <blockquote style="margin:0;font-size:1rem;line-height:1.5">“The components hold up in production, even in Safari.”</blockquote>
      <figcaption style="display:flex;gap:.7rem;align-items:center"><span aria-hidden="true" style="width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,#34d399,#0ea5e9)"></span><span><strong style="display:block;font-size:.9rem">Daniel Okafor</strong><small style="color:var(--mv-fg-muted)">Lead front-end, Rails & Co</small></span></figcaption>
    </figure>
    <figure style="margin:0;padding:1.3rem 1.4rem;display:grid;gap:1rem;min-height:11rem;align-content:space-between">
      <blockquote style="margin:0;font-size:1rem;line-height:1.5">“Finally, animations that respect reduced motion.”</blockquote>
      <figcaption style="display:flex;gap:.7rem;align-items:center"><span aria-hidden="true" style="width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,#fbbf24,#f97316)"></span><span><strong style="display:block;font-size:.9rem">Priya Raman</strong><small style="color:var(--mv-fg-muted)">Accessibility specialist</small></span></figcaption>
    </figure>
    </mv-card-stack>
    <p style="margin:0;font-size:.85rem;color:var(--mv-fg-muted)">autoplay · no-controls · pauses on hover</p>
  </div>
</div>
<script type="module">
  const stack = document.getElementById("cs-demo");
  const out = document.getElementById("cs-demo-out");
  const saved = [];
  stack.addEventListener("mv-swipe", (e) => {
    const name = e.detail.card.querySelector("h4").textContent;
    if (e.detail.direction === "right") saved.push(name);
    out.textContent = e.detail.direction === "right" ? `Saved to your wishlist: ${saved.join(", ")}` : `Skipped ${name}`;
  });
</script>

API

Attributes

NameTipoDefaultDescription
labelstringAccessible name of the region.
accept-label / reject-labelstringLike / SkipText for the stamps, buttons and announcements.
no-loopbooleanSwiped cards leave the stack (data-empty + mv-empty at the end) instead of going back underneath.
no-controlsbooleanHides the Skip / Like buttons.
autoplaynumber (ms)Advances to the next card on an interval (4000 ms if empty); pauses on hover and focus, disabled under reduced motion.
thresholdnumber0.3Fraction of the width to cross to confirm a slow drag (a quick flick is enough).

Properties

NameTipoDescription
currentElement | nullTop card.
remainingnumberCards still in the stack.

Methods

NameDescription
accept() / reject()Flings the top card right or left.
next()Tucks the top card under the pile with no verdict (used by autoplay).
undo()Brings back the last swiped card from the side it left.
reset()Puts every card back in its original order.

Events

NameDescription
mv-swipedetail: { direction: 'left' | 'right' | 'next', index, card }.
mv-emptyNo cards left (no-loop).

Content structure

NameDescription
childrenEach child is a card (role=group “card”, named after its title).

CSS classes

NameDescription
mv-card-stack-deck / -card / -tint / -stamp (-stamp-icon, -stamp-text) / -controls / -buttonGenerated parts; data-top on the top card, data-gone on cards that left the stack (no-loop), data-kind on buttons (reject | undo | accept).

CSS variables

NameDefaultDescription
--mv-card-stack-width20remCard width.
--mv-card-stack-offset14pxHow far the pile settles below the top card.
--mv-card-stack-tilt1Multiplier for the resting tilt of the cards below (0 = squared-up pile).
--mv-card-stack-radiusvar(--mv-radius-2xl)Card radius.
--mv-card-stack-accept / --mv-card-stack-rejectsuccess / dangerBadge, tint and button colors.

Accessibility

Region with aria-roledescription “card stack”. The stack is focusable: ← skip, → accept, Backspace or Ctrl/Cmd+Z undo (instructions exposed via aria-describedby). Only the top card is exposed; the others are aria-hidden and inert. Every verdict is announced (“I’m in: Lake Como. Next card: …”) in a polite live region. Named Skip / Undo / Like buttons; Undo is disabled until something was swiped. Reduced motion: instant flings, tucks and settles, no autoplay; dragging still works.

Esta página se tradujo con IA. Informar de un problema de traducción