# Hero 9

> Swiss ruled split — registry copy beside a modular block grid motion stage.

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

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

## Install

```bash
npx shadcn@latest add https://nusaiba.dev/r/base-nova/hero-9.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`, `badge`, `button`

## Files

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

```ts
export const heroSite = {
	name: "Nusaiba",
	namespace: "@nusaiba",
	domain: "nusaiba.dev",
} as const;

export const heroAnnouncement = {
	label: "Open registry",
	detail: "Motion-first shadcn blocks",
} as const;

export const heroCopy = {
	headline: "Sections built for motion.",
	subhead:
		"Preview every block live, install with the CLI, and keep source in your repo — no runtime lock-in.",
	primaryCta: { label: "Browse blocks", href: "/blocks" },
	secondaryCta: { label: "View docs", href: "/docs" },
} as const;

export const heroPosterCopy = {
	headline: ["Compose", "landing", "pages", "with", "intent."],
	subhead:
		"Editorial sections for developers who care about rhythm, type, and installable motion — not another SaaS template.",
} as const;

export const heroArchitectureCopy = {
	eyebrow: "Registry",
	headline: "Invisible grid. Visible craft.",
	subhead:
		"Each block ships as files you own. Wire copy, tokens, and motion to the product story you are telling.",
} as const;

export const heroMagazineCopy = {
	issue: "Vol. 03",
	headline: "Motion is the interface.",
	pullQuote: "Less decoration. More composition.",
	subhead:
		"Heroes, pricing, FAQs, footers — staged for scroll-reveal, stagger, and purposeful negative space.",
	trustLabel: "Installs beside",
} as const;

export const heroMagazineTrustBrands = [
	{ name: "Next.js", domain: "nextjs.org" },
	{ name: "Vercel", domain: "vercel.com" },
	{ name: "Linear", domain: "linear.app" },
	{ name: "Notion", domain: "notion.so" },
] as const;

export const heroFilmCopy = {
	headline: "A library that reads like a magazine.",
	subhead:
		"Scan live previews, copy a prompt, or pull a section with one command. Every layout is a distinct archetype.",
} as const;

export const heroPerspectiveCopy = {
	headline: "Depth before decoration.",
	subhead:
		"Layers imply movement before animation runs — perspective, overlap, and reveal composition baked into the layout.",
} as const;

export const heroGalleryCopy = {
	headline: "Ten families. One install path.",
	subhead:
		"From heroes to footers, each category explores a different visual narrative for landing pages.",
} as const;

export const heroDiagonalCopy = {
	headline: "Cut against the grid.",
	subhead:
		"Diagonal rhythm and masked stages — editorial tension without gradients, blobs, or stock dashboards.",
} as const;

export const heroFramedCopy = {
	headline: "Framed for the fold.",
	subhead:
		"Museum-grade spacing, decor corners, and a motion stage that feels exhibition-ready on first paint.",
} as const;

export const heroShowcaseBlocks = [
	{ id: "hero", label: "Hero", pattern: "scroll-reveal" },
	{ id: "pricing", label: "Pricing", pattern: "focus-shift" },
	{ id: "faqs", label: "FAQs", pattern: "blur-focus" },
	{ id: "cta", label: "CTA", pattern: "expand-grid" },
	{ id: "footer", label: "Footer", pattern: "fade-mask" },
] as const;

export const heroGalleryTiles = [
	{ id: "motion", title: "Motion patterns", caption: "Linked to live blocks" },
	{ id: "cli", title: "shadcn CLI", caption: "npx shadcn@latest add" },
	{ id: "prompt", title: "Copy prompt", caption: "Registry-native handoff" },
	{ id: "pro", title: "Pro license", caption: "Commercial client work" },
] as const;
```

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

