Gallery✨ Premium

Vertical Marquee

A column of lines that rises gently inside a fixed-height window, in a continuous loop, stopping on hover or with a Pause button.

CSSVertical

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Gallery has 44 effects, including 6 free. Explore the category →

3 usage examples

Latest sign-ups for a trail race — a box beside the registration page — Vertical Marquee example 1

① Latest sign-ups for a trail race — a box beside the registration page

WhenOn the Saunevre Ridge Trail page, the right-hand column (108 px wide) raises the latest runners with their town and how long ago they signed up, next to the distance, elevation, bib gauge and “Sign up” button.
WhySeeing other runners register reassures people and nudges them to do it, with no pop-up or pushy counter. The narrow column takes almost nothing from the page, and the Pause button, placed in the box title, leaves the names their full width.
Settingsdata-speed="16", --vtmq-window: 238px, --vtmq-align: left, --vtmq-size: 11.5px, --vtmq-space: 12px, --vtmq-height: auto, --vtmq-bg: transparent; button moved to the box's top right by the page stylesheet.
Teleprompter in a video recording app — the script rises in the middle of the screen — Vertical Marquee example 2

② Teleprompter in a video recording app — the script rises in the middle of the screen

WhenIn the Parlhève video studio, during take 3 of a participatory budget presentation, the speech rises in 20 px lines in the middle of the screen, between two orange reading marks; the bottom bar shows the speed, the camera thumbnail and the “Stop take” button.
WhyThe most direct use of the effect: continuous scrolling at a fixed speed in pixels per second, set to the speaker's pace, and a Pause button to catch your breath. At the end, the text starts again from the top, which helps when rehearsing before the take.
Settingsdata-speed="24", --vtmq-window: 232px, --vtmq-size: 20px, --vtmq-space: 14px, --vtmq-color: #f4f1ea, --vtmq-button-size: 30px, --vtmq-height: auto, --vtmq-bg: transparent; hover stop turned off in this scene (pointer-events: none on .vtmq-viewport) so a mouse resting on the screen does not freeze the text: only the Pause button stops it; button placed in the control bar, top and bottom fades added by the page stylesheet.
Unanswered topics on a gardening forum — a box of links next to the discussion — Vertical Marquee example 3

③ Unanswered topics on a gardening forum — a box of links next to the discussion

WhenOn the Fanerolle gardeners' help forum, to the right of a thread about drip irrigation, the “Unanswered” box raises six waiting questions (tomatoes turning black, pruning raspberries, nettle tea…), each with its section and age; every question is a link.
WhyThe box invites people to help without taking over the discussion: you spot a question you can answer, resting the mouse stops the column and a click opens the topic. With the keyboard, Tab visits the six links, column held still.
Settingsdata-speed="30", --vtmq-window: 206px, --vtmq-align: left, --vtmq-size: 11.5px, --vtmq-space: 8px, --vtmq-color: #23412b, --vtmq-button-size: 20px, --vtmq-height: auto, --vtmq-bg: transparent; an <a href> styled as a white card in each <li>, button placed in the box title by the page stylesheet.

How it works

The .vtmq-viewport window is 120 px tall (--vtmq-window) and hides whatever overflows. Inside, the .vtmq-group list holds your lines, written once. The script compares the list's height with the window's and stacks below it as many duplicates as needed to keep the window full, even with only three lines or a window enlarged to 400 px.

The track rises by one list height, then the vtmq-scroll animation starts over: the line that reappears at the bottom is a duplicate, at the very spot of the original, so the loop never jerks. Speed is set in pixels per second in data-speed (20 by default, i.e. one 39 px line every 2 seconds); data-direction="down" makes the column go down.

Lines are centred (--vtmq-align also takes left and right); the old version stuck them to the left edge of the area. If line heights change (font loaded late, text wrapping in a narrow column), a ResizeObserver redoes the maths; if a script edits the list, the duplicates are rebuilt.

Accessibility

  • Stopping: the column stays still while the mouse is over it, and the round Pause button (aria-pressed) freezes it until the next click. That is what WCAG 2.2.2 asks for automatic motion lasting over 5 seconds.
  • Speech output: duplicates are marked aria-hidden="true"; a screen reader announces the list of lines once, like any list (before the repair: twice).
  • Keyboard: a link inside a line only takes focus in the original list; the column then goes back to its starting position, stops and scrolls the window up or down to that link. It moves again once focus leaves.
  • Reduced motion: under prefers-reduced-motion: reduce, the column no longer moves on its own, shows only the original list and becomes a small area you scroll with a finger or a wheel; when the list overflows, the window also takes keyboard focus (Tab) and scrolls with the arrow keys.
  • Contrast: cyan #22d3ee on #0a0a0f reaches 10.9:1.

Browser compatibility

Vertical CSS animation (translateY towards a variable), 4.5 KB of commented JavaScript, no dependency. ResizeObserver: Chrome 64, Firefox 69, Safari 13.1; prefers-reduced-motion: Chrome 74, Firefox 63, Safari 10.1. Measured in Chromium 151, Firefox 153 and WebKit 26.5: window always full, hover and button stop the column, two independent columns on one page, 0 errors.

Chrome 74+✓ Full (measured)
Firefox 69+✓ Full (measured)
Safari 13.1+✓ Full (WebKit engine measured)
Edge 79+✓ Full (Chromium engine)
Mobile iOS 13.4+✓ Full (WebKit engine measured)
Android Chrome 74+✓ Full (Chromium engine)

Without JavaScript, the window shows the list standing still and can be scrolled by hand. In Safari before 13.1 (no ResizeObserver), the number of duplicates is only recomputed when the browser window is resized.

The code

HTML structure to paste into your page (the CSS + JS come with your purchase):

index.html — structure
<div class="fx-0189-root" data-speed="20" data-direction="up">
  <div class="vtmq-band">
    <div class="vtmq-viewport">
      <div class="vtmq-track">
        <ul class="vtmq-group" role="list">
          <li>First line</li>
          <li>Second line</li>
        </ul>
      </div>
    </div>
    <button type="button" class="vtmq-toggle" aria-pressed="false" aria-label="Pause"></button>
  </div>
</div>

One-time payment, no subscription — the code is yours forever

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Lines (HTML — the <li> of .vtmq-group) 4 lines Text, links, pills or small images, written once. The script makes the duplicates.
data-speed 20 Pixels per second. Between 12 and 25 so a line can be read before it leaves.
data-direction up down: the column goes down.
--vtmq-window 120px Height of the visible window.
--vtmq-align center Line alignment: left, center or right.
--vtmq-color / --vtmq-size / --vtmq-space #22d3ee / 1.25rem / 1rem Colour, text size and space between two lines (the same at the seam).
--vtmq-height / --vtmq-bg / --vtmq-padding 180px / #0a0a0f / 1rem 0 Demo frame; auto, transparent and 0 to fit it into a card.
--vtmq-button / --vtmq-button-size rgba(255, 255, 255, .8) / 2rem Colour and size of the Pause button.

FAQ

Yes: the script measures the whole list's height, not one line's. A line wrapping onto two rows or a taller pill does not break the loop.
That is not what this effect does: the column rises continuously. Step-by-step scrolling (one line, a pause, the next) needs a different animation, such as stepped @keyframes written for a fixed number of lines.
No: it stays stopped until the button is clicked again (its state is announced through aria-pressed). Only the hover stop ends when the mouse leaves.