# Faqs 2

> Split FAQ with framed accordion, grid pattern, and registry-focused copy — matches the Nusaiba landing page.

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

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

## Install

```bash
npx shadcn@latest add https://nusaiba.dev/r/base-nova/faqs-2.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`, `accordion`, `@nusaiba/decor-icon`, `@magicui/grid-pattern`

## Files

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

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

export type FaqCategoryId = "install" | "plans" | "customize";

export type FaqItem = {
	id: string;
	category: FaqCategoryId;
	title: string;
	content: string;
};

export const faqCategories: { id: FaqCategoryId; label: string }[] = [
	{ id: "install", label: "Install" },
	{ id: "plans", label: "Plans" },
	{ id: "customize", label: "Customize" },
];

export const faqItems: FaqItem[] = [
	{
		id: "what-is",
		category: "install",
		title: `What is ${faqSite.name}?`,
		content: `${faqSite.name} is an open shadcn registry of motion-first landing sections — heroes, pricing, FAQs, footers, and more. Preview live, install into your repo with the CLI, and keep every file.`,
	},
	{
		id: "landing-pages",
		category: "plans",
		title: "Can I use blocks on a client landing page?",
		content:
			"Yes. Free blocks ship for personal and commercial projects. Pro blocks include a commercial license for client work — install once, edit the copy and tokens, and ship the page you own.",
	},
	{
		id: "install",
		category: "install",
		title: "How do I add a block to my project?",
		content: `Add the ${faqSite.namespace} registry to components.json, then run npx shadcn@latest add ${faqSite.namespace}/block-name. Files land in your codebase — no runtime dependency on ${faqSite.name}.`,
	},
	{
		id: "free-vs-pro",
		category: "plans",
		title: "What's included in Free vs Pro?",
		content:
			"Free blocks cover core landing patterns — hero, features, FAQ, CTA, footer. Pro unlocks advanced motion blocks, registry token install, lifetime updates, and commercial licensing for agency work.",
	},
	{
		id: "customize",
		category: "customize",
		title: "Can I change copy and layout after install?",
		content:
			"Every block is yours to edit. Swap headlines for your product, trim sections, restyle with your tokens, or remove motion — the source lives in your project, not behind a package import.",
	},
	{
		id: "frameworks",
		category: "install",
		title: "Which stacks are supported?",
		content:
			"Any React setup that runs shadcn/ui — Next.js, Vite, Remix, and others. Blocks use your existing components.json aliases and design tokens.",
	},
	{
		id: "motion",
		category: "customize",
		title: "Do animations respect reduced motion?",
		content:
			"Yes. Blocks check prefers-reduced-motion and fall back to static layouts. Motion is packaging — your landing page stays readable when users request less animation.",
	},
	{
		id: "copy-prompt",
		category: "install",
		title: "What is Copy Prompt?",
		content: `Each block page includes a Copy Prompt — a ready-made instruction for Cursor or other AI tools to install and adapt the section. Use it when you want a guided handoff instead of the CLI alone.`,
	},
	{
		id: "tokens",
		category: "customize",
		title: "Will blocks match my brand colors?",
		content:
			"Blocks use semantic tokens — background, foreground, muted, primary — from your globals.css. After install, they inherit your theme without hardcoded palette lock-in.",
	},
	{
		id: "preview",
		category: "install",
		title: "Can I preview before installing?",
		content: `Browse ${faqSite.domain}/blocks, toggle light and dark preview, and read the source explorer. When a section fits your landing page, copy the CLI command or prompt and add it to your repo.`,
	},
];

export const faqSplitHeader = {
	eyebrow: "FAQ",
	title: "Questions before you ship",
	description:
		"Quick answers about installing blocks, Pro licensing, and tailoring sections for your landing page.",
	supportLabel: "Browse the library",
	supportHref: "/blocks",
} as const;

