# Auth 1

> Split sign-in with brand rail, scroll-reveal stagger, and registry-native copy.

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

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

## Install

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

## Files

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

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

export const authSignInCopy = {
	eyebrow: "Welcome back",
	title: "Sign in to Nusaiba",
	description: "Access your registry token, Pro blocks, and account settings.",
	submit: "Sign in",
	forgot: "Forgot password?",
	switchPrompt: "No account?",
	switchAction: "Create one",
} as const;

export const authSignUpCopy = {
	eyebrow: "Get started",
	title: "Create your Nusaiba account",
	description:
		"Install motion blocks into your repo, unlock Pro registry access, and keep every file.",
	submit: "Create account",
	switchPrompt: "Already have an account?",
	switchAction: "Sign in",
	terms:
		"By continuing, you agree to our Terms and acknowledge that blocks install into your own codebase.",
} as const;

export const authCenteredSignInCopy = {
	eyebrow: "Account",
	title: "Log in",
	description: "Continue to your registry token, billing, and saved blocks.",
} as const;

export const authMagicLinkCopy = {
	eyebrow: "Passwordless",
	title: "Sign in with email",
	description:
		"We'll send a one-time link — no password to remember. Perfect for quick access before you install blocks.",
	submit: "Send magic link",
	hint: "Check your inbox at the address you used to purchase Pro.",
} as const;

export const authOAuthCopy = {
	eyebrow: "Sign in",
	title: "Choose how to continue",
	description: "Use a provider or your work email — same account across CLI and site.",
	divider: "or continue with email",
} as const;

export const authOtpCopy = {
	eyebrow: "Verify",
	title: "Enter the code",
	description: "We sent a 6-digit code to your email. Paste it below to unlock Pro install.",
	resend: "Resend code",
} as const;

export const authBenefitsCopy = {
	eyebrow: "Why sign in",
	title: "Ship landing pages faster",
	description:
		"One account for preview, purchase, and registry token — then install sections straight into your repo.",
} as const;

export const authBenefits = [
	"Registry token for Pro block install",
	"Copy Prompt history and CLI shortcuts",
	"Commercial license for client projects",
	"Lifetime updates on purchased blocks",
] as const;

export const authFramedCopy = {
	eyebrow: "Registry access",
	title: "Sign in to install",
	description: "Preview blocks live, then add them to your landing page with one shadcn command.",
} as const;

export const authOAuthProviders = [
	{ id: "github", label: "Continue with GitHub" },
	{ id: "google", label: "Continue with Google" },
] as const;

export const authVisualCopy = {
	eyebrow: "Install",
	title: "Blocks that land in your repo",
	description:
		"Sign in to copy your registry token, unlock Pro sections, and keep full source ownership.",
} as const;
```

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

```tsx
import { cn } from "@/lib/utils";
import { authSite } from "./auth-data";

export function AuthOAuthIcon({ provider }: { provider: "github" | "google" }) {
	if (provider === "github") {
		return (
			<svg aria-hidden className="size-4" fill="currentColor" viewBox="0 0 24 24">
				<path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12z" />
			</svg>
		);
	}

	return (
		<svg aria-hidden className="size-4" viewBox="0 0 24 24">
			<path
				d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z"
				fill="#4285F4"
			/>
			<path
				d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
				fill="#34A853"
			/>
			<path
				d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
				fill="#FBBC05"
			/>
			<path
				d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
				fill="#EA4335"
			/>
		</svg>
	);
}

export function AuthDivider({ label }: { label: string }) {
	return (
		<div className="relative py-1">
			<div aria-hidden className="absolute inset-0 flex items-center">
				<div className="w-full border-border border-t" />
			</div>
			<p className="relative mx-auto w-fit bg-background px-3 text-muted-foreground text-xs">
				{label}
			</p>
		</div>
	);
}

export function AuthBenefitList({ items }: { items: readonly string[] }) {
	return (
		<ul className="space-y-3">
			{items.map((item) => (
				<li className="flex items-start gap-2.5 text-sm" key={item}>
					<span
						aria-hidden
						className="mt-1.5 size-1.5 shrink-0 rounded-full bg-foreground"
					/>
					<span className="text-pretty text-muted-foreground">{item}</span>
				</li>
			))}
		</ul>
	);
}

