Skip to content

Marquee

Loop supporting content horizontally or vertically, pausing on hover and focus.

Use it for supporting content that works as ambience: a wall of customer logos, a column of testimonials. Don't put anything people need to read or act on in a loop, like announcements, prices or the only link to a page. Show those in a static list.

Installation

npm i @youj/motion

import { Marquee } from '@youj/motion'

For an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.

Usage

import { Marquee } from '@youj/motion'

export function Customers() {
  return (
    <Marquee speed={40} fade={64}>
      <Logo name="Northwind" />
      <Logo name="Halcyon" />
      <Logo name="Vertex" />
    </Marquee>
  )
}

Marquee measures its content, repeats it just enough times to fill the track plus one, and moves the row by exactly one copy's length per cycle, so the seam never shows. It measures again when the track or the content resizes.

Examples

Direction and speed

direction is left, right, up or down. speed is in pixels per second at normal speed, so a long row and a short row move at the same pace. Two rows running opposite ways, at slightly different speeds, give a logo wall depth without drawing the eye.

gap spaces the items and the copies, and fade masks each edge over that many pixels. fade={0} turns the mask off.

Reference

Propschildren, direction, speed… (6)

Props

Marquee props
PropType
childrenRequiredReactNode–
directionMarqueeDirection
'left''right''up''down'

Which way the content travels. Left and right follow the reading direction: under RTL the row mirrors and they swap.

default 'left'

'left'
speednumber

Pixels per second at time scale 1.

default 40

40
pauseOnHoverboolean

Ease to a stop while hovered. Keyboard focus inside always stops it and brings the focused item into view.

default true

true
fadenumber

Width of the faded edges in px. 0 disables.

default 48

48
gapnumber

Space between items and between copies, in px.

default 48

48
Other props pass through: extends HTMLAttributes<HTMLDivElement>.
MotionLoop, ease to a stop and back, focused item into view… · reduced motion

Motion

Motion tokens
MomentTokens
Loopconstant velocity; divided by the time scale
no token

`speed` px/s, default 40

`speed` px/s, default 40
Ease to a stop and backhover; the time constant of an exponential ease
duration.slow

340ms

340ms
Focused item into viewfocus within stops the loop at once; the item eases clear of the fade
duration.base

220ms

220ms
Frame clampthe longest step a single frame can take
duration.instant

80ms

80ms

Reduced motion

One static row that wraps, or a scrollable column when vertical. No copies, no mask and no movement.

Reduced motion

Nothing moves. A horizontal marquee renders its content once, as a centered row that wraps onto new lines, and a vertical one becomes a column that scrolls. The edge fade is dropped, so every item is fully visible.

Design the rest of the section for it too. With nothing to pause, the first preview drops its Pause button, and it drops the second row, which would only repeat the first. Read useYouj().reduced to make choices like these.

AccessibilityKeyboard, ARIA, focus

Accessibility

  • One copy of the content is in the accessibility tree. The copies that fill the loop are aria-hidden and inert, so links inside them are never read or tabbed to twice.
  • A second row that repeats the first should be hidden too, with aria-hidden on the Marquee, as in the first preview.
  • Keyboard focus inside the marquee stops it, and Tab onto a link slides that link fully into view, so focus never lands on a half-hidden item. Pair it with a visible pause control for anything longer than a few seconds.
  • The edge fade is a mask. It reaches a few pixels past the row on the other axis, so an item's own focus ring shows; a ring around the whole marquee goes on a wrapper, as the vertical preview does with has-focus-visible:ring-2.
  • Don't put essential links or information only in a marquee: moving targets are hard to read and hard to hit.