VisorVisor
ComponentsData Display

Marquee

A multi-band counter-flow infinite-scroll primitive for continuous animated content strips.

Single Band

The simplest usage: pass items and optionally a separator glyph. Items are duplicated internally for a seamless loop.

Counter-Flow Dual Band

Pass a bands array with per-band direction, durationSec, and separator. Opposite directions create the counter-flow pattern common in "trusted by" sections.

Marketing-Display Scale (Descenders + Calm Speed)

At marketing-display font sizes, items with descender glyphs (g, y, p, q, j) require the default line-height: var(--line-height-normal) to clear the band's overflow boundary. The default durationSec of 40 provides a calm, readable scroll at display scale — pass a lower value for body-text tickers. Scale typography via the --marquee-item-font-size / --marquee-item-font-weight slots.

Custom Render

Use renderItem to render rich nodes — logos, avatar groups, or styled cards — instead of plain text.

Installation

npx visor add marquee

This copies two files into your project:

  • components/ui/marquee/marquee.tsx — the component
  • components/ui/marquee/marquee.module.css — the styles

Usage

import { Marquee } from '@/components/ui/marquee/marquee';

// Single-band shorthand
<Marquee
  items={['Acme Corp', 'Vercel', 'Figma']}
  durationSec={20}
  separator="·"
/>

// Multi-band counter-flow
<Marquee
  bands={[
    { items: clientNames, direction: 'left',  durationSec: 25, separator: '' },
    { items: clientNamesReversed, direction: 'right', durationSec: 30, separator: '' },
  ]}
  pauseOnHover
  gap="2.5rem"
/>

Accessibility

  • All tracks are wrapped in aria-hidden="true" — the scrolling content is decorative. Add meaningful context via aria-label on the root element.
  • Pause-on-hover (pauseOnHover, default true) lets users read content without time pressure.
  • prefers-reduced-motion: reduce disables the animation entirely, rendering items statically.

API Reference

MarqueeProps

No props data available for “marquee”.

MarqueeBand

PropTypeDefaultDescription
itemsReactNode[]Items to display in this band
direction"left" | "right""left"Scroll direction
durationSecnumber40Duration of one full cycle in seconds. Tuned for marketing-display sizes; pass a lower value (e.g. 20) for ticker-style strips.
separatorReactNode | stringSeparator rendered between items

Customization Slots

The marquee root (.root) exposes the following CSS custom properties. Defaults resolve to the body-text style used by ticker strips — override any of them in the root style (or a parent token-override scope) to retheme without forking the component.

SlotDefaultPurpose
--marquee-item-colorvar(--text-primary)Item text color
--marquee-item-font-sizevar(--font-size-sm)Item font size
--marquee-item-font-weightvar(--font-weight-medium)Item font weight
--marquee-item-line-heightvar(--line-height-normal)Item line-height (also applied to separator)
--marquee-item-letter-spacingnormalItem letter-spacing
--marquee-separator-colorvar(--text-tertiary)Separator color
--marquee-separator-font-sizevar(--marquee-item-font-size)Separator font size — defaults to item size