```tsx
"use client";

import Link from "next/link";
import { useRef, useState } from "react";
import { ArrowRightIcon } from "lucide-react";
import { motion, useInView, useReducedMotion } from "motion/react";
import { Badge } from "@/components/ui/badge";
import { buttonVariants } from "@/components/ui/button";
import { BrandFonts, BrandWordmark } from "@/lib/brand-typography";
import { motionStagger, motionViewport } from "@/lib/motion-tokens";
import { cn } from "@/lib/utils";
import { heroAnnouncement, heroCopy } from "./hero-data";

const springPress = "active:scale-[0.96] transition-transform";

const logoIconClass =
	"size-5 shrink-0 object-contain outline outline-1 -outline-offset-1 outline-black/10 dark:outline-white/10";

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

export function HeroAnnouncement({
	className,
	detail = heroAnnouncement.detail,
	label = heroAnnouncement.label,
}: {
	className?: string;
	detail?: string;
	label?: string;
}) {
	return (
		<div className={cn("flex flex-wrap items-center gap-2", className)}>
			<Badge variant="outline">{label}</Badge>
			<span className="text-muted-foreground text-xs tracking-wide">{detail}</span>
		</div>
	);
}

export function HeroCtaRow({
	className,
	primary = heroCopy.primaryCta,
	secondary = heroCopy.secondaryCta,
}: {
	className?: string;
	primary?: { label: string; href: string };
	secondary?: { label: string; href: string };
}) {
	return (
		<div className={cn("flex flex-col gap-3 sm:flex-row sm:items-center", className)}>
			<Link className={cn(buttonVariants(), springPress)} href={primary.href}>
				{primary.label}
				<ArrowRightIcon data-icon="inline-end" />
			</Link>
			<Link
				className={cn(buttonVariants({ variant: "outline" }), springPress)}
				href={secondary.href}
			>
				{secondary.label}
			</Link>
		</div>
	);
}

export function MotionShowcaseFrame({
	children,
	className,
	label,
}: {
	children: React.ReactNode;
	className?: string;
	label?: string;
}) {
	return (
		<div
			className={cn(
				"relative overflow-hidden border border-border bg-background shadow-[0_1px_0_rgba(0,0,0,0.03)] dark:shadow-[0_1px_0_rgba(255,255,255,0.04)]",
				className,
			)}
		>
			{label ? (
				<p className="absolute top-3 left-3 z-10 font-mono text-[10px] text-muted-foreground uppercase tracking-[0.2em]">
					{label}
				</p>
			) : null}
			{children}
		</div>
	);
}

export function BlockStripChip({
	label,
	pattern,
}: {
	label: string;
	pattern: string;
}) {
	return (
		<div className="flex w-[11rem] shrink-0 flex-col justify-between border border-border bg-card/60 p-4">
			<p className="font-medium text-sm tracking-tight">{label}</p>
			<p className="mt-8 font-mono text-[10px] text-muted-foreground uppercase tracking-[0.18em]">
				{pattern}
			</p>
		</div>
	);
}

export function SwissRule({ className }: { className?: string }) {
	return <div aria-hidden className={cn("h-px w-full bg-border", className)} />;
}

export function HeroInlineLogoStrip({
	brands,
	className,
	label,
}: {
	brands: readonly { name: string; domain: string }[];
	className?: string;
	label: string;
}) {
	const reduceMotion = useReducedMotion();
	const listRef = useRef<HTMLUListElement>(null);
	const inView = useInView(listRef, motionViewport);
	const [focused, setFocused] = useState<string | null>(null);

	const itemVariants = reduceMotion
		? { hidden: { opacity: 1 }, visible: { opacity: 1 } }
		: {
				hidden: { opacity: 0, filter: "blur(4px)", y: 6 },
				visible: { opacity: 1, filter: "blur(0px)", y: 0, transition: { type: "spring" as const, duration: 0.35, bounce: 0 } },
			};

	return (
		<div className={cn("max-w-md", className)}>
			<BrandFonts id="hero-inline-logo-strip-fonts" />
			<p className="font-mono text-[10px] text-muted-foreground uppercase tracking-[0.2em]">
				{label}
			</p>
			<motion.ul
				animate={inView ? "visible" : "hidden"}
				className="mt-4 flex flex-wrap items-center gap-x-5 gap-y-3 sm:gap-x-6"
				initial="hidden"
				onMouseLeave={() => setFocused(null)}
				ref={listRef}
				variants={{
					hidden: {},
					visible: {
						transition: { staggerChildren: motionStagger.item, delayChildren: 0.04 },
					},
				}}
			>
				{brands.map((brand) => {
					const src = getLogoDevUrl(brand.domain);
					const isFocused = focused === brand.domain;
					const dimmed = focused !== null && !isFocused;

					return (
						<motion.li key={brand.domain} variants={itemVariants}>
							<motion.button
								animate={
									reduceMotion
										? undefined
										: {
												opacity: dimmed ? 0.42 : 1,
												filter: dimmed ? "blur(3px)" : "blur(0px)",
												scale: isFocused ? 1.04 : 1,
											}
								}
								aria-label={brand.name}
								className="flex h-6 items-center gap-2 rounded-sm outline-none transition-colors active:scale-[0.96]"
								onBlur={() => setFocused((current) => (current === brand.domain ? null : current))}
								onFocus={() => setFocused(brand.domain)}
								onMouseEnter={() => setFocused(brand.domain)}
								transition={{ type: "spring", duration: 0.3, bounce: 0 }}
								type="button"
							>
								{src ? (
									<img
										alt=""
										aria-hidden
										className={logoIconClass}
										height={20}
										loading="lazy"
										referrerPolicy="origin"
										src={src}
									/>
								) : null}
								<BrandWordmark
									className={cn(
										"text-[0.75rem] leading-none sm:text-[0.8125rem]",
										isFocused ? "text-foreground" : "text-foreground/75",
										!src && "opacity-80",
									)}
									domain={brand.domain}
									name={brand.name}
								/>
							</motion.button>
						</motion.li>
					);
				})}
			</motion.ul>
		</div>
	);
}
```

