{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "newsletter-4",
  "title": "Topic Picker Double Opt-In",
  "description": "Multi-step newsletter card where readers toggle topic interests, then land on a double opt-in check-your-inbox confirmation step.",
  "dependencies": [
    "@base-ui/react"
  ],
  "registryDependencies": [
    "button",
    "card",
    "checkbox",
    "input",
    "label"
  ],
  "files": [
    {
      "path": "registry/blocks/newsletter/4/newsletter-block.tsx",
      "content": "\"use client\"\n\nimport { useState } from \"react\"\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Card,\n  CardContent,\n  CardDescription,\n  CardHeader,\n  CardTitle,\n} from \"@/components/ui/card\"\nimport { Checkbox } from \"@/components/ui/checkbox\"\nimport { Input } from \"@/components/ui/input\"\nimport { Label } from \"@/components/ui/label\"\nimport { cn } from \"@/lib/utils\"\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\n/** An icon held in data and rendered later, e.g. `<item.icon className=\"size-4\" />`. */\ntype IconRenderer = (props: IconProps) => React.ReactNode\n\ntype Topic = {\n  id: string\n  label: string\n  description: string\n  icon: IconRenderer\n}\n\nconst topics: Topic[] = [\n  {\n    id: \"product\",\n    label: \"Product\",\n    description: \"Release notes & roadmap\",\n    icon: (p: IconProps) => (\n      <IconPlaceholder\n        lucide=\"Shapes\"\n        tabler=\"IconShape\"\n        hugeicons=\"ShapesIcon\"\n        phosphor=\"Shapes\"\n        remixicon=\"RiShapesLine\"\n        {...p}\n      />\n    ),\n  },\n  {\n    id: \"engineering\",\n    label: \"Engineering\",\n    description: \"Deep dives & best practices\",\n    icon: (p: IconProps) => (\n      <IconPlaceholder\n        lucide=\"Code\"\n        tabler=\"IconCode\"\n        hugeicons=\"CodeIcon\"\n        phosphor=\"Code\"\n        remixicon=\"RiCodeSSlashLine\"\n        {...p}\n      />\n    ),\n  },\n  {\n    id: \"design\",\n    label: \"Design\",\n    description: \"Systems, craft & process\",\n    icon: (p: IconProps) => (\n      <IconPlaceholder\n        lucide=\"Palette\"\n        tabler=\"IconPalette\"\n        hugeicons=\"PaintBoardIcon\"\n        phosphor=\"Palette\"\n        remixicon=\"RiPaletteLine\"\n        {...p}\n      />\n    ),\n  },\n]\n\nexport default function NewsletterBlock() {\n  const [email, setEmail] = useState(\"\")\n  const [selected, setSelected] = useState<string[]>([\"product\"])\n  const [submitted, setSubmitted] = useState(false)\n\n  function toggleTopic(id: string) {\n    setSelected((prev) =>\n      prev.includes(id) ? prev.filter((t) => t !== id) : [...prev, id]\n    )\n  }\n\n  function handleSubmit(e: React.FormEvent) {\n    e.preventDefault()\n    if (!email.trim() || selected.length === 0) return\n    setSubmitted(true)\n  }\n\n  return (\n    <section className=\"flex w-full items-center justify-center bg-muted/30 px-6 py-16 text-foreground\">\n      <div className=\"mx-auto w-full max-w-md\">\n        <Card className=\"[--card-spacing:--spacing(6)]\">\n          {submitted ? (\n            <CardContent className=\"flex flex-col items-center gap-5 py-6 text-center\">\n              <span className=\"flex size-14 items-center justify-center rounded-full bg-primary/10 text-primary ring-1 ring-primary/20\">\n                <IconPlaceholder\n                  lucide=\"Inbox\"\n                  tabler=\"IconInbox\"\n                  hugeicons=\"InboxIcon\"\n                  phosphor=\"Tray\"\n                  remixicon=\"RiInboxLine\"\n                  aria-hidden=\"true\"\n                  size={26}\n                />\n              </span>\n              <div className=\"flex flex-col gap-2\">\n                <h2 className=\"font-heading text-xl font-bold tracking-tight\">\n                  Check your inbox\n                </h2>\n                <p className=\"text-sm text-muted-foreground\">\n                  We sent a confirmation link to{\" \"}\n                  <span className=\"font-medium text-foreground\">{email}</span>.\n                  Click it to start receiving the Acme newsletter.\n                </p>\n              </div>\n\n              <div className=\"w-full rounded-lg border border-border bg-muted/40 p-4 text-left\">\n                <p className=\"text-xs font-medium text-muted-foreground\">\n                  Your selected topics\n                </p>\n                <ul className=\"mt-2 flex flex-wrap gap-1.5\">\n                  {topics\n                    .filter((t) => selected.includes(t.id))\n                    .map((t) => (\n                      <li\n                        key={t.id}\n                        className=\"inline-flex items-center gap-1 rounded-4xl bg-background px-2 py-1 text-xs font-medium ring-1 ring-border\"\n                      >\n                        <t.icon aria-hidden=\"true\" size={13} />\n                        {t.label}\n                      </li>\n                    ))}\n                </ul>\n              </div>\n\n              <Button\n                type=\"button\"\n                variant=\"outline\"\n                className=\"w-full\"\n                onClick={() => setSubmitted(false)}\n              >\n                Use a different email\n              </Button>\n            </CardContent>\n          ) : (\n            <>\n              <CardHeader>\n                <CardTitle className=\"text-xl tracking-tight\">\n                  Subscribe To Acme\n                </CardTitle>\n                <CardDescription className=\"text-sm\">\n                  Pick the topics you care about. One email per week, no noise.\n                </CardDescription>\n              </CardHeader>\n              <CardContent>\n                <form\n                  className=\"flex flex-col gap-5\"\n                  onSubmit={handleSubmit}\n                  noValidate\n                >\n                  <div className=\"flex flex-col gap-2\">\n                    <Label htmlFor=\"newsletter-email\">Email address</Label>\n                    <Input\n                      id=\"newsletter-email\"\n                      type=\"email\"\n                      required\n                      placeholder=\"you@company.com\"\n                      value={email}\n                      onChange={(e) => setEmail(e.target.value)}\n                    />\n                  </div>\n\n                  <fieldset className=\"flex flex-col gap-2\">\n                    <legend className=\"mb-2 text-sm font-medium\">\n                      What should we send you?\n                    </legend>\n                    {topics.map((topic) => {\n                      const active = selected.includes(topic.id)\n                      return (\n                        <Label\n                          key={topic.id}\n                          htmlFor={`topic-${topic.id}`}\n                          className={cn(\n                            \"flex cursor-pointer items-center gap-3 rounded-lg border p-3 font-normal transition-colors has-focus-visible:ring-2 has-focus-visible:ring-ring\",\n                            active\n                              ? \"border-primary bg-primary/5\"\n                              : \"border-border bg-background hover:bg-muted/50\"\n                          )}\n                        >\n                          <span\n                            className={cn(\n                              \"flex size-9 shrink-0 items-center justify-center rounded-md\",\n                              active\n                                ? \"bg-primary/10 text-primary\"\n                                : \"bg-muted text-muted-foreground\"\n                            )}\n                          >\n                            <topic.icon aria-hidden=\"true\" size={18} />\n                          </span>\n                          <span className=\"flex min-w-0 flex-1 flex-col\">\n                            <span className=\"text-sm font-medium\">\n                              {topic.label}\n                            </span>\n                            <span className=\"truncate text-xs text-muted-foreground\">\n                              {topic.description}\n                            </span>\n                          </span>\n                          <Checkbox\n                            id={`topic-${topic.id}`}\n                            checked={active}\n                            onCheckedChange={() => toggleTopic(topic.id)}\n                            className=\"size-5 shrink-0\"\n                            aria-label={topic.label}\n                          />\n                        </Label>\n                      )\n                    })}\n                  </fieldset>\n\n                  <Button\n                    type=\"submit\"\n                    className=\"w-full\"\n                    disabled={!email.trim() || selected.length === 0}\n                  >\n                    <IconPlaceholder\n                      lucide=\"Send\"\n                      tabler=\"IconSend\"\n                      hugeicons=\"MailSendIcon\"\n                      phosphor=\"PaperPlane\"\n                      remixicon=\"RiMailSendLine\"\n                      data-icon=\"inline-start\"\n                      aria-hidden=\"true\"\n                    />\n                    Subscribe\n                  </Button>\n\n                  <p className=\"flex items-center justify-center gap-1.5 text-xs text-muted-foreground\">\n                    <IconPlaceholder\n                      lucide=\"ShieldCheck\"\n                      tabler=\"IconShieldCheck\"\n                      hugeicons=\"Shield01Icon\"\n                      phosphor=\"ShieldCheck\"\n                      remixicon=\"RiShieldCheckLine\"\n                      aria-hidden=\"true\"\n                      className=\"shrink-0\"\n                      size={13}\n                    />\n                    Double opt-in. Unsubscribe anytime, no questions asked.\n                  </p>\n                </form>\n              </CardContent>\n            </>\n          )}\n        </Card>\n      </div>\n    </section>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/blocks/newsletter-4.tsx"
    }
  ],
  "meta": {
    "height": "650px",
    "tier": "free"
  },
  "categories": [
    "newsletter"
  ],
  "type": "registry:block"
}