@markwasfy/slider-marquee
v0.2.0
Published
An advanced, themeable, accessible React news ticker / marquee component.
Downloads
298
Maintainers
Readme
- 🔁 Seamless infinite scroll (no visible jump)
- ⚡ Constant speed in px/second, independent of content length
- ⏸️ Configurable pause on hover and pause on focus
- ↔️ Left or right direction
- 🏷️ Pinned label badge (e.g.
UPDATES) with a pulsing status dot - 🎨 Fully themeable via CSS custom properties — no CSS-in-JS dependency
- ♿ Respects
prefers-reduced-motion(falls back to a static, scrollable list); links are keyboard-focusable - 📦 Ships ESM + CJS + TypeScript types

Install
npm install @markwasfy/slider-marqueereact and react-dom (>=17) are peer dependencies.
Usage
import { NewsTicker } from "@markwasfy/slider-marquee";
import "@markwasfy/slider-marquee/styles.css"; // import once, anywhere in your app
export function App() {
return (
<NewsTicker
items={[
"Supply-chain attack hits popular npm pipeline",
"NVIDIA enters the PC race with the RTX Spark superchip",
{ content: "Read the full report →", href: "/report", target: "_blank" },
]}
badge={{ label: "Updates" }}
speed={60}
pauseOnHover
/>
);
}Remember to import the stylesheet once:
import "@markwasfy/slider-marquee/styles.css".
Choosing a component
| Component | Best for | How it works |
| ---------------- | -------------------------------- | ------------ |
| NewsTicker | Typical text lists (a few → a few hundred items) | Duplicates content and animates with a single CSS transform. |
| VirtualTicker | Very large lists (thousands+) | Recycles DOM nodes — only on-screen items exist in the DOM. |
| LogoTicker | Sponsor / "trusted by" logo rows | Image-tuned variant of NewsTicker (logo height, greyscale-on-hover). |
NewsTicker and VirtualTicker take the same props (see below) and look
identical. LogoTicker has its own image-oriented props (see
Logo / sponsor ticker).
import { VirtualTicker } from "@markwasfy/slider-marquee";
import "@markwasfy/slider-marquee/styles.css";
const items = Array.from({ length: 10000 }, (_, i) => ({
id: i,
content: `Headline #${i + 1}`,
}));
<VirtualTicker items={items} badge={{ label: "Live" }} speed={90} />;With VirtualTicker, a 10,000-item list still renders only a handful of nodes
(roughly however many fit on screen, plus a small buffer). It drives the scroll
with requestAnimationFrame, recycling items as they leave/enter the viewport,
so per-frame cost stays constant as the list grows.
Logo / sponsor ticker
LogoTicker is a purpose-built variant for a scrolling row of logos — sponsors,
partners, or a "trusted by" strip. It runs on the same seamless-scroll engine but
takes image-oriented props instead of text items.
import { LogoTicker } from "@markwasfy/slider-marquee";
import "@markwasfy/slider-marquee/styles.css";
<LogoTicker
logos={[
{ src: "/logos/acme.svg", alt: "Acme", href: "https://acme.com" },
{ src: "/logos/globex.svg", alt: "Globex" },
{ src: "/logos/initech.svg", alt: "Initech" },
]}
logoHeight={40}
grayscale // greyscale by default, full colour on hover/focus
speed={40}
/>;A LogoItem is either an image — { id?, src, alt, width?, href?, target? } —
or a custom node: { id?, content, href?, target? } (e.g. for inline SVG).
LogoTicker props
| Prop | Type | Default | Description |
| -------------- | ----------------------------------- | ---------------- | ----------- |
| logos | LogoItem[] | — (required) | The logos to scroll. |
| speed | number | 40 | Scroll speed in pixels per second. |
| direction | "left" \| "right" | "left" | Scroll direction. |
| pauseOnHover | boolean | true | Pause while the pointer is over the strip. |
| pauseOnFocus | boolean | true | Pause while any logo link has keyboard focus. |
| logoHeight | number | 40 | Rendered height of each logo, in px (width auto). |
| gap | number | 48 | Horizontal gap between logos, in px. |
| grayscale | boolean | false | Greyscale logos that fade to full colour on hover/focus. |
| fadeEdges | boolean | true | Fade the left/right edges with a gradient mask. |
| badge | BadgeConfig \| ReactNode \| false | false | Optional pinned label (e.g. "Sponsors"). |
| ariaLabel | string | "Sponsor logos"| Accessible label for the strip. |
| className | string | — | Extra class on the root element. |
| style | CSSProperties | — | Inline styles on the root (also accepts theme CSS vars). |
LogoTicker sits on a transparent background by default (logos usually live on
the page rather than in a coloured bar) and uses the same --smq-* theme tokens.
Props
| Prop | Type | Default | Description |
| -------------- | --------------------------------------------- | --------------- | ----------- |
| items | NewsItem[] | — (required) | Headlines. A NewsItem is a string or { id?, content, href?, target? }. |
| speed | number | 60 | Scroll speed in pixels per second. |
| direction | "left" \| "right" | "left" | Scroll direction. |
| pauseOnHover | boolean | true | Pause while the pointer is over the ticker. Set false for a classic continuous ticker. |
| pauseOnFocus | boolean | true | Pause while any item/link has keyboard focus. |
| badge | BadgeConfig \| ReactNode \| false | false | Pinned label. Pass { label, showDot?, dotColor? }, any node, or false to hide. |
| separator | ReactNode | gold diamond ◆ | Node rendered between items. Pass null for none. |
| fadeEdges | boolean | true | Fade the left/right edges with a gradient mask. |
| gap | number | 24 | Horizontal gap (px) between items and separators. |
| ariaLabel | string | "News ticker" | Accessible label for the ticker region. |
| className | string | — | Extra class on the root element. |
| style | CSSProperties | — | Inline styles on the root (also accepts theme CSS vars). |
Theming
Override any of these CSS custom properties — via the style prop or your own stylesheet:
| Variable | Default | Purpose |
| ------------------ | ----------- | ------- |
| --smq-bg | #111114 | Bar background |
| --smq-fg | #f4f4f5 | Text colour |
| --smq-link | #ffffff | Link colour |
| --smq-badge-bg | #f5c518 | Badge background |
| --smq-badge-fg | #1a1a1a | Badge text |
| --smq-dot | #ef4444 | Status dot colour |
| --smq-separator | #f5c518 | Separator colour |
| --smq-height | 44px | Bar height |
| --smq-radius | 10px | Corner radius |
| --smq-fade-width | 48px | Edge fade width |
<NewsTicker
items={items}
style={{
"--smq-bg": "#f4f4f5",
"--smq-fg": "#18181b",
"--smq-badge-bg": "#dc2626",
"--smq-radius": "999px",
} as React.CSSProperties}
/>Live headlines (RSS / API)
The component is intentionally data-source agnostic — fetch wherever you like and pass items:
const [items, setItems] = useState<NewsItem[]>([]);
useEffect(() => {
fetch("/api/headlines")
.then((r) => r.json())
.then((data) =>
setItems(data.map((h) => ({ id: h.id, content: h.title, href: h.url }))),
);
}, []);
return <NewsTicker items={items} badge={{ label: "Live" }} />;Development
npm install
npm run dev # Vite demo with several examples
npm run build # build the library to dist/ (ESM + CJS + types + CSS)
npm run typecheck # type-check the projectPublishing
The package only ships the dist/ folder (see the files field). prepublishOnly
type-checks and rebuilds automatically, so a fresh dist/ is always published.
# one-time: log in to npm
npm login
# publish (scoped packages are private by default; publishConfig already
# sets access to public, so this publishes a public package)
npm publishBump the version with npm version patch|minor|major before publishing.
License
MIT
