Components / Motion / Horizontal Reel

Horizontal Reel beta

Scroll down, travel sideways: vertical scrolling drives a horizontal strip of cards.

Preview
01 · CastOne mold for every part
02 · CheckThe mold inspects
03 · ComposeBlocks from parts
04 · ThemeFive accents, one core
05 · ShipCopy, paste, own it
06 · AgainThe mold remembers

Scroll inside the box. Down becomes sideways.

<div class="k-reel" data-reel>
  <div class="k-reel-space">
    <div class="k-reel-view"><div class="k-reel-strip">
      <div class="k-reel-card"><b>01 · Cast</b><span>One mold for every part</span></div><div class="k-reel-card"><b>02 · Check</b><span>The mold inspects</span></div><div class="k-reel-card"><b>03 · Compose</b><span>Blocks from parts</span></div><div class="k-reel-card"><b>04 · Theme</b><span>Five accents, one core</span></div><div class="k-reel-card"><b>05 · Ship</b><span>Copy, paste, own it</span></div><div class="k-reel-card"><b>06 · Again</b><span>The mold remembers</span></div>
    </div></div>
  </div>
</div>
<p style="font-size:12.5px;color:var(--k-ink-3);margin-top:9px">Scroll inside the box. Down becomes sideways.</p>
/* KATA Horizontal Reel — down becomes sideways. */
.k-reel{height:236px;overflow:auto;border:1px solid var(--k-line);border-radius:14px;background:var(--k-bg);font-family:var(--k-font);color:var(--k-ink)}
.k-reel-space{height:290%;position:relative}
.k-reel-view{position:sticky;top:0;height:236px;overflow:hidden;display:flex;align-items:center}
.k-reel-strip{display:flex;gap:13px;padding:0 18px;will-change:transform}
.k-reel-card{flex:none;width:196px;height:148px;border:1px solid var(--k-line);border-radius:13px;background:var(--k-bg-soft);padding:16px;display:flex;flex-direction:column;justify-content:flex-end;gap:5px}
.k-reel-card b{font-size:14.5px;font-weight:650}
.k-reel-card span{font-size:12px;color:var(--k-ink-2)}
// KATA Horizontal Reel — the scroll fraction, pointed sideways.
document.querySelectorAll('[data-reel]').forEach(function (root) {
  var strip = root.querySelector('.k-reel-strip');
  var view = root.querySelector('.k-reel-view');
  function update() {
    var max = root.scrollHeight - root.clientHeight;
    var p = max > 0 ? root.scrollTop / max : 0;
    var travel = Math.max(0, strip.scrollWidth - view.clientWidth + 18);
    strip.style.transform = 'translateX(' + (-p * travel).toFixed(1) + 'px)';
  }
  root.addEventListener('scroll', update, { passive: true });
  update();
});

Installation

CLI

node kata-ui.mjs add horizontal-reel

Writes the part’s CSS and JS into your project. New to the CLI? See the CLI doc.

Manual

  1. Load tokens.css once, before any part.
  2. Copy the CSS tab into your stylesheet.
  3. Copy the HTML where it belongs. The part has a script — include it after the markup; it initializes itself.

Usage

The page’s scroll becomes the strip’s journey: a tall spacer, a sticky window, and a script that maps scroll fraction to sideways travel. In the wild it runs full-bleed sections; here it is boxed so the trick can be studied. Use it for the one gallery that deserves a journey — not for navigation.

Accessibility

The mapping is one-to-one with the scrollbar, so position is never a mystery, and every card is ordinary content in reading order. Under prefers-reduced-motion the strip becomes a plain horizontally scrollable row — the journey, democratized.

Anatomy

The public surface of this part, read from its own source. Class names are the API — the CSS tab above is the full contract.

.k-reelclass
.k-reel-spaceclass
.k-reel-viewclass
.k-reel-stripclass
.k-reel-cardclass
data-reeldata hook

Related parts