Components / Motion / Horizontal Reel
Horizontal Reel beta
Scroll down, travel sideways: vertical scrolling drives a horizontal strip of cards.
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
- Load
tokens.cssonce, before any part. - Copy the CSS tab into your stylesheet.
- 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-reel | class |
.k-reel-space | class |
.k-reel-view | class |
.k-reel-strip | class |
.k-reel-card | class |
data-reel | data hook |
Related parts
Accordion Gallery beta
Five scenes in a row; the one you touch opens up and tells its caption.
Aurora beta
A slow wash of color behind a hero — the accent and the core, drifting under glass.
Beam border beta
A single point of light travels the edge of a card. The beam is the core color — the one accent, in motion.
Blur In beta
Words arrive out of focus and sharpen one after another, like eyes adjusting.