registry/blocks/logo-cloud/14/logo-cloud-section.tsx
"use client";
import { useState } from "react";
import { motion, useReducedMotion } from "motion/react";
import { BrandFonts, BrandWordmark } from "@/lib/brand-typography";
import { defaultSpring, motionViewport } from "@/lib/motion-tokens";
import { cn } from "@/lib/utils";
import { SectionShell } from "@/components/layout-contract";
type Brand = { name: string; domain: string };
const rowTop: 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" },
];
const rowBottom: Brand[] = [
{ name: "Slack", domain: "slack.com" },
{ name: "GitHub", domain: "github.com" },
{ name: "Supabase", domain: "supabase.com" },
{ name: "OpenAI", domain: "openai.com" },
{ name: "Turso", domain: "turso.tech" },
{ name: "HubSpot", domain: "hubspot.com" },
];
const logoIconClass =
"size-6 shrink-0 object-contain outline outline-1 -outline-offset-1 outline-black/10 dark:outline-white/10 md:size-[1.625rem]";
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(
"text-[0.9375rem] leading-none text-foreground/85 md:text-base",
!src && "opacity-80",
)}
domain={brand.domain}
name={brand.name}
/>
</div>
);
}
function DriftRow({
brands,
direction,
paused,
reduceMotion,
}: {
brands: Brand[];
direction: "left" | "right";
paused: boolean;
reduceMotion: boolean;
}) {
const items = [...brands, ...brands];
const animateX = direction === "left" ? ["0%", "-50%"] : ["-50%", "0%"];
return (
<div className="overflow-hidden mask-[linear-gradient(to_right,transparent,black_10%,black_90%,transparent)]">
<motion.div
animate={reduceMotion || paused ? undefined : { x: animateX }}
className="flex w-max gap-8 pr-8 md:gap-10 md:pr-10"
style={{ skewX: reduceMotion ? 0 : direction === "left" ? -1.25 : 1.25 }}
transition={
reduceMotion || paused
? undefined
: {
duration: 32,
repeat: Number.POSITIVE_INFINITY,
ease: "linear",
}
}
>
{items.map((brand, index) => (
<LogoMark brand={brand} key={`${brand.domain}-${index}`} />
))}
</motion.div>
</div>
);
}
export function LogoCloudDuplexDriftSection() {
const reduceMotion = useReducedMotion();
const [paused, setPaused] = useState(false);
return (
<SectionShell spacingMode="compact" className="bg-background" onMouseEnter={() => setPaused(true)}
onMouseLeave={() => setPaused(false)}
>
<BrandFonts id="logo-cloud-14-brand-fonts" />
<div className="mx-auto flex w-full max-w-5xl flex-col items-center gap-8 md:gap-9">
<motion.p
className="text-pretty text-center text-muted-foreground text-sm lowercase tracking-[0.14em]"
initial={reduceMotion ? false : { opacity: 0, y: 8, filter: "blur(4px)" }}
transition={defaultSpring}
viewport={motionViewport}
whileInView={reduceMotion ? undefined : { opacity: 1, y: 0, filter: "blur(0px)" }}
>
dual ribbons in motion
</motion.p>
<div className="flex w-full flex-col gap-5 md:gap-6">
<DriftRow
brands={rowTop}
direction="left"
paused={paused}
reduceMotion={!!reduceMotion}
/>
<DriftRow
brands={rowBottom}
direction="right"
paused={paused}
reduceMotion={!!reduceMotion}
/>
</div>
</div>
</SectionShell>
);
}


