export const faqLandingHeader = {
	eyebrow: "FAQ",
	title: `Questions about ${faqSite.name}`,
	description:
		"Quick answers about the registry, install flow, and what lands in your project after you run the CLI.",
	supportLabel: "Browse the library",
	supportHref: "/blocks",
} as const;

export const faqCenteredHeader = {
	eyebrow: "Support",
	title: "Common questions",
	description:
		"Everything you need to pick blocks, install with shadcn, and ship a polished landing page.",
} as const;

export function faqsByCategory(category: FaqCategoryId) {
	return faqItems.filter((item) => item.category === category);
}
```

### `registry/blocks/faqs/2/faqs-section.tsx`

```tsx
"use client";

import Link from "next/link";
import { useRef } from "react";
import { motion, useInView, useReducedMotion } from "motion/react";
import {
	Accordion,
	AccordionContent,
	AccordionItem,
	AccordionTrigger,
} from "@/components/ui/accordion";
import { DecorIcon } from "@/components/decor-icon";
import { GridPattern } from "@/components/ui/grid-pattern";
import { motionStagger, motionViewport } from "@/lib/motion-tokens";
import { faqItems, faqLandingHeader } from "../faqs-data";
import { SectionShell, ContentRail } from "@/components/layout-contract";

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

export function FaqsLandingContent() {
	const reduceMotion = useReducedMotion();
	const ref = useRef<HTMLDivElement>(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 (
		<div className="grid gap-12 lg:grid-cols-5 lg:gap-16" ref={ref}>
			<motion.div
				animate={inView ? "visible" : "hidden"}
				className="lg:col-span-2 lg:pt-2"
				initial="hidden"
				variants={{
					hidden: {},
					visible: { transition: { staggerChildren: motionStagger.item, delayChildren: 0.04 } },
				}}
			>
				<div className="max-w-2xl">
					<motion.p
						className="font-medium text-primary text-xs uppercase tracking-[0.25em]"
						variants={itemVariants}
					>
						{faqLandingHeader.eyebrow}
					</motion.p>
					<motion.h2
						className="mt-3 text-balance font-medium text-2xl tracking-tight md:text-4xl"
						variants={itemVariants}
					>
						{faqLandingHeader.title}
					</motion.h2>
					<motion.p
						className="mt-4 text-pretty text-muted-foreground text-sm md:text-base"
						variants={itemVariants}
					>
						{faqLandingHeader.description}
					</motion.p>
				</div>
				<motion.p className="mt-8 text-muted-foreground text-sm" variants={itemVariants}>
					Want to try first?{" "}
					<Link
						className="text-foreground underline-offset-4 hover:underline"
						href={faqLandingHeader.supportHref}
					>
						{faqLandingHeader.supportLabel}
					</Link>
				</motion.p>
			</motion.div>

			<motion.div
				animate={inView ? "visible" : "hidden"}
				className="relative overflow-hidden border border-border bg-background p-2 lg:col-span-3"
				initial="hidden"
				variants={itemVariants}
			>
				<DecorIcon position="top-left" />
				<DecorIcon position="top-right" />
				<GridPattern
					className="absolute inset-0 stroke-foreground/[0.04]"
					height={32}
					width={32}
				/>
				<Accordion className="relative bg-card/40 px-2">
					{faqItems.map((item) => (
						<AccordionItem className="px-2" key={item.id} value={item.id}>
							<AccordionTrigger className="py-4 text-left hover:no-underline">
								{item.title}
							</AccordionTrigger>
							<AccordionContent className="pb-4 text-muted-foreground text-sm leading-relaxed">
								{item.content}
							</AccordionContent>
						</AccordionItem>
					))}
				</Accordion>
			</motion.div>
		</div>
	);
}

export function FaqsLandingSection() {
	return (
		<SectionShell spacingMode="section">
			<ContentRail maxWidth="max-w-6xl">
				<FaqsLandingContent />
			</ContentRail>
		</SectionShell>
	);
}
```