### `registry/blocks/hero/9/hero-section.tsx`

```tsx
"use client";

import { useRef } from "react";
import { motion, useInView, useReducedMotion } from "motion/react";
import { ContentRail, SectionShell } from "@/components/layout-contract";
import { motionStagger, motionViewport } from "@/lib/motion-tokens";
import { cn } from "@/lib/utils";
import { heroArchitectureCopy, heroCopy } from "../hero-data";
import {
	HeroAnnouncement,
	HeroCtaRow,
	MotionShowcaseFrame,
	SwissRule,
} from "../hero-primitives";

const spring = { type: "spring" as const, duration: 0.35, bounce: 0 };

const gridFrames = [
	{ label: "hero-4", span: "col-span-2 row-span-2" },
	{ label: "pricing-2", span: "col-span-1" },
	{ label: "faqs-2", span: "col-span-1" },
	{ label: "cta-3", span: "col-span-2" },
] as const;

export function HeroArchitectureSection() {
	const reduceMotion = useReducedMotion();
	const ref = useRef<HTMLElement>(null);
	const inView = useInView(ref, motionViewport);

	const itemVariants = reduceMotion
		? { hidden: { opacity: 1 }, visible: { opacity: 1 } }
		: {
				hidden: { opacity: 0, filter: "blur(4px)", y: 12 },
				visible: { opacity: 1, filter: "blur(0px)", y: 0, transition: spring },
			};

	return (
		<SectionShell spacingMode="section" ref={ref}>
			<ContentRail>
				<SwissRule />
				<div className="grid gap-0 md:grid-cols-12">
					<motion.div
						animate={inView ? "visible" : "hidden"}
						className="flex flex-col border-border md:col-span-5 md:border-r md:pr-10 md:py-12"
						initial="hidden"
						variants={{
							hidden: {},
							visible: { transition: { staggerChildren: motionStagger.item, delayChildren: 0.05 } },
						}}
					>
						<motion.p
							className="font-medium text-primary text-xs uppercase tracking-[0.28em]"
							variants={itemVariants}
						>
							{heroArchitectureCopy.eyebrow}
						</motion.p>
						<motion.div className="mt-6" variants={itemVariants}>
							<HeroAnnouncement label={heroArchitectureCopy.eyebrow} />
						</motion.div>
						<motion.h1
							className="mt-8 text-balance font-medium text-3xl tracking-[-0.03em] md:text-5xl md:leading-[1.02]"
							variants={itemVariants}
						>
							{heroArchitectureCopy.headline}
						</motion.h1>
						<motion.p
							className="mt-6 max-w-sm text-pretty text-muted-foreground text-sm leading-relaxed"
							variants={itemVariants}
						>
							{heroArchitectureCopy.subhead}
						</motion.p>
						<motion.div className="mt-10" variants={itemVariants}>
							<HeroCtaRow />
						</motion.div>
					</motion.div>

					<motion.div
						animate={inView ? "visible" : "hidden"}
						className="mt-10 md:col-span-7 md:mt-0 md:py-12 md:pl-10"
						initial="hidden"
						variants={{
							hidden: {},
							visible: { transition: { staggerChildren: motionStagger.group, delayChildren: 0.12 } },
						}}
					>
						<MotionShowcaseFrame className="p-4 md:p-6" label="swiss grid">
							<div className="grid grid-cols-3 grid-rows-3 gap-2 md:gap-3">
								{gridFrames.map((frame) => (
									<motion.div
										className={cn(
											"flex min-h-[4.5rem] flex-col justify-between border border-border bg-muted/30 p-3",
											frame.span,
										)}
										key={frame.label}
										variants={itemVariants}
									>
										<span className="font-mono text-[10px] text-muted-foreground uppercase tracking-[0.16em]">
											{frame.label}
										</span>
										<span className="h-1 w-8 bg-foreground/15" />
									</motion.div>
								))}
							</div>
						</MotionShowcaseFrame>
						<motion.p
							className="mt-4 font-mono text-[10px] text-muted-foreground uppercase tracking-[0.2em]"
							variants={itemVariants}
						>
							{heroCopy.subhead.slice(0, 64)}…
						</motion.p>
					</motion.div>
				</div>
				<SwissRule className="mt-0" />
			</ContentRail>
		</SectionShell>
	);
}
```
