# Integrations 5

> Asymmetric bento with flagship connector cell and capability tags — not a flat logo wall.

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

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

## Install

```bash
npx shadcn@latest add https://nusaiba.dev/r/base-nova/integrations-5.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`

## Files

### `registry/blocks/integrations/integrations-data.ts`

```ts
export const integrationsEyebrow = "Integrations";

export const integrationsGridCopy = {
	title: "Connects with your stack",
	description:
		"Sync installs, trigger workflows, and keep every tool in lockstep — registry-native handoff included.",
} as const;

export const integrationsOrbitalCopy = {
	title: "Endless possibilities",
	description:
		"Enhance your ship loop with integrations that meet your team where they already work — from design to deploy.",
} as const;

export const integrationsNetworkCopy = {
	title: "Bring your whole workflow",
	description:
		"Design in Figma, ship from GitHub, notify in Slack — one registry surface ties the loop together.",
} as const;

export const integrationsSplitCopy = {
	title: "The synergy of integration",
	headline: "Streamlining installs across your toolchain",
	description:
		"Each connector ships with typed hooks, documented env vars, and Copy Prompt handoff — no bespoke glue code.",
	cta: "Browse all connectors",
} as const;

export const integrationsBentoCopy = {
	title: "Connected surfaces",
	description: "Four install paths — pick the channel that matches how your team ships.",
} as const;

export const integrationsTimelineCopy = {
	title: "Connect in minutes",
	description: "Provision, authorize, and verify — a linear path from empty to live.",
} as const;

export const integrationsPickerCopy = {
	title: "Find your connector",
	description: "Search the catalog — arrow keys to preview, Enter to open docs.",
} as const;

export const integrationsMarqueeCopy = {
	title: "Always in sync",
	description: "A quiet strip of connectors — logo and name only, no badge clutter.",
} as const;

export const integrationsDepthCopy = {
	title: "Integration depth",
	description: "Compare native registry hooks against API and webhook fallbacks.",
} as const;

export const integrationsFramedCopy = {
	eyebrow: "Pro connectors",
	title: "Constellation map",
	description: "Framed overview of commercial-grade sync paths for Pro workspaces.",
} as const;

export type IntegrationBrand = {
	name: string;
	domain: string;
	description: string;
	tag?: string;
	sync?: "Native" | "API" | "Webhook";
};

export const integrationBrands: IntegrationBrand[] = [
	{
		name: "GitHub",
		domain: "github.com",
		description: "Install blocks from CI and open PRs with registry tokens",
		tag: "CI/CD",
		sync: "Native",
	},
	{
		name: "Vercel",
		domain: "vercel.com",
		description: "Preview registry sections on every deployment",
		tag: "Deploy",
		sync: "Native",
	},
	{
		name: "Figma",
		domain: "figma.com",
		description: "Hand off frames to Copy Prompt install flows",
		tag: "Design",
		sync: "API",
	},
	{
		name: "Slack",
		domain: "slack.com",
		description: "Notify channels when Pro blocks update",
		tag: "Alerts",
		sync: "Webhook",
	},
	{
		name: "Linear",
		domain: "linear.app",
		description: "Link block issues to registry changelog entries",
		tag: "Issues",
		sync: "API",
	},
	{
		name: "Notion",
		domain: "notion.so",
		description: "Embed live block previews in team wikis",
		tag: "Docs",
		sync: "API",
	},
	{
		name: "Stripe",
		domain: "stripe.com",
		description: "Gate Pro registry installs behind license checks",
		tag: "Billing",
		sync: "Native",
	},
	{
		name: "npm",
		domain: "npmjs.com",
		description: "Publish private registry packages to your scope",
		tag: "Packages",
		sync: "Native",
	},
	{
		name: "Zapier",
		domain: "zapier.com",
		description: "Trigger automations when catalog entries change",
		tag: "Automate",
		sync: "Webhook",
	},
	{
		name: "Intercom",
		domain: "intercom.com",
		description: "Surface install help inside customer chat",
		tag: "Support",
		sync: "API",
	},
	{
		name: "HubSpot",
		domain: "hubspot.com",
		description: "Sync Pro leads from pricing block events",
		tag: "CRM",
		sync: "Webhook",
	},
	{
		name: "Asana",
		domain: "asana.com",
		description: "Create tasks when new block families ship",
		tag: "Projects",
		sync: "API",
	},
];

export const orbitalBrands = integrationBrands.slice(0, 8);

export const networkBrands = integrationBrands.slice(0, 4);

export const stackBrands = integrationBrands.slice(0, 4);

export const bentoBrands = integrationBrands.slice(0, 4);

export const timelineBrands = integrationBrands.slice(0, 5);

export const pickerBrands = integrationBrands.slice(0, 8);

export const marqueeBrands = integrationBrands.slice(0, 10);

export const depthBrands = integrationBrands.slice(0, 6);

export const constellationBrands = integrationBrands.slice(0, 6);
```

