Card Stack
介绍
点击或横向拖动最上面的卡片,它会飞出并回到牌堆底部。
纯 HTML、CSS 与少量原生 JS,没有框架依赖。复制提示词交给 Claude Code、Cursor、Trae 或 Codex,模型会把 Card Stack 加进你的项目并换成你的配色。
适用场景
功能介绍、案例与作品、团队成员、文章列表等需要把信息成组展示的区块。
安装
订阅 Builder 后,用 8pt CLI 装进项目,或在已接入 8pt MCP 的编辑器里直接调用。
$ npm i -g @8pt-dev/cli && 8pt add card-stack
mcp › 使用 8pt MCP 调用 get_component("card-stack")
代码
Card Stack 的源码,动效只用 transform 与 opacity,并尊重减少动态效果设置。
<div class="x-cs"> <div class="x-cs__card" style="--c:#4D74FF"><small>Spacing</small><b>8 · 16 · 24 · 32</b><span class="x-cs__bars"><i style="width:8px"></i><i style="width:16px"></i><i style="width:24px"></i><i style="width:32px"></i></span></div> <div class="x-cs__card" style="--c:#00a3ff"><small>Radius</small><b>4 · 8 · 12 · 16</b><span class="x-cs__rad"><i></i><i></i><i></i><i></i></span></div> <div class="x-cs__card" style="--c:#ff4fd8"><small>Type</small><b>12 · 14 · 16 · 24</b><span class="x-cs__type">Aa Aa <em>Aa</em></span></div> <div class="x-cs__card" style="--c:#9bd400"><small>Motion</small><b>160 · 240 · 400 ms</b><span class="x-cs__bars"><i style="width:16px"></i><i style="width:24px"></i><i style="width:40px"></i></span></div> </div>
.x-cs{position:relative;width:220px;height:150px;touch-action:pan-y}
.x-cs__card{position:absolute;inset:0;display:flex;flex-direction:column;gap:6px;padding:18px;border-radius:18px;background:var(--d-card,#101016);border:1px solid var(--d-line,rgba(255,255,255,.12));box-shadow:0 20px 40px -24px rgba(0,0,0,.7);color:var(--d-fg,#fff);cursor:grab;user-select:none;transition:transform .5s cubic-bezier(.2,.9,.2,1),opacity .4s;will-change:transform}
.x-cs__card::before{content:"";position:absolute;inset:0 0 auto;height:3px;border-radius:18px 18px 0 0;background:var(--c)}
.x-cs__card small{font:600 11px/1 ui-monospace,monospace;color:var(--d-muted,#a0a0ab);letter-spacing:.06em;text-transform:uppercase}
.x-cs__card b{font:700 19px/1.2 var(--d-font,system-ui,sans-serif);letter-spacing:-.01em}
.x-cs__bars,.x-cs__rad{display:flex;gap:6px;align-items:flex-end;margin-top:auto}
.x-cs__bars i{height:22px;border-radius:3px;background:var(--c);opacity:.8}
.x-cs__rad i{width:26px;height:26px;background:var(--c);opacity:.8}
.x-cs__rad i:nth-child(1){border-radius:4px}.x-cs__rad i:nth-child(2){border-radius:8px}.x-cs__rad i:nth-child(3){border-radius:12px}.x-cs__rad i:nth-child(4){border-radius:16px}
.x-cs__type{margin-top:auto;font:600 14px/1 var(--d-font,system-ui,sans-serif);color:var(--c)}
.x-cs__type em{font-size:24px;font-style:normal}const init = (root) => {
const box = root.querySelector('.x-cs'); let cards = [...box.children];
const layout = () => cards.forEach((c, i) => { c.style.zIndex = cards.length - i; c.style.opacity = i > 2 ? 0 : 1; c.style.transform = `translateY(${i * -12}px) scale(${1 - i * 0.06})`; });
const fling = (dir) => {
const top = cards[0]; top.style.transform = `translateX(${dir * 260}px) rotate(${dir * 18}deg)`; top.style.opacity = 0;
setTimeout(() => { cards.push(cards.shift()); top.style.transition = 'none'; layout(); top.getBoundingClientRect(); top.style.transition = ''; }, 320);
};
let sx = null, dx = 0;
box.addEventListener('pointerdown', e => { if (e.target.closest('.x-cs__card') !== cards[0]) return; sx = e.clientX; dx = 0; cards[0].style.transition = 'none'; cards[0].setPointerCapture(e.pointerId); });
box.addEventListener('pointermove', e => { if (sx === null) return; dx = e.clientX - sx; cards[0].style.transform = `translateX(${dx}px) rotate(${dx / 14}deg)`; });
box.addEventListener('pointerup', () => { if (sx === null) return; cards[0].style.transition = ''; sx = null; Math.abs(dx) > 70 ? fling(Math.sign(dx)) : Math.abs(dx) < 4 ? fling(1) : layout(); });
layout();
…订阅 Builder 查看完整源码。查看价格