{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "onboarding-3",
  "title": "Multi-Step Setup Wizard",
  "description": "Onboarding wizard card stepping through profile, workspace, invites, and a summary, with a progress bar, step indicator, and preference switches.",
  "dependencies": [
    "@base-ui/react"
  ],
  "registryDependencies": [
    "badge",
    "button",
    "card",
    "field",
    "input",
    "label",
    "progress",
    "separator",
    "sonner",
    "switch"
  ],
  "files": [
    {
      "path": "registry/blocks/onboarding/3/onboarding-block.tsx",
      "content": "\"use client\"\n\nimport { useState } from \"react\"\nimport { toast } from \"sonner\"\nimport { cn } from \"@/lib/utils\"\nimport { Badge } from \"@/components/ui/badge\"\nimport { Button } from \"@/components/ui/button\"\nimport { Card, CardContent, CardFooter, CardHeader } from \"@/components/ui/card\"\nimport { Field, FieldLabel } from \"@/components/ui/field\"\nimport { Input } from \"@/components/ui/input\"\nimport { Label } from \"@/components/ui/label\"\nimport { Progress } from \"@/components/ui/progress\"\nimport { Separator } from \"@/components/ui/separator\"\nimport { Switch } from \"@/components/ui/switch\"\nimport { Toaster } from \"@/components/ui/sonner\"\nimport { IconPlaceholder } from \"@/components/icons/icon-placeholder\"\n\n/** Props a call site may pass through to an icon. */\ntype IconProps = { className?: string; size?: number | string }\n\nconst steps = [\n  {\n    id: 1,\n    title: \"Profile\",\n    icon: (p: IconProps) => (\n      <IconPlaceholder\n        lucide=\"User\"\n        tabler=\"IconUser\"\n        hugeicons=\"UserIcon\"\n        phosphor=\"User\"\n        remixicon=\"RiUserLine\"\n        {...p}\n      />\n    ),\n  },\n  {\n    id: 2,\n    title: \"Workspace\",\n    icon: (p: IconProps) => (\n      <IconPlaceholder\n        lucide=\"Building2\"\n        tabler=\"IconBuilding\"\n        hugeicons=\"BuildingIcon\"\n        phosphor=\"Building\"\n        remixicon=\"RiBuilding2Line\"\n        {...p}\n      />\n    ),\n  },\n  {\n    id: 3,\n    title: \"Invite\",\n    icon: (p: IconProps) => (\n      <IconPlaceholder\n        lucide=\"Users\"\n        tabler=\"IconUsersGroup\"\n        hugeicons=\"UserGroupIcon\"\n        phosphor=\"UsersThree\"\n        remixicon=\"RiTeamLine\"\n        {...p}\n      />\n    ),\n  },\n  {\n    id: 4,\n    title: \"Done\",\n    icon: (p: IconProps) => (\n      <IconPlaceholder\n        lucide=\"Check\"\n        tabler=\"IconCheck\"\n        hugeicons=\"Tick02Icon\"\n        phosphor=\"Check\"\n        remixicon=\"RiCheckLine\"\n        {...p}\n      />\n    ),\n  },\n] as const\n\ntype Prefs = {\n  notifications: boolean\n  weeklyDigest: boolean\n  publicProfile: boolean\n}\n\nexport default function OnboardingBlock() {\n  const [step, setStep] = useState(1)\n  const [firstName, setFirstName] = useState(\"Jordan\")\n  const [lastName, setLastName] = useState(\"Rivera\")\n  const [role, setRole] = useState(\"Product Designer\")\n  const [workspace, setWorkspace] = useState(\"Acme HQ\")\n  const [prefs, setPrefs] = useState<Prefs>({\n    notifications: true,\n    weeklyDigest: true,\n    publicProfile: false,\n  })\n  const [invites, setInvites] = useState<string[]>([\n    \"casey@acme.com\",\n    \"morgan@acme.com\",\n  ])\n  const [inviteDraft, setInviteDraft] = useState(\"\")\n  const [finished, setFinished] = useState(false)\n\n  const total = steps.length\n  const isLast = step === total\n  const progress = (step / total) * 100\n\n  function next() {\n    setStep((s) => Math.min(s + 1, total))\n  }\n\n  function back() {\n    setStep((s) => Math.max(s - 1, 1))\n  }\n\n  function finish() {\n    setFinished(true)\n    toast.success(\"Welcome To Acme\", {\n      description: `Your workspace \"${workspace}\" is ready. ${invites.length} teammate${\n        invites.length === 1 ? \"\" : \"s\"\n      } invited.`,\n    })\n  }\n\n  function addInvite() {\n    const value = inviteDraft.trim()\n    if (!value || invites.includes(value)) return\n    setInvites((list) => [...list, value])\n    setInviteDraft(\"\")\n  }\n\n  function removeInvite(email: string) {\n    setInvites((list) => list.filter((e) => e !== email))\n  }\n\n  function togglePref(key: keyof Prefs) {\n    setPrefs((p) => ({ ...p, [key]: !p[key] }))\n  }\n\n  return (\n    <section className=\"flex w-full items-center justify-center bg-muted/30 px-6 py-16 text-foreground\">\n      <Card className=\"mx-auto w-full max-w-2xl\">\n        <CardHeader className=\"gap-6\">\n          <div className=\"flex items-center justify-between gap-4\">\n            <div className=\"flex items-center gap-2.5\">\n              <svg\n                viewBox=\"0 0 24 24\"\n                fill=\"currentColor\"\n                aria-hidden=\"true\"\n                className=\"size-6 shrink-0 text-primary\"\n              >\n                <rect\n                  x=\"3\"\n                  y=\"3\"\n                  width=\"8\"\n                  height=\"8\"\n                  transform=\"rotate(-6 7 7)\"\n                />\n                <rect\n                  x=\"3\"\n                  y=\"13\"\n                  width=\"8\"\n                  height=\"8\"\n                  transform=\"rotate(5 7 17)\"\n                />\n                <rect\n                  x=\"13\"\n                  y=\"13\"\n                  width=\"8\"\n                  height=\"8\"\n                  transform=\"rotate(-4 17 17)\"\n                />\n                <rect\n                  x=\"13\"\n                  y=\"3\"\n                  width=\"8\"\n                  height=\"8\"\n                  transform=\"rotate(15 17 7)\"\n                />\n              </svg>\n              <span className=\"text-sm font-semibold tracking-tight\">Acme</span>\n            </div>\n            <Badge variant=\"secondary\">\n              Step {step} of {total}\n            </Badge>\n          </div>\n\n          <div className=\"space-y-3\">\n            <Progress\n              value={progress}\n              aria-label={`Step ${step} of ${total}`}\n            />\n            <ol className=\"flex items-center justify-between\">\n              {steps.map((s) => {\n                const active = s.id === step\n                const done = s.id < step\n                const Icon = s.icon\n                return (\n                  <li key={s.id} className=\"flex flex-1 justify-center\">\n                    <button\n                      type=\"button\"\n                      onClick={() => setStep(s.id)}\n                      aria-current={active ? \"step\" : undefined}\n                      className=\"group flex cursor-pointer flex-col items-center gap-1.5 text-center transition-opacity hover:opacity-80 focus-visible:ring-1 focus-visible:ring-ring focus-visible:outline-none\"\n                    >\n                      <span\n                        className={cn(\n                          \"flex size-8 items-center justify-center rounded-full border text-xs font-medium transition-colors\",\n                          active &&\n                            \"border-primary bg-primary text-primary-foreground\",\n                          done &&\n                            \"border-primary/40 bg-primary/10 text-primary\",\n                          !active &&\n                            !done &&\n                            \"border-border bg-background text-muted-foreground group-hover:border-primary/40 group-hover:text-foreground\"\n                        )}\n                      >\n                        {done ? (\n                          <IconPlaceholder\n                            lucide=\"Check\"\n                            tabler=\"IconCheck\"\n                            hugeicons=\"Tick02Icon\"\n                            phosphor=\"Check\"\n                            remixicon=\"RiCheckLine\"\n                            className=\"size-4\"\n                            aria-hidden=\"true\"\n                          />\n                        ) : (\n                          <Icon className=\"size-4\" aria-hidden=\"true\" />\n                        )}\n                      </span>\n                      <span\n                        className={cn(\n                          \"text-xs font-medium\",\n                          active ? \"text-foreground\" : \"text-muted-foreground\"\n                        )}\n                      >\n                        {s.title}\n                      </span>\n                    </button>\n                  </li>\n                )\n              })}\n            </ol>\n          </div>\n        </CardHeader>\n\n        <CardContent className=\"min-h-64\">\n          {step === 1 && (\n            <div className=\"space-y-5\">\n              <div className=\"space-y-1\">\n                <h2 className=\"font-heading text-lg font-semibold tracking-tight\">\n                  Tell us about you\n                </h2>\n                <p className=\"text-sm text-muted-foreground\">\n                  This is how teammates will see you across Acme.\n                </p>\n              </div>\n              <div className=\"grid gap-4 sm:grid-cols-2\">\n                <Field>\n                  <FieldLabel htmlFor=\"first-name\">First name</FieldLabel>\n                  <Input\n                    id=\"first-name\"\n                    value={firstName}\n                    onChange={(e) => setFirstName(e.target.value)}\n                  />\n                </Field>\n                <Field>\n                  <FieldLabel htmlFor=\"last-name\">Last name</FieldLabel>\n                  <Input\n                    id=\"last-name\"\n                    value={lastName}\n                    onChange={(e) => setLastName(e.target.value)}\n                  />\n                </Field>\n              </div>\n              <Field>\n                <FieldLabel htmlFor=\"role\">Role</FieldLabel>\n                <Input\n                  id=\"role\"\n                  value={role}\n                  onChange={(e) => setRole(e.target.value)}\n                />\n              </Field>\n            </div>\n          )}\n\n          {step === 2 && (\n            <div className=\"space-y-5\">\n              <div className=\"space-y-1\">\n                <h2 className=\"font-heading text-lg font-semibold tracking-tight\">\n                  Workspace preferences\n                </h2>\n                <p className=\"text-sm text-muted-foreground\">\n                  Set up how your workspace behaves. Change these any time.\n                </p>\n              </div>\n              <Field>\n                <FieldLabel htmlFor=\"workspace\">Workspace name</FieldLabel>\n                <Input\n                  id=\"workspace\"\n                  value={workspace}\n                  onChange={(e) => setWorkspace(e.target.value)}\n                />\n              </Field>\n              <div className=\"divide-y divide-border rounded-lg border border-border\">\n                <PrefRow\n                  id=\"pref-notifications\"\n                  label=\"Product notifications\"\n                  description=\"Get notified about activity in your workspace.\"\n                  checked={prefs.notifications}\n                  onChange={() => togglePref(\"notifications\")}\n                />\n                <PrefRow\n                  id=\"pref-digest\"\n                  label=\"Weekly digest\"\n                  description=\"A summary of what shipped, every Monday.\"\n                  checked={prefs.weeklyDigest}\n                  onChange={() => togglePref(\"weeklyDigest\")}\n                />\n                <PrefRow\n                  id=\"pref-public\"\n                  label=\"Public profile\"\n                  description=\"Let others outside your team find you.\"\n                  checked={prefs.publicProfile}\n                  onChange={() => togglePref(\"publicProfile\")}\n                />\n              </div>\n            </div>\n          )}\n\n          {step === 3 && (\n            <div className=\"space-y-5\">\n              <div className=\"space-y-1\">\n                <h2 className=\"font-heading text-lg font-semibold tracking-tight\">\n                  Invite your team\n                </h2>\n                <p className=\"text-sm text-muted-foreground\">\n                  Acme is better together. Add teammates by email.\n                </p>\n              </div>\n              <Field>\n                <FieldLabel htmlFor=\"invite\">Email address</FieldLabel>\n                <div className=\"flex gap-2\">\n                  <Input\n                    id=\"invite\"\n                    type=\"email\"\n                    placeholder=\"name@acme.com\"\n                    value={inviteDraft}\n                    onChange={(e) => setInviteDraft(e.target.value)}\n                    onKeyDown={(e) => {\n                      if (e.key === \"Enter\") {\n                        e.preventDefault()\n                        addInvite()\n                      }\n                    }}\n                  />\n                  <Button type=\"button\" onClick={addInvite}>\n                    Add\n                  </Button>\n                </div>\n              </Field>\n              <ul className=\"space-y-2\">\n                {invites.map((email) => (\n                  <li\n                    key={email}\n                    className=\"flex items-center justify-between gap-3 rounded-lg border border-border bg-card px-3 py-2\"\n                  >\n                    <span className=\"flex min-w-0 items-center gap-2 text-sm\">\n                      <IconPlaceholder\n                        lucide=\"Mail\"\n                        tabler=\"IconMail\"\n                        hugeicons=\"MailIcon\"\n                        phosphor=\"Envelope\"\n                        remixicon=\"RiMailLine\"\n                        className=\"size-4 shrink-0 text-muted-foreground\"\n                        aria-hidden=\"true\"\n                      />\n                      <span className=\"truncate\">{email}</span>\n                    </span>\n                    <button\n                      type=\"button\"\n                      onClick={() => removeInvite(email)}\n                      className=\"text-xs font-medium text-muted-foreground transition-colors hover:text-foreground\"\n                    >\n                      Remove\n                    </button>\n                  </li>\n                ))}\n                {invites.length === 0 && (\n                  <li className=\"rounded-lg border border-dashed border-border px-3 py-6 text-center text-sm text-muted-foreground\">\n                    No invites yet. Add a teammate above.\n                  </li>\n                )}\n              </ul>\n            </div>\n          )}\n\n          {step === 4 && (\n            <div className=\"space-y-5\">\n              <div className=\"flex flex-col items-center gap-3 text-center\">\n                <span className=\"flex size-12 items-center justify-center rounded-lg bg-primary/10 text-primary\">\n                  <IconPlaceholder\n                    lucide=\"Check\"\n                    tabler=\"IconCheck\"\n                    hugeicons=\"Tick02Icon\"\n                    phosphor=\"Check\"\n                    remixicon=\"RiCheckLine\"\n                    className=\"size-6\"\n                    aria-hidden=\"true\"\n                  />\n                </span>\n                <div className=\"space-y-1\">\n                  <h2 className=\"font-heading text-lg font-semibold tracking-tight\">\n                    You&apos;re all set\n                  </h2>\n                  <p className=\"text-sm text-muted-foreground\">\n                    Review your setup, then jump into your workspace.\n                  </p>\n                </div>\n              </div>\n              <div className=\"rounded-lg border border-border bg-muted/40 p-4\">\n                <dl className=\"space-y-3 text-sm\">\n                  <SummaryRow\n                    label=\"Name\"\n                    value={`${firstName} ${lastName}`.trim() || \"Not set\"}\n                  />\n                  <Separator />\n                  <SummaryRow label=\"Role\" value={role || \"Not set\"} />\n                  <Separator />\n                  <SummaryRow\n                    label=\"Workspace\"\n                    value={workspace || \"Not set\"}\n                  />\n                  <Separator />\n                  <SummaryRow\n                    label=\"Invites\"\n                    value={`${invites.length} teammate${\n                      invites.length === 1 ? \"\" : \"s\"\n                    }`}\n                  />\n                </dl>\n              </div>\n            </div>\n          )}\n        </CardContent>\n\n        <CardFooter className=\"justify-between gap-3\">\n          <Button\n            type=\"button\"\n            variant=\"outline\"\n            onClick={back}\n            disabled={step === 1}\n          >\n            <IconPlaceholder\n              lucide=\"ArrowLeft\"\n              tabler=\"IconArrowLeft\"\n              hugeicons=\"ArrowLeftIcon\"\n              phosphor=\"ArrowLeft\"\n              remixicon=\"RiArrowLeftLine\"\n              data-icon=\"inline-start\"\n              className=\"size-4\"\n              aria-hidden=\"true\"\n            />\n            Back\n          </Button>\n          {isLast ? (\n            <Button type=\"button\" onClick={finish} disabled={finished}>\n              {finished ? \"All Set\" : \"Finish\"}\n              <IconPlaceholder\n                lucide=\"Check\"\n                tabler=\"IconCheck\"\n                hugeicons=\"Tick02Icon\"\n                phosphor=\"Check\"\n                remixicon=\"RiCheckLine\"\n                className=\"size-4\"\n                aria-hidden=\"true\"\n              />\n            </Button>\n          ) : (\n            <Button type=\"button\" onClick={next}>\n              Next\n              <IconPlaceholder\n                lucide=\"ArrowRight\"\n                tabler=\"IconArrowRight\"\n                hugeicons=\"ArrowRightIcon\"\n                phosphor=\"ArrowRight\"\n                remixicon=\"RiArrowRightLine\"\n                className=\"size-4\"\n                aria-hidden=\"true\"\n              />\n            </Button>\n          )}\n        </CardFooter>\n      </Card>\n      <Toaster />\n    </section>\n  )\n}\n\nfunction PrefRow({\n  id,\n  label,\n  description,\n  checked,\n  onChange,\n}: {\n  id: string\n  label: string\n  description: string\n  checked: boolean\n  onChange: () => void\n}) {\n  return (\n    <div className=\"flex items-start justify-between gap-4 px-4 py-3\">\n      <div className=\"space-y-0.5\">\n        <Label htmlFor={id} className=\"text-sm font-medium\">\n          {label}\n        </Label>\n        <p className=\"text-xs text-muted-foreground\">{description}</p>\n      </div>\n      <Switch id={id} checked={checked} onCheckedChange={onChange} />\n    </div>\n  )\n}\n\nfunction SummaryRow({ label, value }: { label: string; value: string }) {\n  return (\n    <div className=\"flex items-center justify-between gap-4\">\n      <dt className=\"text-muted-foreground\">{label}</dt>\n      <dd className=\"font-medium\">{value}</dd>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/blocks/onboarding-3.tsx"
    }
  ],
  "meta": {
    "height": "623px",
    "tier": "free"
  },
  "categories": [
    "onboarding"
  ],
  "type": "registry:block"
}