### `registry/blocks/integrations/integrations-primitives.tsx`

```tsx
"use client";

import { motion, useInView, useReducedMotion } from "motion/react";
import { useRef, type ReactNode } from "react";
import { cn } from "@/lib/utils";
import { defaultSpring, motionStagger, motionViewport } from "@/lib/motion-tokens";
import type { IntegrationBrand } from "./integrations-data";
import { integrationsEyebrow } from "./integrations-data";

export const integrationItemSpring = defaultSpring;

export function integrationRevealInitial(reduceMotion: boolean) {
	return reduceMotion
		? false
		: { opacity: 0, y: 10, filter: "blur(4px)" as const };
}

export function integrationRevealVisible(reduceMotion: boolean) {
	return reduceMotion ? undefined : { opacity: 1, y: 0, filter: "blur(0px)" as const };
}

export function integrationItemVariants(reduceMotion: boolean | null) {
	return reduceMotion
		? { hidden: { opacity: 1 }, visible: { opacity: 1 } }
		: {
				hidden: { opacity: 0, y: 10, filter: "blur(4px)" },
				visible: { opacity: 1, y: 0, filter: "blur(0px)", transition: integrationItemSpring },
			};
}

export const integrationStaggerContainer = {
	hidden: {},
	visible: {
		transition: { staggerChildren: motionStagger.item, delayChildren: 0.04 },
	},
};

export function MotionStaggerGroup({
	children,
	className,
}: {
	children: ReactNode;
	className?: string;
}) {
	const ref = useRef<HTMLDivElement>(null);
	const inView = useInView(ref, motionViewport);

	return (
		<motion.div
			animate={inView ? "visible" : "hidden"}
			className={className}
			initial="hidden"
			ref={ref}
			variants={integrationStaggerContainer}
		>
			{children}
		</motion.div>
	);
}

export function MotionStaggerItem({
	children,
	className,
	style,
}: {
	children: ReactNode;
	className?: string;
	style?: React.CSSProperties;
}) {
	const reduceMotion = useReducedMotion();
	const itemVariants = integrationItemVariants(reduceMotion);

	return (
		<motion.div className={className} style={style} variants={itemVariants}>
			{children}
		</motion.div>
	);
}

export function MotionHoverLift({
	children,
	className,
}: {
	children: ReactNode;
	className?: string;
}) {
	return (
		<motion.div
			className={className}
			transition={defaultSpring}
			whileHover={{ y: -2 }}
		>
			{children}
		</motion.div>
	);
}

/** Tile flies out from hub center — use motion x/y, not CSS transform. */
export function RadialRevealTile({
	brand,
	x,
	y,
	delay = 0,
	className,
}: {
	brand: IntegrationBrand;
	x: number;
	y: number;
	delay?: number;
	className?: string;
}) {
	const reduceMotion = useReducedMotion();

	return (
		<motion.div
			className={cn("absolute top-1/2 left-1/2", className)}
			initial={
				reduceMotion
					? false
					: { x: 0, y: 0, opacity: 0, scale: 0.88, filter: "blur(6px)" }
			}
			transition={{ ...defaultSpring, delay }}
			viewport={motionViewport}
			whileInView={
				reduceMotion
					? undefined
					: { x, y, opacity: 1, scale: 1, filter: "blur(0px)" }
			}
		>
			<div className="-translate-x-1/2 -translate-y-1/2">
				<IntegrationTile brand={brand} />
			</div>
		</motion.div>
	);
}

export function HubReveal({
	children,
	className,
	delay = 0,
}: {
	children: ReactNode;
	className?: string;
	delay?: number;
}) {
	const reduceMotion = useReducedMotion();

	return (
		<motion.div
			className={cn("absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2", className)}
			initial={reduceMotion ? false : { opacity: 0, scale: 0.92, filter: "blur(4px)" }}
			transition={{ ...defaultSpring, delay }}
			viewport={motionViewport}
			whileInView={reduceMotion ? undefined : { opacity: 1, scale: 1, filter: "blur(0px)" }}
		>
			{children}
		</motion.div>
	);
}

export function TimelineStepReveal({
	children,
	index,
	className,
}: {
	children: ReactNode;
	index: number;
	className?: string;
}) {
	const reduceMotion = useReducedMotion();

	return (
		<motion.li
			className={className}
			initial={integrationRevealInitial(!!reduceMotion)}
			transition={{ ...defaultSpring, delay: index * motionStagger.item }}
			viewport={motionViewport}
			whileInView={integrationRevealVisible(!!reduceMotion)}
		>
			{children}
		</motion.li>
	);
}

export function MotionIntegrationTile({
	brand,
	className,
}: {
	brand: IntegrationBrand;
	className?: string;
}) {
	return (
		<MotionHoverLift>
			<IntegrationTile brand={brand} className={className} />
		</MotionHoverLift>
	);
}

export function getLogoDevUrl(domain: string, size = "80") {
	const token = process.env.NEXT_PUBLIC_LOGO_DEV_PUBLISHABLE_KEY;
	if (!token) return null;

	const params = new URLSearchParams({
		token,
		size,
		format: "png",
		retina: "true",
		fallback: "monogram",
	});

	return `https://img.logo.dev/${domain}?${params}`;
}

