registry/blocks/logo-cloud/7/logo-cloud-section.tsx
"use client";
import { useRef, useState } from "react";
import { motion, useInView, useReducedMotion } from "motion/react";
import { DecorIcon } from "@/components/decor-icon";
import { BrandFonts, BrandWordmark } from "@/lib/brand-typography";
import { defaultSpring, motionStagger, motionViewport } from "@/lib/motion-tokens";
import { cn } from "@/lib/utils";
import { SectionShell, ContentRail } from "@/components/layout-contract";
function getLogoDevUrl(domain: string, size = 88) {
const token = process.env.NEXT_PUBLIC_LOGO_DEV_PUBLISHABLE_KEY;
if (!token) return null;
const params = new URLSearchParams({
token,
size: String(size),
format: "png",
retina: "true",
fallback: "monogram",
});
return `https://img.logo.dev/${domain}?${params}`;
}
type BentoLogo = {
name: string;
domain: string;
span: string;
featured?: boolean;
};
const logos: BentoLogo[] = [
{
name: "Stripe",
domain: "stripe.com",
span: "col-span-2 row-span-2 md:col-start-1 md:row-start-1",
featured: true,
},
{
name: "Notion",
domain: "notion.so",
span: "md:col-start-3 md:row-start-1",
},
{
name: "Vercel",
domain: "vercel.com",
span: "md:col-start-3 md:row-start-2",
},
{
name: "Linear",
domain: "linear.app",
span: "md:col-start-1 md:row-start-3",
},
{
name: "Figma",
domain: "figma.com",
span: "md:col-start-2 md:row-start-3",
},
{
name: "Slack",
domain: "slack.com",
span: "md:col-start-3 md:row-start-3",
},
];
function BentoLogoCell({
brand,
isFocused,
onFocus,
reduceMotion,
cellVariants,
}: {
brand: BentoLogo;
isFocused: boolean;
onFocus: () => void;
reduceMotion: boolean;
cellVariants: {
hidden: { opacity: number; scale?: number; filter?: string };
visible: { opacity: number; scale?: number; filter?: string };
};
}) {
const featured = brand.featured;
const showWordmark = featured || isFocused;
const src = getLogoDevUrl(brand.domain, featured ? 160 : 96);
return (
<motion.button
aria-label={`${brand.name}${showWordmark ? "" : " — show wordmark"}`}
aria-pressed={isFocused}
className={cn(
"group relative flex h-full min-h-[5.75rem] w-full items-center justify-center overflow-hidden bg-background transition-[background-color,transform] active:scale-[0.96] md:min-h-0",
"after:absolute after:top-1/2 after:left-1/2 after:size-10 after:-translate-1/2 md:after:size-11",
featured && "min-h-[10rem] md:min-h-0",
isFocused &&
!featured &&
"bg-muted/20",
brand.span,
)}
initial={false}
onClick={onFocus}
transition={{ type: "spring", duration: 0.35, bounce: 0 }}
type="button"
variants={cellVariants}
>
<div className="flex min-w-0 max-w-[88%] items-center justify-center gap-2 px-3 sm:max-w-[84%] sm:gap-2.5 md:max-w-[80%] md:px-4">
{src ? (
<motion.img
alt=""
animate={{
opacity: showWordmark ? 1 : 0.55,
scale: showWordmark ? 1 : 0.94,
}}
aria-hidden
className={cn(
"shrink-0 object-contain outline outline-1 -outline-offset-1 outline-black/10 dark:outline-white/10",
featured ? "h-12 w-auto sm:h-14 md:h-16" : "h-6 w-auto sm:h-7 md:h-8",
)}
height={featured ? 64 : 32}
loading="lazy"
referrerPolicy="origin"
src={src}
transition={{ type: "spring", duration: 0.3, bounce: 0 }}
width={featured ? 160 : 96}
/>
) : (
<span
className={cn(
"shrink-0 font-medium text-foreground/55",
featured ? "text-lg sm:text-xl md:text-2xl" : "text-sm",
)}
>
{brand.name}
</span>
)}
<motion.div
animate={
reduceMotion
? { opacity: showWordmark ? 1 : 0, maxWidth: showWordmark ? 160 : 0 }
: {
opacity: showWordmark ? 1 : 0,
maxWidth: showWordmark ? 160 : 0,
filter: showWordmark ? "blur(0px)" : "blur(4px)",
}
}
className="min-w-0 overflow-hidden whitespace-nowrap"
initial={false}
transition={{ type: "spring", duration: 0.3, bounce: 0 }}
>
<BrandWordmark
className={cn(
"block text-foreground",
featured
? "text-base leading-none sm:text-lg md:text-2xl"
: "text-xs sm:text-sm",
showWordmark && (featured ? "ml-0 sm:ml-1" : "ml-1.5 sm:ml-2"),
)}
domain={brand.domain}
name={brand.name}
/>
</motion.div>
</div>
</motion.button>
);
}
export function LogoCloudBentoSection() {
const reduceMotion = useReducedMotion();
const gridRef = useRef<HTMLDivElement>(null);
const gridInView = useInView(gridRef, motionViewport);
const [focusedDomain, setFocusedDomain] = useState<string | null>(null);
const cellVariants = reduceMotion
? {
hidden: { opacity: 1 },
visible: { opacity: 1 },
}
: {
hidden: { opacity: 0, scale: 0.97, filter: "blur(4px)" },
visible: { opacity: 1, scale: 1, filter: "blur(0px)" },
};
return (
<SectionShell spacingMode="compact">
<BrandFonts id="logo-cloud-7-brand-fonts" />
<ContentRail maxWidth="max-w-5xl" className="space-y-8 md:space-y-10">
<div className="mx-auto max-w-xl space-y-2 text-center">
<motion.p
className="font-medium text-primary text-xs uppercase tracking-[0.22em]"
initial={reduceMotion ? false : { opacity: 0, y: 8, filter: "blur(4px)" }}
transition={{ ...defaultSpring, delay: 0 }}
viewport={motionViewport}
whileInView={reduceMotion ? undefined : { opacity: 1, y: 0, filter: "blur(0px)" }}
>
Integrations
</motion.p>
<motion.p
className="text-balance font-medium text-lg tracking-tight md:text-xl"
initial={reduceMotion ? false : { opacity: 0, y: 8, filter: "blur(4px)" }}
transition={{ ...defaultSpring, delay: motionStagger.item }}
viewport={motionViewport}
whileInView={reduceMotion ? undefined : { opacity: 1, y: 0, filter: "blur(0px)" }}
>
Built with your stack
</motion.p>
</div>
<motion.div
className="relative border border-border bg-border p-px shadow-[0_1px_2px_rgba(0,0,0,0.04)] dark:shadow-[0_1px_2px_rgba(0,0,0,0.2)]"
initial={reduceMotion ? false : { opacity: 0, y: 14 }}
transition={{ ...defaultSpring, delay: motionStagger.item * 2 }}
viewport={motionViewport}
whileInView={reduceMotion ? undefined : { opacity: 1, y: 0 }}
>
<DecorIcon position="top-left" />
<DecorIcon position="top-right" />
<DecorIcon position="bottom-left" />
<DecorIcon position="bottom-right" />
<motion.div
animate={gridInView ? "visible" : "hidden"}
className="grid grid-cols-2 grid-rows-[10rem_repeat(4,minmax(5.75rem,1fr))] gap-px bg-border md:grid-cols-3 md:grid-rows-[repeat(3,minmax(9.5rem,1fr))]"
initial="hidden"
onClick={(event) => {
if (event.target === event.currentTarget) {
setFocusedDomain(null);
}
}}
ref={gridRef}
variants={{
hidden: {},
visible: {
transition: {
staggerChildren: motionStagger.item,
delayChildren: 0.04,
},
},
}}
>
{logos.map((brand) => (
<BentoLogoCell
brand={brand}
cellVariants={cellVariants}
isFocused={focusedDomain === brand.domain}
key={brand.domain}
onFocus={() =>
setFocusedDomain((current) =>
current === brand.domain ? null : brand.domain,
)
}
reduceMotion={!!reduceMotion}
/>
))}
</motion.div>
</motion.div>
</ContentRail>
</SectionShell>
);
}



































