{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "sign-in-form",
  "type": "registry:ui",
  "title": "Sign In Form",
  "description": "Canonical sign-in pattern: title + email + password + submit. Composition of the auth primitive set (Form / Field / Input / Button + Typography for the title). The block owns *structure*; consumers wire `action` / `onSubmit` and pass the brand specifics…",
  "author": "ofri-peretz <https://github.com/ofri-peretz>",
  "categories": [
    "form",
    "pattern"
  ],
  "dependencies": [],
  "registryDependencies": [
    "https://ds.interlace.tools/r/theme.json",
    "https://ds.interlace.tools/r/button.json",
    "https://ds.interlace.tools/r/form.json",
    "https://ds.interlace.tools/r/input.json",
    "https://ds.interlace.tools/r/stack.json",
    "https://ds.interlace.tools/r/typography.json"
  ],
  "files": [
    {
      "path": "registry/interlace-ui/patterns/sign-in-form.tsx",
      "target": "components/ui/patterns/sign-in-form.tsx",
      "type": "registry:ui",
      "content": "import * as React from 'react';\n\n// @interlace/sign-in-form v1.2.0 — Interlace design system.\n// Docs, props and live preview: https://ds.interlace.tools/c/sign-in-form\n// What changed since: https://ds.interlace.tools/c/sign-in-form#history\n// Generated banner — keep it, the upgrade diff reads this version.\n\n/**\n * @interlace/ui — SignInForm\n *\n * Canonical sign-in pattern: title + email + password + submit. Composition\n * of the auth primitive set (Form / Field / Input / Button + Typography for\n * the title). The block owns *structure*; consumers wire `action` /\n * `onSubmit` and pass the brand specifics (provider name, password-reset\n * link, sign-up link) via props.\n *\n * ## Anatomy\n *\n *   SignInForm                       (block — data-min-viewport=320)\n *     ├─ Typography variant=h3       (title)\n *     ├─ Typography variant=body     (subtitle — optional)\n *     ├─ Form                        (the <form> element from primitives/form.tsx)\n *     │   ├─ Field name=\"email\"\n *     │   │   ├─ FieldLabel\n *     │   │   └─ FieldControl render={<Input type=\"email\" />}\n *     │   ├─ Field name=\"password\"\n *     │   │   ├─ FieldLabel\n *     │   │   └─ FieldControl render={<Input type=\"password\" />}\n *     │   └─ Button type=\"submit\"\n *     └─ <slot for sign-up link>\n *\n * ## MIN_VIEWPORT — 320\n *\n * Auth must work on the narrowest phone: this is the smallest target the\n * WCAG 2.5.5 floor allows for the input + submit pair.\n *\n * | Rule | Concept                          | Where in this file                                          |\n * | ---- | -------------------------------- | ----------------------------------------------------------- |\n * | R4   | Extends React.ComponentProps     | `React.ComponentProps<'form'> & SignInFormProps`            |\n * | R6   | data-slot on root                | `data-slot=\"sign-in-form\"`                                  |\n * | R7   | className merged + ...rest       | `cn(...) + {...props}` on the Form root                     |\n * | R10  | Composition seam                 | `actions` + `footer` props slot consumer-supplied UI        |\n * | R13  | Ecosystem first                  | Built on the primitives layer; no bespoke form state        |\n * | R14  | Declares min viewport            | `data-min-viewport={String(MIN_VIEWPORT)}` + exported const |\n * | R18  | Tailwind only                    | Zero inline style; classes only                             |\n * | R19  | Tokens only                      | Spacing/typography from existing primitives                 |\n * | R25  | Server component                 | No hooks → no `'use client'`                                |\n * | R26  | A11y from primitives             | Base UI Field owns label association + aria-describedby    |\n */\n\nimport { Button } from '@/components/ui/button';\nimport { Field, FieldControl, FieldDescription, FieldError, FieldLabel, Form } from '@/components/ui/form';\nimport { Input } from '@/components/ui/input';\nimport { Stack } from '@/components/ui/stack';\nimport { Typography } from '@/components/ui/typography';\n\nexport const MIN_VIEWPORT = 320 as const;\n\ntype SignInFormProps = React.ComponentProps<'form'> & {\n  /** Headline above the form. Defaults to \"Sign in\". */\n  title?: React.ReactNode;\n  /** Optional supporting copy under the title. */\n  subtitle?: React.ReactNode;\n  /** Label on the submit button. Defaults to \"Sign in\". */\n  submitLabel?: React.ReactNode;\n  /** Slot beneath the submit button — typically a \"Forgot password?\" link. */\n  actions?: React.ReactNode;\n  /** Slot below the form — typically a \"Don't have an account? Sign up.\" prompt. */\n  footer?: React.ReactNode;\n};\n\nexport function SignInForm({\n  className,\n  title = 'Sign in',\n  subtitle,\n  submitLabel = 'Sign in',\n  actions,\n  footer,\n  ...props\n}: SignInFormProps) {\n  return (\n    <Stack\n      gap=\"lg\"\n      data-slot=\"sign-in-form\"\n      data-min-viewport={String(MIN_VIEWPORT)}\n      className=\"w-full max-w-96\"\n    >\n      <div>\n        <Typography as=\"h3\" variant=\"h3\">\n          {title}\n        </Typography>\n        {subtitle ? (\n          <Typography variant=\"body\" tone=\"muted\" className=\"mt-2\">\n            {subtitle}\n          </Typography>\n        ) : null}\n      </div>\n\n      <Form className={className} {...props}>\n        <Stack gap=\"md\">\n          <Field name=\"email\">\n            <FieldLabel>Email</FieldLabel>\n            <FieldControl\n              render={<Input type=\"email\" autoComplete=\"email\" required />}\n            />\n            <FieldError />\n          </Field>\n\n          <Field name=\"password\">\n            <FieldLabel>Password</FieldLabel>\n            <FieldControl\n              render={\n                <Input type=\"password\" autoComplete=\"current-password\" required />\n              }\n            />\n            <FieldDescription>\n              Use a unique password. We never store it in plain text.\n            </FieldDescription>\n            <FieldError />\n          </Field>\n\n          <Button type=\"submit\" className=\"mt-2 w-full\">\n            {submitLabel}\n          </Button>\n\n          {actions ? (\n            <div className=\"text-center text-sm\">{actions}</div>\n          ) : null}\n        </Stack>\n      </Form>\n\n      {footer ? (\n        <Typography variant=\"ui-sm\" tone=\"muted\" align=\"center\">\n          {footer}\n        </Typography>\n      ) : null}\n    </Stack>\n  );\n}\n"
    }
  ],
  "meta": {
    "tier": "pattern",
    "client": false,
    "minViewport": 320,
    "loading": false,
    "version": "1.2.0",
    "since": "1.0.0"
  },
  "docs": "## @interlace/sign-in-form\n\nInstalled to `components/ui/patterns/sign-in-form.tsx`.\n\n```tsx\nimport { /* … */ } from '@/components/ui/patterns/sign-in-form';\n```\n\nProps, a11y contract, live preview and source: https://ds.interlace.tools/c/sign-in-form\n\nRequires the `@interlace/theme` CSS baseline (installed automatically as a registry dependency)."
}
