docs(02): UI design contract for Phase 2 — Users & Authentication
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
---
|
||||
phase: 2
|
||||
slug: users-authentication
|
||||
status: draft
|
||||
status: approved
|
||||
shadcn_initialized: false
|
||||
preset: none
|
||||
created: 2026-05-22
|
||||
@@ -44,8 +44,8 @@ Declared values (multiples of 4 only):
|
||||
|
||||
Exceptions:
|
||||
- Touch targets (buttons, checkboxes, radio): minimum 44px height — enforced via `min-h-[44px]` on all interactive controls
|
||||
- TOTP code input cells: 48px wide × 56px tall per digit cell (6 cells)
|
||||
- Backup code grid cells: 8px horizontal padding, 12px vertical padding (between xs and sm)
|
||||
- TOTP code input cells: 48px wide × 56px tall per digit cell (6 cells). Justification: 48px (scale) is too compact for touch-target comfort on a digit-only input; 64px wastes vertical space — 56px balances touch ergonomics and form compactness.
|
||||
- Backup code grid cells and text inputs: 12px vertical/horizontal padding (`py-3 px-3` = 12px). Justification: 16px inflates form card height beyond max-w-sm on mobile viewports; 8px collapses touch targets below 44px when combined with text-sm line height — 12px is the ergonomic midpoint. All instances use the same value for consistency.
|
||||
|
||||
---
|
||||
|
||||
@@ -54,13 +54,13 @@ Exceptions:
|
||||
| Role | Size | Weight | Line Height | Tailwind Class |
|
||||
|------|------|--------|-------------|----------------|
|
||||
| Body | 14px | 400 (regular) | 1.5 | `text-sm` |
|
||||
| Label | 14px | 500 (medium) | 1.4 | `text-sm font-medium` |
|
||||
| Label | 14px | 600 (semibold) | 1.4 | `text-sm font-semibold` |
|
||||
| Heading | 20px | 600 (semibold) | 1.3 | `text-xl font-semibold` |
|
||||
| Display | 24px | 700 (bold) | 1.2 | `text-2xl font-bold` |
|
||||
| Display | 24px | 600 (semibold) | 1.2 | `text-2xl font-semibold` |
|
||||
|
||||
**Source:** Extracted from `HomeView.vue` (`text-2xl font-bold` page heading, `text-sm` body, `text-lg font-semibold` section headings) and `DocumentCard.vue` (`font-medium text-sm`). Heading reduced from text-lg to text-xl for auth page section hierarchy.
|
||||
|
||||
Font weight scale: exactly 2 weights in components (regular 400 + semibold/bold 600/700). Labels use 500 (medium) as the only addition for form ergonomics.
|
||||
Font weight scale: exactly 2 weights — regular (400) for body text and helper text; semibold (600) for headings, labels, CTAs, and display text. Differentiation between roles relies on SIZE, not weight. Do not use `font-medium` (500) or `font-bold` (700) anywhere in Phase 2 components.
|
||||
|
||||
---
|
||||
|
||||
@@ -99,7 +99,7 @@ All text inputs (`<input type="text|email|password">`) follow this contract:
|
||||
| Disabled | `border-gray-200` | `bg-gray-50` | none | `text-gray-400` |
|
||||
| Read-only | `border-gray-200` | `bg-gray-50` | none | `text-gray-700` |
|
||||
|
||||
Base classes (all states share): `block w-full rounded-lg px-3 py-2.5 text-sm transition-colors`
|
||||
Base classes (all states share): `block w-full rounded-lg px-3 py-3 text-sm transition-colors`
|
||||
|
||||
Error messages appear **inline**, directly below the field, never as a toast. Class: `mt-1 text-xs text-red-600`. Maximum 1 error line per field.
|
||||
|
||||
@@ -123,7 +123,7 @@ Visual contract:
|
||||
| 4 (all rules pass) | 4 | `bg-green-500` | "Strong" |
|
||||
|
||||
Rules checked client-side (≥12 chars, uppercase, lowercase, digit, special char — matches AUTH-01):
|
||||
- Label: `text-xs font-medium` at same color as the bar, right-aligned
|
||||
- Label: `text-xs font-semibold` at same color as the bar, right-aligned
|
||||
- Unlit segments: `bg-gray-200`
|
||||
- Layout: `mt-2 space-y-1`
|
||||
|
||||
@@ -137,7 +137,7 @@ HaveIBeenPwned breach check runs on blur (not on every keystroke). If the passwo
|
||||
|
||||
- QR code: 200px × 200px centered in a white card with `border border-gray-200 rounded-xl p-6`
|
||||
- Manual secret: `font-mono text-sm text-gray-700 bg-gray-50 px-3 py-2 rounded-md border border-gray-200 break-all`
|
||||
- Copy secret button: icon-only (clipboard SVG, w-4 h-4), `text-gray-400 hover:text-gray-600`, positioned inline right of the secret block
|
||||
- Copy secret button: icon-only (clipboard SVG, w-4 h-4), `text-gray-400 hover:text-gray-600`, positioned inline right of the secret block. Must include `aria-label="Copy secret key"` for accessibility.
|
||||
- Instruction copy: "Open your authenticator app and scan this QR code, or enter the key manually."
|
||||
|
||||
**Step 2 — Code Verification**
|
||||
@@ -156,7 +156,7 @@ Each code cell:
|
||||
- `bg-gray-50 border border-gray-200 rounded-md px-3 py-2 text-center`
|
||||
- Do not number them (no 1., 2. prefix — prevents users from thinking order matters)
|
||||
|
||||
Copy-all button: `"Copy all codes"` — text button with clipboard icon, `text-indigo-600 hover:text-indigo-700 text-sm font-medium`. On click: copies newline-separated codes to clipboard, button text changes to `"Copied"` for 2 seconds.
|
||||
Copy-all button: `"Copy all codes"` — text button with clipboard icon, `text-indigo-600 hover:text-indigo-700 text-sm font-semibold`. On click: copies newline-separated codes to clipboard, button text changes to `"Copied"` for 2 seconds.
|
||||
|
||||
Acknowledgment checkbox (required before proceeding):
|
||||
- Checkbox + label: `"I have saved these codes in a secure place. I understand they will not be shown again."`
|
||||
@@ -226,11 +226,13 @@ Auth pages (`/login`, `/register`, `/reset-password`) share a single-column cent
|
||||
|
||||
- Page background: `bg-gray-50 min-h-screen flex items-center justify-center`
|
||||
- Card: `bg-white rounded-2xl shadow-sm border border-gray-200 p-8 w-full max-w-sm`
|
||||
- Logo/brand above card: `text-lg font-bold text-indigo-600 tracking-tight` centered, `mb-6`
|
||||
- Card heading: `text-2xl font-bold text-gray-900 mb-1`
|
||||
- Logo/brand above card: `text-xl font-semibold text-indigo-600 tracking-tight` centered, `mb-6`
|
||||
- Card heading: `text-2xl font-semibold text-gray-900 mb-1`
|
||||
- Card subheading: `text-sm text-gray-500 mb-6`
|
||||
- No sidebar on auth pages — full-width layout
|
||||
|
||||
Primary focal point of all auth screens is the card heading (Display / 24px / text-gray-900 font-semibold); secondary anchor is the primary CTA button (indigo-600 bg).
|
||||
|
||||
Auth pages are NOT wrapped in the main `App.vue` sidebar layout. They use a bare layout with no navigation.
|
||||
|
||||
---
|
||||
@@ -246,13 +248,13 @@ Sections (rendered as stacked cards with `space-y-6`):
|
||||
3. **Change password** — current password + new password + strength indicator
|
||||
4. **Sessions** — "Sign out all devices" with confirmation dialog
|
||||
|
||||
Role badge: `inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-indigo-100 text-indigo-700` for admin; `bg-gray-100 text-gray-600` for user.
|
||||
Role badge: `inline-flex items-center px-2 py-1 rounded text-xs font-semibold bg-indigo-100 text-indigo-700` for admin; `bg-gray-100 text-gray-600` for user.
|
||||
|
||||
**Sign-out-all confirmation dialog:**
|
||||
|
||||
- Not a browser `confirm()`. Rendered as an inline confirmation block that replaces the button on click.
|
||||
- Copy: "This will sign you out of all devices, including this one. You will need to sign in again."
|
||||
- Two buttons: "Cancel" (`text-gray-600 hover:text-gray-800 text-sm`) + "Sign out all devices" (`bg-red-600 hover:bg-red-700 text-white text-sm font-medium px-4 py-2 rounded-lg`)
|
||||
- Two buttons: "Keep signed in" (`text-gray-600 hover:text-gray-800 text-sm`) + "Sign out all devices" (`bg-red-600 hover:bg-red-700 text-white text-sm font-semibold px-4 py-2 rounded-lg`)
|
||||
- After confirmation: loading state on the destructive button, then redirect to `/login`
|
||||
|
||||
---
|
||||
@@ -264,8 +266,8 @@ Uses standard sidebar layout. Admin link in `AppSidebar.vue` visible only when `
|
||||
**Sub-navigation (within AdminView):**
|
||||
|
||||
Horizontal tab strip below the page heading:
|
||||
- Tab class (default): `px-4 py-2 text-sm font-medium text-gray-500 hover:text-gray-700 border-b-2 border-transparent`
|
||||
- Tab class (active): `px-4 py-2 text-sm font-medium text-indigo-600 border-b-2 border-indigo-600`
|
||||
- Tab class (default): `px-4 py-2 text-sm font-semibold text-gray-500 hover:text-gray-700 border-b-2 border-transparent`
|
||||
- Tab class (active): `px-4 py-2 text-sm font-semibold text-indigo-600 border-b-2 border-indigo-600`
|
||||
- Strip: `flex border-b border-gray-200 mb-6`
|
||||
- Tabs: "Users" | "Quotas" | "AI Config"
|
||||
|
||||
@@ -291,7 +293,7 @@ Actions column (per row): text links, separated by `·`
|
||||
|
||||
**Create User (Users tab):**
|
||||
|
||||
"Create user" button: positioned top-right of table, `bg-indigo-600 hover:bg-indigo-700 text-white text-sm font-medium px-4 py-2 rounded-lg`.
|
||||
"Create user" button: positioned top-right of table, `bg-indigo-600 hover:bg-indigo-700 text-white text-sm font-semibold px-4 py-2 rounded-lg`.
|
||||
|
||||
Create user form renders as an inline panel above the table (not a modal), with: email field, temporary password field (pre-generated, read-only with copy button), role selector (dropdown: User / Admin).
|
||||
|
||||
@@ -319,7 +321,7 @@ Provider and model: dropdown selectors (not free-text). Populated from the backe
|
||||
- Layout: `flex items-center gap-3 px-4 py-3 border-t border-gray-100`
|
||||
- Avatar: initials-based circle, 32px, `bg-indigo-100 text-indigo-700 text-xs font-semibold rounded-full w-8 h-8 flex items-center justify-center shrink-0`
|
||||
- Email: `text-xs text-gray-600 truncate flex-1`
|
||||
- Sign-out icon button: arrow-right-on-rectangle SVG (w-4 h-4), `text-gray-400 hover:text-gray-600`
|
||||
- Sign-out icon button: arrow-right-on-rectangle SVG (w-4 h-4), `text-gray-400 hover:text-gray-600`, `aria-label="Sign out"`
|
||||
|
||||
---
|
||||
|
||||
@@ -353,7 +355,7 @@ If the redirect was triggered by a 401 (token expired, not initial page load), s
|
||||
| New password success copy | "Password updated. Please sign in." (redirects to /login, not auto-login — per AUTH-05) |
|
||||
| TOTP step heading | "Two-factor authentication" |
|
||||
| TOTP step helper | "Enter the 6-digit code from your authenticator app." |
|
||||
| TOTP verify CTA | "Verify" |
|
||||
| TOTP verify CTA | "Verify code" |
|
||||
| TOTP backup code link | "Use a backup code instead" |
|
||||
| TOTP enrollment heading | "Set up two-factor authentication" |
|
||||
| TOTP backup codes heading | "Save your backup codes" |
|
||||
@@ -364,13 +366,14 @@ If the redirect was triggered by a 401 (token expired, not initial page load), s
|
||||
| Account page heading | "Account settings" |
|
||||
| Sign-out-all label | "Sign out all devices" |
|
||||
| Sign-out-all confirmation copy | "This will sign you out of all devices, including this one. You will need to sign in again." |
|
||||
| Sign-out-all cancel CTA | "Keep signed in" |
|
||||
| Sign-out-all destructive CTA | "Sign out all devices" |
|
||||
| Admin page heading | "Admin panel" |
|
||||
| Admin: create user CTA | "Create user" |
|
||||
| Admin: deactivate action | "Deactivate" |
|
||||
| Admin: reactivate action | "Reactivate" |
|
||||
| Admin: reset password action | "Reset password" |
|
||||
| Admin: deactivate confirmation | Inline: "Deactivate [email]? They will lose access immediately. Their data is preserved." + "Deactivate" / "Cancel" |
|
||||
| Admin: deactivate confirmation | Inline: "Deactivate [email]? They will lose access immediately. Their data is preserved." + "Deactivate" / "Keep account" |
|
||||
| Empty state (users table) | Heading: "No users yet" / Body: "Create the first user account to get started." |
|
||||
| Generic server error | "Something went wrong. Please try again or contact support if the problem persists." |
|
||||
| Login failure | "Incorrect email or password." |
|
||||
@@ -425,11 +428,11 @@ Components to create, with their file paths:
|
||||
|
||||
## Checker Sign-Off
|
||||
|
||||
- [ ] Dimension 1 Copywriting: PASS
|
||||
- [ ] Dimension 2 Visuals: PASS
|
||||
- [ ] Dimension 3 Color: PASS
|
||||
- [ ] Dimension 4 Typography: PASS
|
||||
- [ ] Dimension 5 Spacing: PASS
|
||||
- [ ] Dimension 6 Registry Safety: PASS
|
||||
- [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:** pending
|
||||
**Approval:** approved — 2026-05-22
|
||||
|
||||
Reference in New Issue
Block a user