export function IntegrationLogo({
	domain,
	name,
	className,
	size = "md",
}: {
	domain: string;
	name: string;
	className?: string;
	size?: "sm" | "md" | "lg";
}) {
	const src = getLogoDevUrl(domain, size === "lg" ? "96" : size === "sm" ? "64" : "80");
	const box =
		size === "lg" ? "size-9 md:size-10" : size === "sm" ? "size-6" : "size-7 md:size-8";

	if (src) {
		return (
			<img
				alt={`${name} logo`}
				className={cn("object-contain", box, className)}
				height={40}
				loading="lazy"
				referrerPolicy="origin"
				src={src}
				width={40}
			/>
		);
	}

	return (
		<span
			aria-hidden
			className={cn(
				"flex items-center justify-center rounded-md bg-muted font-medium text-foreground/70 text-xs",
				box,
				className,
			)}
		>
			{name.charAt(0)}
		</span>
	);
}

export function IntegrationTile({
	brand,
	className,
}: {
	brand: IntegrationBrand;
	className?: string;
}) {
	return (
		<div
			className={cn(
				"flex size-11 items-center justify-center rounded-xl bg-muted/30 md:size-12",
				className,
			)}
		>
			<IntegrationLogo domain={brand.domain} name={brand.name} size="sm" />
		</div>
	);
}

export function NusaibaHubMark({ className }: { className?: string }) {
	const cells = [1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1];

	return (
		<div
			className={cn(
				"flex size-12 items-center justify-center rounded-xl border border-border/80 bg-background md:size-14",
				className,
			)}
		>
			<span
				aria-hidden
				className="grid size-7 grid-cols-4 gap-px rounded-sm bg-foreground/10 p-0.5"
			>
				{cells.map((filled, index) => (
					<span
						className={cn("rounded-[1px]", filled ? "bg-foreground" : "bg-transparent")}
						key={index}
					/>
				))}
			</span>
		</div>
	);
}

