diff --git a/docs/superpowers/specs/2026-03-24-login-dialog-design.md b/docs/superpowers/specs/2026-03-24-login-dialog-design.md new file mode 100644 index 0000000..6191d6a --- /dev/null +++ b/docs/superpowers/specs/2026-03-24-login-dialog-design.md @@ -0,0 +1,173 @@ +# 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 + 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** — social providers + credentials + all links +- **Credentials only** — no `socialProviders` passed, social section and divider hidden +- **Social only** — only `socialProviders` passed, credentials section hidden (make `onSubmit` optional for this case, or accept it will never fire) + +## 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 + +- `