# Logo cloud 1

> Infinite scrolling logo marquee with Logo.dev marks, wordmarks, and edge fade mask.

A shadcn/ui block from the [@nusaiba](https://nusaiba.dev) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/nusaiba/logo-cloud-1).

- Page, with a live preview: https://shadcn.exchange/r/nusaiba/logo-cloud-1
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/nusaiba/logo-cloud-1-light-3723b004e8929662.png
- Registry item: https://nusaiba.dev/r/base-nova/logo-cloud-1.json
- Categories: logo-cloud
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://nusaiba.dev/r/base-nova/logo-cloud-1.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `motion`
- Registry items: `@nusaiba/foundation`, `@nusaiba/brand-typography`

## Files

### `registry/blocks/logo-cloud/1/logo-cloud-section.tsx`

```tsx
"use client";

import { useState } from "react";
import { motion, useReducedMotion } from "motion/react";
import { BrandFonts, BrandWordmark } from "@/lib/brand-typography";
import { cn } from "@/lib/utils";
import { SectionShell, ContentRail } from "@/components/layout-contract";

type Brand = { name: string; domain: string };

const brands: Brand[] = [
	{ name: "Stripe", domain: "stripe.com" },
	{ name: "Notion", domain: "notion.so" },
	{ name: "Vercel", domain: "vercel.com" },
	{ name: "Linear", domain: "linear.app" },
	{ name: "Figma", domain: "figma.com" },
	{ name: "Shopify", domain: "shopify.com" },
	{ name: "Slack", domain: "slack.com" },
	{ name: "GitHub", domain: "github.com" },
];

const logoIconClass =
	"size-6 shrink-0 object-contain md:size-[1.625rem] outline outline-1 -outline-offset-1 outline-black/10 dark:outline-white/10";
const logoWordmarkClass = "text-[0.9375rem] leading-none md:text-base";

function getLogoDevUrl(domain: string) {
	const token = process.env.NEXT_PUBLIC_LOGO_DEV_PUBLISHABLE_KEY;
	if (!token) return null;
	const params = new URLSearchParams({
		token,
		size: "96",
		format: "png",
		retina: "true",
		fallback: "monogram",
	});
	return `https://img.logo.dev/${domain}?${params}`;
}

function LogoMark({ brand }: { brand: Brand }) {
	const src = getLogoDevUrl(brand.domain);

	return (
		<div className="flex h-7 shrink-0 items-center gap-2 md:h-8">
			{src ? (
				<img
					alt=""
					aria-hidden
					className={logoIconClass}
					height={26}
					loading="lazy"
					referrerPolicy="origin"
					src={src}
				/>
			) : null}
			<BrandWordmark
				className={cn(logoWordmarkClass, "text-foreground/85", !src && "opacity-80")}
				domain={brand.domain}
				name={brand.name}
			/>
		</div>
	);
}

export function LogoCloudSection() {
	const reduceMotion = useReducedMotion();
	const [paused, setPaused] = useState(false);
	const loop = [...brands, ...brands];

	return (
		<SectionShell spacingMode="compact" className="bg-background">
			<BrandFonts id="logo-cloud-1-brand-fonts" />
			<ContentRail maxWidth="max-w-5xl" className="space-y-8 md:space-y-9">
				<p className="text-pretty text-center text-muted-foreground text-sm lowercase tracking-[0.14em]">
					used by teams everywhere
				</p>

				<div
					className="relative overflow-hidden mask-[linear-gradient(to_right,transparent,black_10%,black_90%,transparent)]"
					onMouseEnter={() => setPaused(true)}
					onMouseLeave={() => setPaused(false)}
				>
					<motion.div
						animate={reduceMotion || paused ? undefined : { x: ["0%", "-50%"] }}
						className="flex w-max gap-10 pr-10 md:gap-12 md:pr-12"
						transition={
							reduceMotion || paused
								? undefined
								: {
										duration: 32,
										repeat: Number.POSITIVE_INFINITY,
										ease: "linear",
									}
						}
					>
						{loop.map((brand, index) => (
							<LogoMark brand={brand} key={`${brand.domain}-${index}`} />
						))}
					</motion.div>
				</div>
			</ContentRail>
		</SectionShell>
	);
}
```
