# iOS Sign In

> Form, Sign in with Apple and a Face ID scanning sheet.

- Author: Atelier UI
- License: MIT
- Page: https://chromotion.dev/en/templates/ios-sign-in
- Site: Atelier UI (https://chromotion.dev)

## Install

```bash
npx shadcn@latest add https://chromotion.dev/r/ios-sign-in.json
```

Adds the page at `app/ios-sign-in/page.tsx` together with the components below. Stack: React + TypeScript + Tailwind CSS v4, shadcn/ui conventions. `cn` comes from `@/lib/utils`.

## Components

- [iOS Kit](https://chromotion.dev/r/ios-kit.md): `@/components/ios-kit`

## Dependencies

```bash
npm install lucide-react
```

## Source

```tsx title="app/ios-sign-in/page.tsx"
"use client"

import * as React from "react"
import { Apple, Check, ScanFace } from "lucide-react"

import { cn } from "@/lib/utils"
import { IOSButton, IOSDevice, IOSGroup, IOSSheet } from "@/components/ios-kit"

type Phase = "idle" | "scanning" | "success"

export default function IOSSignIn() {
  const [email, setEmail] = React.useState("")
  const [password, setPassword] = React.useState("")
  const [open, setOpen] = React.useState(false)
  const [phase, setPhase] = React.useState<Phase>("idle")
  const timers = React.useRef<ReturnType<typeof setTimeout>[]>([])

  React.useEffect(() => {
    const list = timers.current
    return () => list.forEach(clearTimeout)
  }, [])

  const start = () => {
    timers.current.forEach(clearTimeout)
    setOpen(true)
    setPhase("scanning")
    timers.current = [
      setTimeout(() => setPhase("success"), 1500),
      setTimeout(() => {
        setOpen(false)
        setPhase("idle")
      }, 2400),
    ]
  }

  const close = () => {
    timers.current.forEach(clearTimeout)
    setOpen(false)
    setPhase("idle")
  }

  return (
    <IOSDevice>
      <div className="flex min-h-0 flex-1 flex-col overflow-y-auto px-5 pt-24 pb-10 [scrollbar-width:none]">
        <div className="flex flex-col items-center text-center">
          <span className="flex size-[84px] items-center justify-center rounded-[20px] bg-gradient-to-br from-sky-400 to-indigo-600 text-[40px] font-bold text-white shadow-lg">A</span>
          <h1 className="mt-5 text-[28px] leading-8 font-bold">Sign in to Atelier</h1>
          <p className="mt-1 text-[15px] text-(color:--ios-label2)">Use your Atelier ID to sync everything.</p>
        </div>

        <form
          className="mt-8"
          onSubmit={(event) => {
            event.preventDefault()
            start()
          }}
        >
          <IOSGroup>
            <label className="relative flex items-center px-4 py-3 after:absolute after:right-0 after:bottom-0 after:left-4 after:h-px after:bg-(color:--ios-sep)">
              <span className="w-24 text-[17px]">Email</span>
              <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="name@example.com" autoComplete="email" className="min-w-0 flex-1 bg-transparent text-[17px] outline-none placeholder:text-(color:--ios-label3)" />
            </label>
            <label className="flex items-center px-4 py-3">
              <span className="w-24 text-[17px]">Password</span>
              <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} placeholder="Required" autoComplete="current-password" className="min-w-0 flex-1 bg-transparent text-[17px] outline-none placeholder:text-(color:--ios-label3)" />
            </label>
          </IOSGroup>

          <div className="mt-2 flex justify-end px-2">
            <button type="button" className="text-[15px] text-(color:--ios-blue) active:opacity-50">Forgot password?</button>
          </div>

          <IOSButton type="submit" className="mt-5 w-full" disabled={!email || !password}>Sign In</IOSButton>
        </form>

        <div className="my-6 flex items-center gap-3 text-[13px] text-(color:--ios-label3)">
          <span className="h-px flex-1 bg-(color:--ios-sep)" /> or <span className="h-px flex-1 bg-(color:--ios-sep)" />
        </div>

        <button type="button" className="flex h-[50px] w-full items-center justify-center gap-2 rounded-[14px] bg-black text-[17px] font-semibold text-white active:opacity-70 dark:bg-white dark:text-black">
          <Apple className="size-5 fill-current" /> Sign in with Apple
        </button>
        <button type="button" onClick={start} className="mt-3 flex h-[50px] w-full items-center justify-center gap-2 rounded-[14px] bg-(color:--ios-fill) text-[17px] font-semibold text-(color:--ios-blue) active:opacity-70">
          <ScanFace className="size-5" /> Use Face ID
        </button>

        <p className="mt-auto pt-8 text-center text-[13px] leading-[18px] text-(color:--ios-label2)">
          Your Apple ID information is used to enable Apple services when you sign in.
        </p>
      </div>

      <IOSSheet open={open} onClose={close} title="Face ID" height="46%">
        <div className="flex flex-col items-center gap-5 pt-10">
          <div className="relative flex size-24 items-center justify-center">
            <span
              className={cn(
                "absolute inset-0 rounded-[28px] border-[3px] transition-colors duration-300",
                phase === "success" ? "border-(color:--ios-green)" : "border-(color:--ios-blue)",
                phase === "scanning" && "animate-pulse"
              )}
            />
            {phase === "success" ? <Check className="size-12 animate-in text-(color:--ios-green) zoom-in-50" strokeWidth={3} /> : <ScanFace className="size-14 text-(color:--ios-blue)" strokeWidth={1.5} />}
          </div>
          <p className="text-[17px] font-medium" aria-live="polite">{phase === "success" ? "Success" : "Scanning your face…"}</p>
          <button type="button" onClick={close} className="text-[17px] text-(color:--ios-blue) active:opacity-50">Cancel</button>
        </div>
      </IOSSheet>
    </IOSDevice>
  )
}
```
