docs(08): UI design contract
This commit is contained in:
parent
6ac1dbd8fc
commit
3dfe054fcc
1 changed files with 256 additions and 0 deletions
256
.planning/phases/08-social-sign-in/08-UI-SPEC.md
Normal file
256
.planning/phases/08-social-sign-in/08-UI-SPEC.md
Normal 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
|
||||||
Loading…
Reference in a new issue