Marquee
An endless rhythm of words, logos, or cards with just the right amount of energy.
THOUGHTS IN CONSTANT MOTION
STAY CURIOUS ✳
MAKE SOMETHING ↗
A little momentum goes a long way.Hover to pause.
Live component. Go ahead, interact.
REACT / TYPESCRIPT
'use client';
import { useState, type CSSProperties, type ReactNode } from 'react';
import './marquee.css';
export interface MarqueeProps {
children: ReactNode;
/** Duration of one loop, in seconds. */
speed?: number;
reverse?: boolean;
pauseOnHover?: boolean;
className?: string;
}
/** A seamless strip with one accessible copy and a persistent pause control. */
export function Marquee({
children,
speed = 24,
reverse = false,
pauseOnHover = true,
className = '',
}: MarqueeProps) {
const [paused, setPaused] = useState(false);
const duration = Number.isFinite(speed) && speed > 0 ? speed : 24;
const style = { '--ac-marquee-duration': `${duration}s` } as CSSProperties;
return (
<div
className={`ac-marquee ${className}`.trim()}
style={style}
data-reverse={reverse}
data-paused={paused}
data-pause-on-hover={pauseOnHover}
>
<div className="ac-marquee__track">
<div className="ac-marquee__group">{children}</div>
<div
className="ac-marquee__group ac-marquee__duplicate"
aria-hidden="true"
ref={(element) => { element?.setAttribute('inert', ''); }}
>
{children}
</div>
</div>
<button
className="ac-marquee__toggle"
type="button"
onClick={() => setPaused((value) => !value)}
aria-label={paused ? 'Resume scrolling' : 'Pause scrolling'}
>
<span aria-hidden="true">{paused ? '↗' : 'Ⅱ'}</span>
</button>
</div>
);
}
export default Marquee;
01
Installation
Run this in your shadcn-configured React project. The component and stylesheet are added together.
TERMINAL
npx shadcn@latest add http://localhost:5173/r/marquee.jsonThis command uses the current site address. For localhost, keep this preview running on the same computer. Files follow your configured components alias.
02
Usage
Import the component and make the details your own.
TypeScript
import { Marquee } from '@/components/anticraft/Marquee';
export function Example() {
return (
<Marquee speed={24} pauseOnHover>
<span>Independent spirit</span>
<span>Thoughtful details</span>
<span>Free to create</span>
</Marquee>
);
}03
Props
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | Required | Items to repeat. One copy is exposed to assistive technology. |
speed | number | 24 | The duration of a complete loop in seconds. Lower values scroll faster. |
reverse | boolean | false | Reverses the scrolling direction. |
pauseOnHover | boolean | true | Pauses while the pointer hovers. Keyboard focus always pauses the strip. |
className | string | '' | Additional classes for the outer strip. |
04
Motion & accessibility
Considered by default.
A persistent pause button and focus pause give users control. Reduced motion removes the animation and duplicate content, leaving a static, wrapping strip.
Use semantic content and meaningful labels in your composition. Check contrast and keyboard behavior with your own styles and content.