Getting Started
Templates
Blocks
Components
For solo developers, indie hackers, and freelancers shipping their own products. Pay once, keep everything forever.
Instant download · No subscription · Lifetime access
For small teams, agencies, and freelancers shipping multiple client products. Lifetime access for everyone on your team.
Instant CLI token for 5 seats · Onboarding included · Invoice on request
pnpm dlx shadcn@latest add @ruixenui-pro/logo-cloud-stripThe grid is responsive in three steps:
sm and below — two columns. The lg-only cell is hidden, so four cells sit in a tidy two-row block.md — four columns. The lg-only cell stays hidden; the strip reads as a single row of four.lg and above — five columns. All cells visible in a single row.Each cell is relative h-10 overflow-hidden. The overflow-hidden is the mask that makes the text-reveal work: the inner motion.div is absolute inset-0, so as it translates between y: 100% (below the cell) and y: -100% (above the cell), only the portion currently intersecting the cell is visible.
A single index state advances on a 2500ms interval. Every cell reads cell.logos[index % cell.logos.length], so all cells advance to their next logo at the same instant — the strip moves in unison.
For each cell:
y: 100% (just below the cell, clipped) and animates to y: 0 (centered).y: -100% (just above the cell, clipped).AnimatePresence (default sync mode), so the upward motion reads as one continuous push: the new logo lifts the old one out.Easing is [0.22, 1, 0.36, 1] (a soft easeOut-back curve) over 550ms. initial={true} on AnimatePresence means the very first frame also performs the rise — every cell's first logo enters from the bottom on mount.
The cycle is gated by IntersectionObserver: when the strip leaves the viewport, the interval stops; when it returns, the interval resumes. No work happens while the component is offscreen.
Edit the CELLS array at the top of the file. Each cell is an array of logos that the carousel rotates through. Set hideUntilLg: true on any cell you want excluded from the four-column layout — that's the mechanism the fifth slot uses.
const CELLS = [
{
logos: [
{ name: "first-logo", ext: "svg", alt: "First" },
{ name: "second-logo", ext: "svg", alt: "Second" },
{ name: "third-logo", ext: "svg", alt: "Third" },
],
},
// ...
{
hideUntilLg: true,
logos: [/* ... */],
},
]Asset files live under /public/logos/; the component constructs URLs as /logos/${name}.${ext}. Logos render as monochrome silhouettes via brightness-0 dark:invert, so they follow the active theme.