Staaarter

Infinite Marquee Feature Pills

A feature section with an eyebrow, heading, and description above one or more auto-scrolling marquee rows of icon-and-label pills. Adjacent rows scroll in alternating directions and pause when hovered; each row fades out at its edges via a CSS mask.

By Staaarter Team
Feature
Docs
Live preview

Docs

Installation

A real shadcn registry command, not a copy-paste stand-in: it fetches this block plus its dependencies and any missing npm packages, and installs them straight into your project.

npx shadcn add https://staaarter.com/r/infinite-marquee-feature-pills.json

Prefer not to use the CLI? Copy the source from the Code toggle above into src/components/blocks/feature/feature106.tsx instead.

Usage

The file also exports feature106Demo, the exact props behind the preview above. Spread it to get a working section in one line, then replace it with your own data.

import { Feature106, feature106Demo } from "@/components/blocks/feature/feature106";

export default function Page() {
  return <Feature106 {...feature106Demo} />;
}

Props

PropTypeDefaultDescription
eyebrowstringundefinedSmall label above the heading; omitted entirely when unset.
headingReactNodenoneSection heading.
descriptionstringundefinedSupporting copy under the heading.
rowsMarqueePillItem[][]noneOne array of pills per marquee row; each row's list is duplicated internally for a seamless loop.
classNamestringundefinedExtra classes for the outer section element.

Types

type MarqueePillItem = {
  label: string;
  icon: LucideIcon;
};

Behavior notes

  • Server component: the scroll is a pure CSS keyframe animation (translateX(0) to translateX(-50%)) on a track that duplicates its row's pill list once, so the loop point is seamless with no JS driving it.
  • Rows alternate scroll direction and duration by index via a small fixed set of literal Tailwind arbitrary-value animate-[...] classes (Tailwind's build-time scanner needs the full class string present literally in source, so these are precomputed rather than built with a template string).
  • Pausing on hover uses `hover:[animation-play-state:paused]` directly on the animated track, a plain CSS pseudo-class, no JS event handler.
  • Each row's edges fade via `mask-image: linear-gradient(...)` so pills entering/leaving the row fade rather than hard-clip.
  • Animation is disabled under `prefers-reduced-motion: reduce`.
  • Icons are a real prop here (not client-boundary-restricted, since this is a server component): each pill carries its own lucide-react icon component.