Image Ticker
v1.0.1FreeShowcase & Media
A gorgeous, cinematic dual-ticker displaying rows of images rotating in opposite directions with built-in lightbox functionality.
Image Ticker
Pointera11y
Installation
Step 1. Initialize ProjectOne-time setup
Run once in your project root to configure paths and dependencies:
npx vectorvesper initStep 2. Add ComponentInstalls source & assets
Downloads and registers the component in your project:
npx vectorvesper add image-tickerStep 3. Required DependenciesAuto-installed with CLI
If installing manually or managing your package lock:
npm install framer-motionPackages:framer-motion
Usage
Import
Usage
This component respects
prefers-reduced-motion and provides a graceful fallback.Props
Two rows of images sliding past each other at opposing angles, with a headline set across them. Cards lift and brighten under the cursor, and clicking one opens it full-size.
<ImageTicker />
The finished section — both rows, the headline, the atmospherics and the lightbox.
| Prop | Type | Default | Description |
|---|---|---|---|
topImages* | string[] | — | Images for the upper row. |
bottomImages* | string[] | — | Images for the lower row, which travels the other way. |
topRotation | number | 35 | Angle of the upper row in degrees. |
bottomRotation | number | -35 | Angle of the lower row. Negative by default so the two rows cross. |
backgroundColor | string | "#030303" | Colour behind the section. |
height | string | "100vh" | Height of the section, as any CSS length. |
title | string | — | Headline drawn over the rows. Omit it and no headline renders. |
titleFontFamily | string | "'Bebas Neue', sans-serif" | Face for the headline. The default names a font the component does not load — point this at one of yours, or load Bebas Neue. |
titleFontSize | string | "clamp(2rem, 4vw, 6rem)" | Headline size. Fluid by default, so it scales with the viewport. |
titleColor | string | "rgba(255, 255, 255, 0.95)" | Headline colour. |
titlePosition | "left" | "right" | "center" | "right" | Which side of the section the headline sits on. |
titleOffset | string | "8vw" | How far in from that edge, as any CSS length. |
showAmbientGlow | boolean | true | A soft wash of colour behind the rows. |
ambientGlowColor | string | "rgba(127,140,255,0.06)" | Colour of that wash. Very low alpha by design. |
showNoiseGrain | boolean | true | Fine grain over the whole section. |
showVignette | boolean | true | Darkens the edges of the section. |
enableLightbox | boolean | true | Whether clicking a card opens it full-size. |
onImageClick | (src: string) => void | — | Called with the clicked image's URL. Use it to route somewhere instead of opening the built-in lightbox. |
tickerProps | Partial<SmoothTickerProps> | {} | Passed through to both rows — card size, speed, hover treatment. `images`, `direction` and `onCardClick` are managed for you and cannot be set here. |
lightboxProps | Partial<LightboxOverlayProps> | {} | Passed through to the lightbox. `src` and `onClose` are managed for you. |
className | string | — | Applied to the section wrapper. |
<SmoothTicker />
One row on its own, if you want a single strip rather than the crossed pair.
| Prop | Type | Default | Description |
|---|---|---|---|
images* | string[] | — | Images in the row. |
direction | 1 | -1 | 1 | Which way the row travels. |
speed | number | 0.05 | How fast it travels. |
cardWidth | number | 170 | Width of one card in pixels. |
cardHeight | number | 200 | Height of one card. |
gap | number | 20 | Space between cards. |
cardBorderRadius | number | 14 | Corner radius of a card. |
cardBackground | string | "#0a0a0e" | Colour behind a card's image. |
containerHeight | number | 230 | Height of the row's track. |
containerBorderRadius | number | 30 | Corner radius of that track. |
containerBackground | string | "#060608" | Colour of the track. |
pauseOnHover | boolean | true | Stops the row while the cursor is over it. |
showHoverGlow | boolean | true | Lights a card under the cursor. |
showShineSweep | boolean | true | Runs a highlight across a hovered card. |
showGradientBorder | boolean | true | Draws a gradient edge on a hovered card. |
onCardClick | (src: string) => void | — | Called with the clicked image's URL. |
className | string | — | Applied to the row. |
<LightboxOverlay />
The full-size viewer, usable on its own. Closes on any click.
| Prop | Type | Default | Description |
|---|---|---|---|
src* | string | null | — | Image to show. Pass null to close it. |
onClose* | () => void | — | Called when it should close. |
imageWidth | string | "420px" | Width of the shown image. |
imageHeight | string | "540px" | Height of the shown image. |
borderRadius | string | "24px" | Corner radius of the image. |
overlayColor | string | "rgba(3,3,3,0.8)" | Colour of the backdrop. |
backdropBlur | string | "24px" | How far the backdrop blurs what is behind it. |
showCloseHint | boolean | true | Shows the line telling people how to dismiss it. |
closeHintText | string | "Click anywhere to close" | That line's text. |
showAmbientGlow | boolean | true | A soft wash behind the image. |
className | string | — | Applied to the overlay. |
Examples
The full section
import ImageTicker from "@/components/vv/image-ticker/ImageTicker";
<ImageTicker
topImages={rowA}
bottomImages={rowB}
title="SELECTED WORK"
/>Bigger cards, slower travel
<ImageTicker
topImages={rowA}
bottomImages={rowB}
title="ARCHIVE"
titlePosition="left"
tickerProps={{ cardWidth: 260, cardHeight: 320, speed: 0.02, gap: 32 }}
/>Route on click instead of opening the lightbox
<ImageTicker
topImages={rowA}
bottomImages={rowB}
enableLightbox={false}
onImageClick={(src) => router.push(`/work/${slugFor(src)}`)}
/>The rows run continuously with no reduced-motion guard. If that matters, set tickerProps={{ speed: 0 }} when the preference is set — the layout stays, the travel stops.
The default headline face is Bebas Neue, which the component does not load for you. Either load it or set titleFontFamily to one of your own.
Both rows duplicate their images to loop seamlessly, so each list is drawn more than once. Ship the images near the size the cards show them at.