nusaiba
blockauth-1source: ok · hit
Auth 1
Split sign-in with brand rail, scroll-reveal stagger, and registry-native copy.
npx shadcn add "https://nusaiba.dev/r/base-nova/auth-1.json"free
Where it goes
logo-cloudlogin
Interaction
scroll-reveal
Use cases
saas-landing
Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.
Dependencies
Bundle weight · mediumanimationmotion
Registry dependencies
@nusaiba/foundationbuttoninputlabel
Files3 · source cached 10/14/2026, 12:25:03 PM
registry/blocks/auth/auth-data.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
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
"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>
);
}