docs(08): UI design contract

This commit is contained in:
Arthur Belleville 2026-05-15 20:45:27 +02:00
parent 6ac1dbd8fc
commit 3dfe054fcc
No known key found for this signature in database

View file

@ -0,0 +1,256 @@
---
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. Equal-prominence Google and Apple provider buttons
3. A visual separator between provider buttons and the email/password form
4. Existing email/password form
5. Existing inline validation/error rendering behavior
Provider buttons:
- Google and Apple are peers; neither is styled as primary over the other.
- Buttons link to provider start routes when configured.
- Buttons render disabled when required provider config is missing.
- Disabled state must be visible and non-clickable.
- Disabled state copy must be short and plain, not diagnostic.
- Provider buttons must not submit the email/password form.
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
- One row for Apple
- Each row shows one of:
- `Connected`
- `Not connected`
- If connected, show the stored provider email.
- 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` |
| Apple button | `Continue with Apple` |
| Disabled Google button | `Google sign-in not configured` |
| Disabled Apple button | `Apple 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 buttons 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