{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "auth-block",
  "type": "registry:example",
  "title": "Auth Block",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "https://seamui.dev/r/motion.json",
    "https://seamui.dev/r/utils.json",
    "https://seamui.dev/r/card.json",
    "https://seamui.dev/r/form.json",
    "https://seamui.dev/r/field.json",
    "https://seamui.dev/r/input.json",
    "https://seamui.dev/r/button.json",
    "https://seamui.dev/r/separator.json",
    "https://seamui.dev/r/otp-field.json"
  ],
  "files": [
    {
      "path": "registry/seam/examples/auth-block.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { AnimatePresence, motion } from \"motion/react\"\n\nimport { depth, fades, reduced, springs, useReducedMotion } from \"@/lib/motion\"\nimport { Button } from \"@/registry/seam/ui/button\"\nimport {\n  Card,\n  CardContent,\n  CardDescription,\n  CardHeader,\n  CardTitle,\n} from \"@/registry/seam/ui/card\"\nimport { Field, FieldError, FieldLabel } from \"@/registry/seam/ui/field\"\nimport { Form } from \"@/registry/seam/ui/form\"\nimport { Input } from \"@/registry/seam/ui/input\"\nimport { OTPField } from \"@/registry/seam/ui/otp-field\"\nimport { Separator } from \"@/registry/seam/ui/separator\"\n\n// The auth block: a sign-in card that steps into 2FA. Everything is the\n// foundation composed — Card, Form/Field validation, an OTP second factor\n// that shakes on a rejected code — and the step swap is motion-owned\n// (AnimatePresence), so it rises with overlay depth and fades under\n// reduced motion.\ntype Step = \"credentials\" | \"code\" | \"done\"\n\nconst COPY: Record<Step, { title: string; desc: (email: string) => string }> = {\n  credentials: {\n    title: \"Sign in\",\n    desc: () => \"Use your work email to continue.\",\n  },\n  code: {\n    title: \"Check your phone\",\n    desc: (email) => `We sent a 6-digit code for ${email}.`,\n  },\n  done: { title: \"Welcome back\", desc: () => \"Two factors, zero doubt.\" },\n}\n\nexport default function AuthBlock() {\n  const [step, setStep] = React.useState<Step>(\"credentials\")\n  const [email, setEmail] = React.useState(\"\")\n  const [invalid, setInvalid] = React.useState(false)\n  const reduceMotion = useReducedMotion() ?? false\n\n  const stepMotion = reduceMotion\n    ? { ...reduced.fadeIn, transition: fades.normal }\n    : { ...depth.overlay, transition: springs.surface }\n\n  return (\n    <Card className=\"w-full max-w-sm\">\n      <CardHeader>\n        <CardTitle>{COPY[step].title}</CardTitle>\n        <CardDescription>{COPY[step].desc(email)}</CardDescription>\n      </CardHeader>\n      <CardContent>\n        {/* Always mounted so screen readers announce the final step. */}\n        <span className=\"sr-only\" aria-live=\"polite\">\n          {step === \"done\" ? `Signed in as ${email}.` : \"\"}\n        </span>\n        <AnimatePresence mode=\"wait\" initial={false}>\n          {step === \"credentials\" && (\n            <motion.div key=\"credentials\" {...stepMotion}>\n              <Form<{ email: string; password: string }>\n                onFormSubmit={(values) => {\n                  setEmail(values.email)\n                  setStep(\"code\")\n                }}\n              >\n                <Field name=\"email\">\n                  <FieldLabel>Email</FieldLabel>\n                  <Input\n                    type=\"email\"\n                    required\n                    placeholder=\"you@company.com\"\n                    autoComplete=\"email\"\n                  />\n                  <FieldError match=\"valueMissing\">\n                    Enter your email.\n                  </FieldError>\n                  <FieldError match=\"typeMismatch\">\n                    That doesn&apos;t look like an email.\n                  </FieldError>\n                </Field>\n                <Field name=\"password\">\n                  <FieldLabel>Password</FieldLabel>\n                  <Input\n                    type=\"password\"\n                    required\n                    minLength={8}\n                    placeholder=\"••••••••\"\n                    autoComplete=\"current-password\"\n                  />\n                  <FieldError match=\"valueMissing\">\n                    Enter your password.\n                  </FieldError>\n                  <FieldError match=\"tooShort\">\n                    At least 8 characters.\n                  </FieldError>\n                </Field>\n                <Button type=\"submit\" className=\"w-full\">\n                  Continue\n                </Button>\n                <div className=\"flex items-center gap-3\">\n                  <Separator className=\"flex-1\" />\n                  <span className=\"text-muted-foreground text-xs\">or</span>\n                  <Separator className=\"flex-1\" />\n                </div>\n                <Button type=\"button\" variant=\"outline\" className=\"w-full\">\n                  Continue with SSO\n                </Button>\n              </Form>\n            </motion.div>\n          )}\n\n          {step === \"code\" && (\n            <motion.div\n              key=\"code\"\n              {...stepMotion}\n              className=\"flex flex-col items-center gap-4\"\n            >\n              {/* Uncontrolled: the step unmounts on change, so the code\n                  resets for free. */}\n              <OTPField\n                length={6}\n                invalid={invalid}\n                onValueChange={() => setInvalid(false)}\n                onValueComplete={(v: string) => {\n                  if (v === \"123456\") setStep(\"done\")\n                  else setInvalid(true)\n                }}\n              />\n              <p className=\"text-muted-foreground text-xs\" aria-live=\"polite\">\n                {invalid\n                  ? \"That code didn't match — try again.\"\n                  : \"The demo code is 123456 — try a wrong one first.\"}\n              </p>\n              <Button\n                type=\"button\"\n                variant=\"ghost\"\n                size=\"sm\"\n                onClick={() => {\n                  setInvalid(false)\n                  setStep(\"credentials\")\n                }}\n              >\n                Use a different account\n              </Button>\n            </motion.div>\n          )}\n\n          {step === \"done\" && (\n            <motion.div\n              key=\"done\"\n              {...stepMotion}\n              className=\"flex flex-col items-center gap-4 py-4\"\n            >\n              <p className=\"text-sm\">\n                Signed in as <strong>{email}</strong>.\n              </p>\n              <Button\n                type=\"button\"\n                variant=\"outline\"\n                size=\"sm\"\n                onClick={() => {\n                  setInvalid(false)\n                  setStep(\"credentials\")\n                }}\n              >\n                Start over\n              </Button>\n            </motion.div>\n          )}\n        </AnimatePresence>\n      </CardContent>\n    </Card>\n  )\n}\n",
      "type": "registry:example"
    }
  ]
}