---
phase: 8
slug: social-sign-in
status: approved
shadcn_initialized: false
preset: none
created: 2026-05-15
---

# Phase 8 — UI Design Contract

> Visual and interaction contract for the Social Sign-in phase. This phase changes the login/signup surfaces and adds a minimal linked-providers account view. It should stay functional and restrained; the user will provide a more polished UI later.

---

## Design System

| Property | Value |
|----------|-------|
| Tool | none |
| Preset | not applicable |
| Component library | local templ components in `backend/internal/web/ui` |
| Icon library | none required for Phase 8 |
| Font | inherited app font |

### Existing Surface To Preserve

- Keep the centered auth card layout used by `LoginPage` and `SignupPage`.
- Keep the current card width: `w-full max-w-sm`.
- Keep the current card padding: `px-6 py-8`.
- Keep existing form labels, inputs, and validation error patterns.
- Do not introduce a marketing-style auth page, hero area, decorative background, split layout, or large illustration.

---

## Interaction Contract

### Login and Signup Pages

Both `/login` and `/signup` must show:

1. Page heading
2. Google provider button
3. A visual separator between the provider button and the email/password form
4. Existing email/password form
5. Existing inline validation/error rendering behavior

Provider button:

- Google is the only rendered social sign-in provider for now.
- The button links to `/auth/google/start` when configured.
- The button renders disabled when required Google config is missing.
- Disabled state must be visible and non-clickable.
- Disabled state copy must be short and plain, not diagnostic.
- The provider button must not submit the email/password form.
- Apple sign-in is disabled and must not render a button, disabled-state copy, or actionable route link.

Successful social sign-in redirects to `/`; no provider-specific welcome page.

### Linked Providers View

Add a protected, minimal linked-providers status view.

Required content:

- Heading: `Linked providers`
- One row for Google
- Each row shows one of:
  - `Connected`
  - `Not connected`
- If connected, show the stored provider email.
- No Apple row in Phase 8.
- No unlink action in Phase 8.
- No add-password UI in Phase 8.

The view may live at `/account` or `/account/providers`; planner decides route naming.

---

## Spacing Scale

Declared values (must be multiples of 4):

| Token | Value | Usage |
|-------|-------|-------|
| xs | 4px | Provider button inner gaps if icons are later added |
| sm | 8px | Button stack gap, inline provider row gap |
| md | 16px | Default form/provider group spacing |
| lg | 24px | Auth section separator margin |
| xl | 32px | Page card inner section breaks |
| 2xl | 48px | Account page section spacing |
| 3xl | 64px | Existing auth page top spacing equivalent |

Exceptions: none

Auth card layout:

- Heading margin-bottom: 24px (`mb-6`) remains acceptable.
- Form/provider section gap: 20px (`space-y-5`) remains acceptable because it already exists; do not introduce additional non-4px spacing.
- Provider button stack gap: 8px.
- Separator vertical margin: 16px above and below.

---

## Typography

| Role | Size | Weight | Line Height |
|------|------|--------|-------------|
| Body | 16px | 400 | 1.5 |
| Label | 14px | 500 | 1.25 |
| Helper / status | 14px | 400 | 1.4 |
| Heading | 24px | 600 | 1.25 |
| Account section heading | 20px | 600 | 1.3 |

Rules:

- Do not use viewport-scaled font sizes.
- Do not use negative letter spacing.
- Keep provider button labels at 16px / 600 if using `ui.Button`, matching existing primary button weight.
- Disabled explanatory copy uses 14px normal weight.

---

## Color

| Role | Value | Usage |
|------|-------|-------|
| Dominant (60%) | `#ffffff` | Auth card and account content surfaces |
| Secondary (30%) | `#f8fafc` | Page background or subtle status surfaces |
| Text | `#0f172a` | Primary headings/body |
| Muted text | `#64748b` | Helper text, disabled provider explanation |
| Border | `#e2e8f0` | Provider buttons, separators, provider status rows |
| Accent (10%) | `#2563eb` | Existing primary email/password submit button and focus ring only |
| Destructive | `#b91c1c` | Error text or future destructive actions only |

Accent reserved for:

- Existing primary email/password submit button
- Focus rings
- Links if needed

Provider buttons:

- Use neutral/outline styling rather than blue primary styling.
- Background: `#ffffff`
- Border: `#e2e8f0`
- Text: `#0f172a`
- Hover when enabled: `#f8fafc`
- Disabled background: `#f1f5f9`
- Disabled text: `#94a3b8`

---

## Copywriting Contract

| Element | Copy |
|---------|------|
| Google button | `Continue with Google` |
| Disabled Google button | `Google sign-in not configured` |
| Auth separator | `or` |
| Provider callback generic error | `Could not sign you in with this provider. Try another sign-in method.` |
| Unverified email error | `This provider did not return a verified email. Try another sign-in method.` |
| Social-only signup conflict | `An account already exists for this email. Sign in with your provider.` |
| Linked providers heading | `Linked providers` |
| Connected status | `Connected` |
| Not connected status | `Not connected` |
| No unlink copy | No visible copy needed; simply omit unlink controls |

Error copy rules:

- Do not expose provider tokens, codes, claim names, key IDs, or raw OAuth errors in the UI.
- Server logs may contain structured error categories, but UI copy stays generic unless the user can act on it.

---

## Component Contract

### Provider Button

Create a provider button pattern rather than reusing the solid blue primary submit button.

Acceptable implementation options:

- Extend `ui.Button` with neutral outline/disabled support.
- Add a small auth-local provider button templ component.

Required behavior:

- Full width inside the auth card.
- Stable height of at least 44px.
- Does not resize based on provider config.
- Uses native disabled semantics for button form controls, or `aria-disabled="true"` plus no actionable `href` for links.
- Keyboard focus ring is visible when enabled.

### Separator

Use a simple horizontal rule pattern:

- Left line
- Center text `or`
- Right line

No decorative icons or large text.

### Linked Provider Row

Each row:

- Provider name on the left.
- Status and email on the right or below on small screens.
- Bordered row or subtle card row.
- Minimum row height 44px.
- No nested cards inside cards.

---

## Responsive Contract

- Auth card remains centered and no wider than `max-w-sm`.
- Provider controls are stacked vertically on all viewports in Phase 8; do not use a two-column button layout.
- Linked providers view uses a single column on mobile.
- Text must not overflow provider buttons; if future localization makes labels long, allow wrapping before reducing font size.
- No horizontal scrolling.

---

## Accessibility Contract

- Provider buttons must be reachable by keyboard.
- Disabled provider controls must communicate disabled state to assistive tech.
- Separator `or` must not be announced as a form control.
- Error messages must be associated with the relevant auth surface and visible near the provider/form area.
- Connected provider rows should expose status text as real text, not only color.

---

## Registry Safety

| Registry | Blocks Used | Safety Gate |
|----------|-------------|-------------|
| shadcn official | none | not required |
| third-party blocks | none | not allowed for Phase 8 |

---

## Checker Sign-Off

- [x] Dimension 1 Copywriting: PASS
- [x] Dimension 2 Visuals: PASS
- [x] Dimension 3 Color: PASS
- [x] Dimension 4 Typography: PASS
- [x] Dimension 5 Spacing: PASS
- [x] Dimension 6 Registry Safety: PASS

**Approval:** approved 2026-05-15
