Scroll Velocity — <mv-scroll-velocity>

Rows of large text that scroll infinitely; their speed and direction follow the (spring-smoothed) scroll velocity, and the text skews on fast scrolls.

CategoryScroll
TypeWeb Component (<mv-scroll-velocity>)
Statusstable
Keywordsmarquee, velocity, infinite, headline, skew, ticker

When to use

Avoid when

Install

node scripts/add.mjs scroll-velocity --out ./src/marvelous

AI agent with the Marvelous UI MCP server: install_components({ slugs: ["scroll-velocity"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).

Files copied (dependencies included): tokens/tokens.css, core/base.css, core/dom.js, core/element.js, core/motion.js, components/scroll-velocity/scroll-velocity.js, components/scroll-velocity/scroll-velocity.css.

Usage

Canonical markup — start from it and customize with attributes, data-* and CSS variables:

<div id="mv-sv-demo" class="mv-scroll-area" data-orientation="vertical" tabindex="0" aria-label="Scroll velocity demo">
  <style>
    #mv-sv-demo { width: min(100%, 56rem); height: 400px; margin-inline: auto; border: 1px solid var(--mv-border); border-radius: var(--mv-radius-xl); background: var(--mv-bg); }
    #mv-sv-demo .runway { height: 1600px; }
    #mv-sv-demo .stick { position: sticky; top: 0; height: 398px; display: grid; align-content: center; gap: 1.25rem; }
    #mv-sv-demo .hint { display: flex; justify-content: space-between; padding: 0 1.5rem; font-size: .78rem; color: var(--mv-fg-muted); }
    #mv-sv-demo .hint kbd { font: 600 .7rem var(--mv-font-mono); padding: .1rem .4rem; border: 1px solid var(--mv-border); border-bottom-width: 2px; border-radius: .3rem; background: var(--mv-bg-subtle); }
    #mv-sv-demo .sep { color: var(--mv-accent); }
  </style>
  <div class="runway">
    <div class="stick">
      <div class="hint"><span>Scroll fast inside this frame — then scroll back up</span><span>speed <kbd>× scroll</kbd></span></div>
      <mv-scroll-velocity velocity="55">
        <p>Web Components <span class="sep">✦</span> Zero dependencies <span class="sep">✦</span></p>
        <p data-variant="outline">Accessible by default <span class="sep" style="-webkit-text-stroke:0;color:var(--mv-accent)">✦</span> Light &amp; dark <span class="sep" style="-webkit-text-stroke:0;color:var(--mv-accent)">✦</span></p>
        <p data-variant="gradient">Scroll-driven · Light DOM · Layered CSS ·</p>
      </mv-scroll-velocity>
      <div class="hint"><span>Rows alternate direction</span><span>Reduced motion: static text</span></div>
    </div>
  </div>
</div>

API

Attributes

NameTypeDefaultDescription
velocitynumber (px/s)60Base speed of the rows at rest.
factornumber5Acceleration added by scrolling (multiplier per 1000 px/s).
skewnumber (deg)10Maximum skew on fast scrolls (0 to disable).
scrollerselector | windowContainer whose velocity is tracked. Default: the nearest scroll container, or the page.
data-direction1 | -1On a row: direction (rows alternate by default).
data-velocitynumber (px/s)On a row: its own base speed.
data-variantoutline | gradient | mutedOn a row: outlined, accent-gradient or muted text.

Content structure

NameDescription
direct childrenEach child (p, h2, div…) becomes a row; its content is duplicated as often as needed (aria-hidden, inert copies).

CSS classes

NameDescription
mv-scroll-velocity-row / -track / -itemRow (original child), translated track, repeated pattern.

CSS variables

NameDefaultDescription
--mv-scroll-velocity-sizeclamp(2.25rem, 10cqi, 5.5rem)Text size (relative to the component width).
--mv-scroll-velocity-gap0.35emGap between two repeats.
--mv-scroll-velocity-row-gapvar(--mv-space-2)Gap between rows.
--mv-scroll-velocity-strokevar(--mv-fg)Outline color (outline variant).
--mv-scroll-velocity-gradientaccent gradientGradient of the gradient variant.

Accessibility

The original text is read only once: repeats are aria-hidden and inert. Loop via frameLoop: paused off-screen and in hidden tabs. Reduced motion: static rows, fully readable text. No scroll hijacking.