export function AuthBrandFooter({ className }: { className?: string }) {
	return (
		<p className={cn("text-muted-foreground text-xs tabular-nums", className)}>
			© {authSite.copyrightYear} {authSite.name}
		</p>
	);
}
```

### `registry/blocks/auth/1/auth-section.tsx`

```tsx
"use client";

import Link from "next/link";
import { useRef } from "react";
import { motion, useInView, useReducedMotion } from "motion/react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { motionStagger, motionViewport } from "@/lib/motion-tokens";
import { cn } from "@/lib/utils";
import { authSignInCopy } from "../auth-data";
import { AuthBrandFooter } from "../auth-primitives";
import { SectionShell, ContentRail } from "@/components/layout-contract";

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

export function AuthSection() {
	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: 8 },
				visible: { opacity: 1, filter: "blur(0px)", y: 0, transition: spring },
			};

	return (
		<SectionShell spacingMode="section" ref={ref}>
			<ContentRail maxWidth="max-w-5xl" className="grid overflow-hidden border border-border shadow-[0_1px_0_rgba(0,0,0,0.03)] md:grid-cols-2 dark:shadow-[0_1px_0_rgba(255,255,255,0.04)]">
				<motion.div
					animate={inView ? "visible" : "hidden"}
					className="flex flex-col justify-between bg-muted/40 p-8 md:p-12"
					initial="hidden"
					variants={{
						hidden: {},
						visible: { transition: { staggerChildren: motionStagger.item, delayChildren: 0.04 } },
					}}
				>
					<div>
						<motion.p
							className="font-medium text-primary text-xs uppercase tracking-[0.22em]"
							variants={itemVariants}
						>
							{authSignInCopy.eyebrow}
						</motion.p>
						<motion.h1
							className="mt-4 text-balance font-medium text-2xl tracking-tight md:text-3xl"
							variants={itemVariants}
						>
							{authSignInCopy.title}
						</motion.h1>
						<motion.p
							className="mt-3 text-pretty text-muted-foreground text-sm leading-relaxed"
							variants={itemVariants}
						>
							{authSignInCopy.description}
						</motion.p>
					</div>
					<motion.div variants={itemVariants}>
						<AuthBrandFooter className="mt-8" />
					</motion.div>
				</motion.div>

				<motion.form
					animate={inView ? "visible" : "hidden"}
					className="flex w-full flex-col justify-center gap-5 bg-background p-8 md:p-12"
					initial="hidden"
					variants={{
						hidden: {},
						visible: { transition: { staggerChildren: motionStagger.item, delayChildren: 0.08 } },
					}}
				>
					<motion.div className="w-full space-y-2" variants={itemVariants}>
						<Label htmlFor="auth-1-email">Email</Label>
						<Input
							autoComplete="email"
							className="w-full"
							id="auth-1-email"
							placeholder="you@nusaiba.dev"
							type="email"
						/>
					</motion.div>
					<motion.div className="w-full space-y-2" variants={itemVariants}>
						<div className="flex w-full items-center justify-between gap-4">
							<Label htmlFor="auth-1-password">Password</Label>
							<Link
								className="shrink-0 text-muted-foreground text-xs hover:text-foreground"
								href="#"
							>
								{authSignInCopy.forgot}
							</Link>
						</div>
						<Input
							autoComplete="current-password"
							className="w-full"
							id="auth-1-password"
							type="password"
						/>
					</motion.div>
					<motion.div variants={itemVariants}>
						<Button
							className={cn("w-full active:scale-[0.96] transition-transform")}
							type="button"
						>
							{authSignInCopy.submit}
						</Button>
					</motion.div>
					<motion.p className="text-center text-muted-foreground text-sm" variants={itemVariants}>
						{authSignInCopy.switchPrompt}{" "}
						<Link className="text-foreground underline-offset-4 hover:underline" href="#">
							{authSignInCopy.switchAction}
						</Link>
					</motion.p>
				</motion.form>
			</ContentRail>
		</SectionShell>
	);
}
```