export function OrbitPosition({
	angle,
	children,
	radius = 132,
}: {
	angle: number;
	children: ReactNode;
	radius?: number;
}) {
	const rad = (angle * Math.PI) / 180;
	const x = Math.cos(rad) * radius;
	const y = Math.sin(rad) * radius;

	return (
		<div
			className="absolute top-1/2 left-1/2"
			style={{ transform: `translate(calc(-50% + ${x}px), calc(-50% + ${y}px))` }}
		>
			{children}
		</div>
	);
}

export function IntegrationCardRow({
	brand,
	className,
}: {
	brand: IntegrationBrand;
	className?: string;
}) {
	return (
		<div className={cn("flex items-center gap-3.5 py-3.5", className)}>
			<div className="flex size-10 shrink-0 items-center justify-center rounded-xl bg-muted/30">
				<IntegrationLogo domain={brand.domain} name={brand.name} size="sm" />
			</div>
			<div className="min-w-0 text-left">
				<p className="font-medium text-sm">{brand.name}</p>
				<p className="mt-0.5 text-pretty text-muted-foreground text-xs leading-relaxed">
					{brand.description}
				</p>
			</div>
		</div>
	);
}

export function IntegrationsSectionHeader({
	title,
	description,
	className,
	align = "center",
}: {
	title: string;
	description: string;
	className?: string;
	align?: "center" | "left";
}) {
	return (
		<MotionReveal
			className={cn(
				"max-w-2xl",
				align === "center" ? "mx-auto text-center" : "text-left",
				className,
			)}
		>
			<p className="text-muted-foreground text-sm">{integrationsEyebrow}</p>
			<h2 className="mt-2 text-balance font-medium text-2xl tracking-tight md:text-3xl">
				{title}
			</h2>
			<p className="mt-3 text-pretty text-muted-foreground text-sm leading-relaxed">
				{description}
			</p>
		</MotionReveal>
	);
}

export function MotionReveal({
	children,
	className,
	delay = 0,
}: {
	children: ReactNode;
	className?: string;
	delay?: number;
}) {
	const reduceMotion = useReducedMotion();

	return (
		<motion.div
			className={className}
			initial={integrationRevealInitial(!!reduceMotion)}
			transition={{ ...defaultSpring, delay }}
			viewport={motionViewport}
			whileInView={integrationRevealVisible(!!reduceMotion)}
		>
			{children}
		</motion.div>
	);
}
```

### `registry/blocks/integrations/5/integrations-section.tsx`

```tsx
"use client";

import { integrationsBentoCopy, bentoBrands } from "../integrations-data";
import { SectionShell, ContentRail } from "@/components/layout-contract";
import {
	IntegrationLogo,
	IntegrationsSectionHeader,
	MotionHoverLift,
	MotionStaggerGroup,
	MotionStaggerItem,
} from "../integrations-primitives";

export function IntegrationsBentoSection() {
	return (
		<SectionShell spacingMode="section">
			<ContentRail maxWidth="max-w-4xl" className="space-y-10">
				<IntegrationsSectionHeader
					description={integrationsBentoCopy.description}
					title={integrationsBentoCopy.title}
				/>

				<MotionStaggerGroup className="grid gap-px overflow-hidden rounded-xl border border-border bg-border sm:grid-cols-2">
					{bentoBrands.map((brand) => (
						<MotionStaggerItem key={brand.domain}>
							<MotionHoverLift className="h-full bg-background p-5">
								<div className="flex flex-col gap-4">
									<div className="flex items-center justify-between gap-3">
										<div className="flex size-10 items-center justify-center rounded-xl bg-muted/30">
											<IntegrationLogo domain={brand.domain} name={brand.name} size="sm" />
										</div>
										{brand.tag ? (
											<span className="text-muted-foreground text-xs">{brand.tag}</span>
										) : null}
									</div>
									<div>
										<p className="font-medium text-sm">{brand.name}</p>
										<p className="mt-1 text-pretty text-muted-foreground text-xs leading-relaxed">
											{brand.description}
										</p>
									</div>
								</div>
							</MotionHoverLift>
						</MotionStaggerItem>
					))}
				</MotionStaggerGroup>
			</ContentRail>
		</SectionShell>
	);
}
```
