Coverflow

커버플로

A gallery where the center card faces forward at full size while the ones beside it tilt away in perspective, shrinking with distance — made famous by old iTunes' album-cover browser.

Also known as: Cover flowPerspective carousel
···
html
<div class="cfwrap"><div class="cf" id="cf"></div></div>
css
.cfwrap{perspective:900px;width:88%;height:70%;display:grid;place-items:center}
.cf{position:relative;width:100%;height:100%}
.card{position:absolute;top:50%;left:50%;width:clamp(46px,20vmin,90px);height:clamp(66px,28vmin,128px);
  margin:calc(clamp(66px,28vmin,128px) / -2) 0 0 calc(clamp(46px,20vmin,90px) / -2);
  border-radius:8px;background:var(--surface);border:1px solid var(--line);box-shadow:0 10px 24px rgba(0,0,0,.18);
  transition:transform .6s cubic-bezier(.2,.8,.2,1), opacity .6s;
  display:grid;place-items:center;font:800 18px/1 sans-serif;color:var(--muted)}
js
const cf = document.getElementById('cf');
const N = 5;
const cards = [];
for (let i = 0; i < N; i++) {
  const c = document.createElement('div');
  c.className = 'card';
  c.textContent = String(i + 1);
  cf.appendChild(c);
  cards.push(c);
}
let active = 0;
function render() {
  cards.forEach((c, i) => {
    const off = i - active;
    const abs = Math.abs(off);
    const rot = Math.max(-1, Math.min(1, off)) * 48;
    const tx = off * 44;
    const tz = -abs * 60;
    const sc = abs === 0 ? 1 : 0.72;
    c.style.transform = 'translateX(' + tx + 'px) translateZ(' + tz + 'px) rotateY(' + (-rot) + 'deg) scale(' + sc + ')';
    c.style.opacity = abs > 2 ? '0' : String(1 - abs * 0.22);
    c.style.zIndex = String(100 - abs);
    c.style.color = abs === 0 ? 'var(--accent)' : 'var(--muted)';
  });
}
render();
setInterval(() => {
  active = (active + 1) % N;
  render();
}, 1500);

Cards are placed by computing each one's offset (= card index − active index) from a single active index. The card at offset 0 faces forward — rotateY(0), scale(1) — while cards farther out (±1, ±2…) tilt via rotateY(±45–60°), shift sideways with translateX, and shrink and fade with distance. Perspective on the parent is non-negotiable — without it, rotateY just squashes a card into a flat rectangle.

The trick is stacking order — a card closer to center must always render on top for the overlap to read correctly. Adding depth with translateZ(−|offset| × k) actually moves each card farther from the camera, so the browser's own depth sorting handles the order for you.

When the active index changes, updating every card's transform at once inside a single CSS transition produces the "flip through" animation automatically — no need to animate each card individually.

When to use

Use it for galleries where one item is the focus while the next/previous peek in — albums, portfolios. If there are many items and users need to scan fast, a grid works better; coverflow only shows one item seriously at a time.