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.
| Category | Scroll |
|---|---|
| Type | Web Component (<mv-scroll-velocity>) |
| Status | stable |
| Keywords | marquee, velocity, infinite, headline, skew, ticker |
When to use
- A bold brand statement or keyword band should react to scroll speed and direction on a landing page
- Oversized outlined or gradient text rows should add energy between sections of an editorial site
Avoid when
- Logos, testimonials or cards should loop at a steady speed regardless of scroll → use Marquee instead
- The text is body copy or information users must read; moving, skewing rows hurt readability
- The page is a dense app or dashboard where constant background motion distracts from the task
Install
node scripts/add.mjs scroll-velocity --out ./src/marvelousAI 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 & 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
| Name | Type | Default | Description |
|---|---|---|---|
velocity | number (px/s) | 60 | Base speed of the rows at rest. |
factor | number | 5 | Acceleration added by scrolling (multiplier per 1000 px/s). |
skew | number (deg) | 10 | Maximum skew on fast scrolls (0 to disable). |
scroller | selector | window | Container whose velocity is tracked. Default: the nearest scroll container, or the page. | |
data-direction | 1 | -1 | On a row: direction (rows alternate by default). | |
data-velocity | number (px/s) | On a row: its own base speed. | |
data-variant | outline | gradient | muted | On a row: outlined, accent-gradient or muted text. |
Content structure
| Name | Description |
|---|---|
direct children | Each child (p, h2, div…) becomes a row; its content is duplicated as often as needed (aria-hidden, inert copies). |
CSS classes
| Name | Description |
|---|---|
mv-scroll-velocity-row / -track / -item | Row (original child), translated track, repeated pattern. |
CSS variables
| Name | Default | Description |
|---|---|---|
--mv-scroll-velocity-size | clamp(2.25rem, 10cqi, 5.5rem) | Text size (relative to the component width). |
--mv-scroll-velocity-gap | 0.35em | Gap between two repeats. |
--mv-scroll-velocity-row-gap | var(--mv-space-2) | Gap between rows. |
--mv-scroll-velocity-stroke | var(--mv-fg) | Outline color (outline variant). |
--mv-scroll-velocity-gradient | accent gradient | Gradient 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.