# Faq 16

A shadcn/ui block from the [@diarmuradi](https://diarmuradi.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/diarmuradi/faq-16).

- Page, with a live preview: https://shadcn.exchange/r/diarmuradi/faq-16
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/diarmuradi/faq-16-light-4515b6aaa61dfd6a.png
- Registry item: https://diarmuradi.com/r/faq-16.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://diarmuradi.com/r/faq-16.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: `@base-ui/react`
- Registry items: `https://diarmuradi.com/r/styles/base-nova/fancy-button.json`, `https://diarmuradi.com/r/styles/base-nova/toggle-group.json`

## Files

### `faq.tsx`

```tsx
"use client"

import { useId, useState } from "react"
import { ToggleGroup } from "@base-ui/react/toggle-group"

import { ToggleGroupItem } from "@/components/ui/toggle-group"
import { FancyButton } from "@/components/ui/fancy-button"

type FaqItem = {
  value: string
  question: string
  answer: string
  label: string
}

const faqs: FaqItem[] = [
  {
    value: "item-1",
    label: "Draft URL",
    question: "How do I share a draft before the domain is ready?",
    answer:
      "Every workspace gets a private preview URL as soon as you save. Send that link to reviewers. Attaching a custom domain later does not change the published content or the comments already left on the draft.",
  },
  {
    value: "item-2",
    label: "Roles",
    question: "Who can hit publish on day one?",
    answer:
      "The owner and anyone invited as Editor. Viewers can comment and request changes only. You can lock publishing from Settings → General until a launch checklist is complete.",
  },
  {
    value: "item-3",
    label: "Import",
    question: "Can I bring copy from Notion or Google Docs?",
    answer:
      "Yes. Paste into a page or use Import. Headings become sections and images land in the asset library. You can undo the import as a single action if the structure looks wrong.",
  },
  {
    value: "item-4",
    label: "Sample",
    question: "Is there a project I can duplicate?",
    answer:
      "Open the template gallery and duplicate “Launch example”. It ships with navigation, a form, and a blog index. Replace the copy; the structure stays.",
  },
  {
    value: "item-5",
    label: "Billing",
    question: "When do I actually need a card?",
    answer:
      "Add billing before a third editor or a custom domain. Until then the starter workspace stays free and you can invite two teammates.",
  },
]

export function Faq() {
  const answerId = useId()
  const [activeValue, setActiveValue] = useState(faqs[0].value)
  const current = faqs.find((item) => item.value === activeValue) ?? faqs[0]

  return (
    <section
      aria-label="FAQ"
      className="mx-auto flex w-full max-w-3xl flex-col gap-8 text-foreground motion-reduce:**:animate-none motion-reduce:**:transition-none"
    >
      <header className="flex flex-col gap-3">
        <p className="flex w-fit items-center gap-2 text-sm text-muted-foreground">
          <span className="size-1.5 rounded-full bg-primary" />
          Reader
        </p>
        <div className="flex flex-col gap-2">
          <h1 className="text-3xl font-semibold tracking-tight text-balance sm:text-4xl md:text-5xl">
            Read one answer
          </h1>
          <p className="text-muted-foreground">
            An index on the left. The full answer stays on the right.
          </p>
        </div>
      </header>

      <div className="grid gap-8 md:grid-cols-[14rem_1fr]">
        <ToggleGroup
          aria-label="Questions"
          orientation="vertical"
          value={[activeValue]}
          onValueChange={(value) => {
            if (value[0]) setActiveValue(value[0])
          }}
          className="flex w-full flex-col gap-1"
        >
          {faqs.map((item, index) => (
            <ToggleGroupItem
              value={item.value}
              key={item.value}
              aria-controls={answerId}
              className="h-auto w-full items-baseline justify-start gap-3 rounded-lg px-3 py-2 text-left font-normal whitespace-normal data-pressed:bg-muted data-pressed:text-foreground"
            >
              <span className="w-6 shrink-0 text-xs tabular-nums">
                {String(index + 1).padStart(2, "0")}
              </span>
              <span className="text-sm font-medium">{item.label}</span>
            </ToggleGroupItem>
          ))}
        </ToggleGroup>

        {current ? (
          <article
            id={answerId}
            aria-live="polite"
            className="flex flex-col gap-4 rounded-2xl bg-card p-6 shadow-elevated-sm md:min-h-80 md:p-8"
          >
            <p className="text-xs font-medium tracking-wide text-muted-foreground uppercase">
              {current.label}
            </p>
            <h2 className="text-2xl font-semibold tracking-tight">
              {current.question}
            </h2>
            <p className="text-muted-foreground">{current.answer}</p>
            <FancyButton size="lg" className="mt-auto w-fit">
              Book a walkthrough
            </FancyButton>
          </article>
        ) : null}
      </div>
    </section>
  )
}
```
