# Login Dialog Design Spec

## Overview

A composable login component for the Cameleer3 design system. Provides a `LoginForm` content component and a `LoginDialog` wrapper that puts it inside a Modal. Supports username/password credentials, configurable social/SSO providers, and built-in client-side validation.

## Components

### LoginForm

Core form component. Lives in `src/design-system/composites/LoginForm/`.

```tsx
interface SocialProvider {
  label: string        // e.g. "Continue with Google"
  icon?: ReactNode     // SVG icon, optional
  onClick: () => void
}

interface LoginFormProps {
  logo?: ReactNode
  title?: string                      // Default: "Sign in"
  socialProviders?: SocialProvider[]   // Omit or [] to hide social section + divider
  onSubmit?: (credentials: { email: string; password: string; remember: boolean }) => void  // Omit to hide credentials section
  onForgotPassword?: () => void       // Omit to hide link
  onSignUp?: () => void               // Omit to hide "Don't have an account?"
  error?: string                      // Server-side error, rendered as Alert
  loading?: boolean                   // Disables form, spinner on submit button
  className?: string
}
```

### LoginDialog

Thin wrapper — passes all `LoginFormProps` through to `LoginForm`, adds Modal control.

```tsx
interface LoginDialogProps extends LoginFormProps {
  open: boolean
  onClose: () => void
}
```

Uses `Modal` with `size="sm"` (400px).

## Layout

Social-first ordering, top to bottom:

1. **Logo slot** — optional `ReactNode` rendered centered above title
2. **Title** — "Sign in" default, centered
3. **Server error** — `Alert variant="error"` shown when `error` prop is set, between title and social buttons
4. **Social buttons** — stacked vertically, each is a `Button variant="secondary"` with icon + label. Hidden when `socialProviders` is empty/omitted.
5. **Divider** — horizontal rule with "or" text, centered. Hidden when social section is hidden.
6. **Email field** — `FormField` + `Input`, required, placeholder "you@example.com"
7. **Password field** — `FormField` + `Input type="password"`, required, placeholder "••••••••"
8. **Remember me / Forgot password row** — `Checkbox` on the left, amber link on the right. Forgot password link hidden when `onForgotPassword` omitted.
9. **Submit button** — `Button variant="primary"`, full width, label "Sign in"
10. **Sign up link** — "Don't have an account? Sign up" centered below. Hidden when `onSignUp` omitted.

### Configuration Variants

The form adapts automatically based on props:

- **Full** — `socialProviders` + `onSubmit` both provided. Social buttons, divider, and credentials all shown.
- **Credentials only** — `onSubmit` provided, no `socialProviders`. Social section and divider hidden.
- **Social only** — `socialProviders` provided, `onSubmit` omitted. Credentials section (email, password, remember me, submit button) and divider hidden.

## Validation

Client-side, triggered on form submit (not on blur):

| Field    | Rule                                              | Error message                          |
|----------|---------------------------------------------------|----------------------------------------|
| Email    | Required                                          | "Email is required"                    |
| Email    | Basic format: `/^[^\s@]+@[^\s@]+\.[^\s@]+$/`     | "Please enter a valid email address"   |
| Password | Required                                          | "Password is required"                 |
| Password | Minimum 8 characters                              | "Password must be at least 8 characters" |

- `onSubmit` only fires when all validation passes
- Field errors displayed inline below each input using `FormField` error pattern (red border + message)
- Field errors clear when the user starts typing in that field
- Server `error` prop clears automatically on next submit attempt

## States

### Loading

When `loading={true}`:
- All inputs disabled
- All social buttons disabled
- Submit button shows `Spinner` component, text hidden (matches existing `Button loading` pattern)
- Form cannot be submitted

### Error

- Server error: `Alert variant="error"` rendered between title and social buttons
- Field errors: inline below each input via `FormField` error styling (red border, error text)

## Styling

- CSS Modules: `LoginForm.module.css`
- All colors via CSS custom properties from `tokens.css`
- Dark mode works automatically — no extra overrides needed
- Social buttons: `var(--bg-surface)` background, `var(--border)` border, hover uses `var(--bg-hover)`
- Divider: `var(--border)` line, `var(--text-muted)` "or" text
- Forgot password + Sign up links: `var(--amber)` color, `font-weight: 500`
- Form gap: 14px between fields
- Social button gap: 8px between buttons

## Accessibility

- `<form>` element with `aria-label="Sign in"`
- Labels tied to inputs via `htmlFor`/`id`
- Error messages linked with `aria-describedby`
- First input auto-focused on mount
- `LoginDialog` traps focus via Modal
- Social buttons are `<button>` elements, keyboard-navigable
- Alert uses `role="alert"` for screen readers
- Enter key submits form (standard `<form onSubmit>`)

## File Structure

```
src/design-system/composites/LoginForm/
  LoginForm.tsx
  LoginForm.module.css
  LoginForm.test.tsx
  LoginDialog.tsx
  LoginDialog.test.tsx
```

Exports added to `src/design-system/composites/index.ts`.

## Primitives Reused

- `FormField` — label + error display wrapper
- `Input` — email and password fields
- `Checkbox` — remember me
- `Button` — submit (primary) + social buttons (secondary)
- `Alert` — server error display
- `Spinner` — loading state in submit button
- `Modal` — LoginDialog wrapper

## Testing

Tests with Vitest + React Testing Library, wrapped in `ThemeProvider`.

### LoginForm tests:
- Renders all elements when all props provided
- Hides social section when `socialProviders` is empty
- Hides divider when no social providers
- Hides forgot password link when `onForgotPassword` omitted
- Hides sign up link when `onSignUp` omitted
- Shows server error Alert when `error` prop set
- Validates required email
- Validates email format
- Validates required password
- Validates password minimum length
- Clears field errors on typing
- Calls `onSubmit` with credentials when valid
- Does not call `onSubmit` when validation fails
- Disables form when `loading={true}`
- Shows spinner on submit button when loading
- Calls social provider `onClick` when clicked
- Calls `onForgotPassword` when link clicked
- Calls `onSignUp` when link clicked

### LoginDialog tests:
- Renders Modal with LoginForm when `open={true}`
- Does not render when `open={false}`
- Calls `onClose` on backdrop click / Esc
- Passes all LoginForm props through
