Features
- Seamless loops with no visible gaps or jumps
- Auto-pauses when off-screen to save resources
- Switches direction at breakpoints via CSS
- Built-in pause and slow hover effects
- Smart content cloning for continuous scrolling
Setup
Copy & paste this script Before </body> tag.
<!-- GSAP Core -->
<script src="https://cdn.jsdelivr.net/npm/gsap@3.13.0/dist/gsap.min.js"></script>
<!-- Divs Marquee Library -->
<script src="https://cdn.jsdelivr.net/gh/idreezus/divs@v1.0.0/dist/marquee/v1.0.0/marquee.min.js"></script>On Webflow, you can enable GSAP site-wide by opening up your site and clicking Settings → GSAP → Enable GSAP. I recommend doing this if not already.
If you go this route, don't include the first script below for GSAP Core.
Skip this step if using one of the variants below.
Otherwise, mark your container with [data-marquee="true"] and each item with [data-marquee-item="true"]
<div data-marquee="true" class="my-marquee">
<div data-marquee-item="true">Item 1</div>
<div data-marquee-item="true">Item 2</div>
<div data-marquee-item="true">Item 3</div>
</div>Skip this step if using one of the variants below.
For horizontal scrolling, use display: flex with flex-direction: row. For vertical scrolling, use display: flex with flex-direction: vertical and some kind of height constraint. Examples:
/* Horizontal marquee */
.my-marquee {
display: flex;
flex-direction: row;
overflow: hidden;
gap: 2rem;
}
/* Vertical marquee */
.my-vertical-marquee {
display: flex;
flex-direction: column;
height: 24rem;
overflow: hidden;
gap: 1rem;
}The marquee auto-initializes when the page loads.
How It Works
Everything about this component is based on the flexbox. This library was designed with Webflow's visual development in mind – I want to see how my marquee looks without surpises. You control the visual behavior by:
- Control orientation simply by setting
flex-direction - Add space between items by using
gap - Size individual items using your typical CSS
The library handles the animation complexity behind the scenes, and GSAP powers the animation timelines and allows for fancy things like speed ramped hover effects.
But here's my favorite part: you can start off in one direction and automatically "flip" at any breakpoint. The library detects the switch and rebuilds the animation to match your new layout.
This came about because vertical infinite marquees are kinda obnoxious on mobile screens. With previous marquee libraries, if you wanted to swap directions at an arbitrary breakpoint, you'd have to duplicate your marquee twice and use display: hidden (meaning you really had two individual marquees that are shown/hidden at different times).
But that's hacky and pollutes the DOM. It's the type of thing that would've made Steve Jobs mad if he was a web developer (or alive).
Responsive Directions
Direction is controlled entirely through CSS flex-direction. Here's an example of a responsive layout that goes from vertical → horizontal on smaller screens.
/* Vertical on desktop */
.my-marquee {
display: flex;
flex-direction: column;
height: 36rem;
overflow: hidden;
}
/* Switch to horizontal on mobile */
@media (min-width: 768px) {
.my-marquee {
flex-direction: row;
height: auto;
}
}Use CSS gap instead of margins for spacing. The library measures gaps to ensure seamless loops without visible seams.
The library detects uses ResizeObserver to detect direction changes during window resize events and rebuilds the animation. Previous playback position is preserved proportionally, so users don't experience jarring jumps when resizing their browser or rotating their device.
Reversing Direction
To animate the marquee in the opposite direction, add [data-marquee="reverse"] to the container element:
<div class="my-marquee" data-marquee="true" data-marquee-reverse="true">
<!-- your items here -->
</div>Hover Effects
Add interactive hover effects with simple attributes on your container element. All hover effects use smooth easing curves and customizable timing.
Slow Effect
To reduce speed to a slower sustained pace during hover, add [data-marquee-effect="slow"] to the container element:
<div data-marquee="true" data-marquee-hover-effect="slow">
<!-- your items here -->
</div>Pause Effect
To ramp speed down to a complete stop when hovering, add [data-marquee-effect="pause"] to the container element:
<div data-marquee="true" data-marquee-hover-effect="pause">
<!-- your items here -->
</div>Trigger Area
By default, hovering anywhere on the container triggers the effect. If you want the effect to only trigger when a specific item is hovered instead (maybe because you have huge gaps between items so the container has lots of "empty" space):
<div
data-marquee="true"
data-marquee-hover-effect="pause"
data-marquee-hover-trigger="items"
>
<!-- your items here -->
</div>Spacing & CSS Gaps
The library measures spacing between items to ensure seamless loops. For reliable results, use the CSS gap property on your container:
.my-marquee {
display: flex;
gap: 2rem; /* your spacing between items */
}The library attempts to measure margins and padding, but gap provides the most consistent and predictable spacing. The spacing calculation uses the median gap between items, which feeds into clone positioning and timeline duration to prevent visible seams at the loop point.
Auto-Cloning
To create infinite loops, items are cloned to create continuous loops. This is done automatically. The library smartly calculates the minimum amount of clones needed based on:
- Container dimensions (width for horizontal, height for vertical)
- Total content size
- Spacing between items
Cloned elements are marked with [aria-hidden-"true"] so screen readers announce the original content once without any of the clones.
Clone count is capped at 10 sets maximum for performance.
Disabling Auto-Clone
If you want to manage cloning manually:
<div data-marquee="true" data-marquee-auto-clone="false" class="my-marquee">
<!-- manually duplicate your items here -->
</div>Disabling auto-clone requires you to manually duplicate content enough times to fill the container and create a seamless loop. Use this only if you need precise control over the DOM structure.
Custom Clone Count
Override the automatic calculation:
<div data-marquee="true" data-marquee-clones="3" class="my-marquee">
<!-- your items here -->
</div>For these next two ones, try resizing the frame to see how it automatically changes from vertical to horizontal at the mobile landscape breakpoint. Isn't that cool?
It's resize time again. Try resizing the frame to see it these vertical ones become horizontal at the mobile landscape breakpoint.
Customization
Core Attributes
All configuration happens through data-marquee-* attributes on the container element:
| Attribute | Values | Default | Description |
|---|---|---|---|
data-marquee | "true" | - | Required to initialize the marquee |
data-marquee-item | "true" | - | Required on each child element to animate |
data-marquee-speed | number | 0.6 | Speed multiplier where 1.0 ≈ 100px/second (higher = faster) |
data-marquee-reverse | "true" | - | Reverses the animation direction |
data-marquee-repeat | number | -1 | Number of loop cycles. -1 = infinite, 0 = play once, 5 = loop 5 times |
Hover Effects
Fine-tune hover interactions with these attributes:
| Attribute | Values | Default | Description |
|---|---|---|---|
data-marquee-hover-effect | "pause" / "slow" | - | Type of hover effect |
data-marquee-hover-trigger | "container" / "items" | "container" | Where hover is detected |
data-marquee-hover-speed | 0-1 | 0.3 (slow)0.1 (pause) | Target speed during hover as fraction of normal |
data-marquee-hover-duration | seconds | 0.4 | Total ramp duration for pause effect |
data-marquee-hover-in | seconds | 0.7 | Slow effect ramp-in duration |
data-marquee-hover-out | seconds | 0.25 | Slow effect ramp-out duration |
data-marquee-hover-ease-in | GSAP ease | "power1.out" | Ramp-in easing function |
data-marquee-hover-ease-out | GSAP ease | "power1.out" | Ramp-out easing function |
Performance
IntersectionObserver
By default, marquees pause when scrolled out of the viewport to improve performance. Control this behavior with:
| Attribute | Values | Default | Description |
|---|---|---|---|
data-marquee-intersection | "true" / "false" | "true" | Pause when out of viewport |
Cloning Performance
Reduce clone count if you have many large items or complex layouts:
| Attribute | Values | Default | Description |
|---|---|---|---|
data-marquee-auto-clone | "true" / "false" | "true" | Enable automatic cloning |
data-marquee-clones | number (1-10) | Auto-calculated | Number of clone sets to append |
JavaScript API
Control marquees programmatically through the global window.Marquee object. The library auto-initializes on page load, but you can also manage instances manually.
Initialization
// Initialize all marquees (called on DOMContentLoaded)
window.Marquee.init();
// Initialize with custom selector
window.Marquee.init(".custom-marquee-class");Getting Instances
// Get single instance
const marquee = window.Marquee.get(element);
// Get all instances (returns array)
const allMarquees = window.Marquee.getAll();
// Check if element has a marquee
const hasMarquee = window.Marquee.has(element);Control Methods
All batch methods are chainable:
// Instance control
const marquee = window.Marquee.get(element);
marquee.play();
marquee.pause();
marquee.destroy();
// Batch control
window.Marquee.pauseAll();
window.Marquee.playAll(".my-marquees");
window.Marquee.destroyAll(".old-marquees");
// Manual direction refresh (useful after dynamic CSS class changes)
window.Marquee.refresh(element);
window.Marquee.refreshAll();
// Chaining
window.Marquee.pauseAll(".slow").playAll(".fast").refreshAll();Dynamic Content Updates
When adding or removing items after initialization, destroy and reinitialize:
const container = document.querySelector("[data-marquee]");
const instance = window.Marquee.get(container);
// Destroy existing instance
instance.destroy();
// Update content
container.innerHTML = "..."; // Add new items with data-marquee-item="true"
// Reinitialize
window.Marquee.init();If you change CSS classes that affect flex-direction outside of a resize event, manually trigger window.Marquee.refresh(element) to rebuild the animation with the new direction.
Accessibility
The library includes built-in accessibility features to ensure marquees work well for all users. Cloned items are marked with aria-hidden = "true" so screen readers announce the original content once, preventing repetitive announcements. The library respects the prefers-reduced-motion media query by reducing animation speed to 10% for users who have indicated a preference for reduced motion. All semantic HTML structure is preserved, ensuring assistive technologies can navigate and understand the content regardless of animation state.
FAQ
Why is my spacing inconsistent or showing gaps at the loop point?
When spacing between items, the library works best with the CSS gap
property. Use that while trying to avoid using margins and padding on the
container or items. The library will try to measure margins and padding, but
no guarantees.
Can I switch between horizontal and vertical at different breakpoints?
Yes. The marquee direction is controlled entirely by CSS flex-direction so
you can change it at any breakpoint. For example, use flex-direction: column
on desktop and switch to flex-direction: row at a mobile breakpoint. The
library will detect the change during resize and rebuilds the animation to
match the new layout.
Why isn't my vertical marquee working?
Vertical marquees need a height constraint on the container. Use either
height or max-height. As long as the total height of all cloned content
exceeds the container's height, the marquee will work. Without a height
constraint, the flex container expands to fit all content, leaving nothing to
scroll.
I followed instructions but my marquee isn't working. What's wrong?
Check your browser console. The library logs clear warning messages when issues are detected (missing GSAP, invalid selectors, items with zero dimensions, incorrect CSS properties, etc). Failed instances are caught individually, so one problematic marquee won't prevent others from initializing. Each error message includes context about what went wrong and how to fix it.
What happens if I dynamically change the marquee's CSS class?
If the CSS class change affects flex-direction, manually trigger
window.Marquee.refresh(element) to rebuild the animation. The library
detects direction changes during window resize events, but dynamic class
changes outside of resize require manual refresh.