From 333978d7cba74080797a7338531f51a1734f32dd Mon Sep 17 00:00:00 2001 From: curo1305 Date: Fri, 22 May 2026 15:12:02 +0200 Subject: [PATCH] =?UTF-8?q?docs(02):=20UI=20design=20contract=20for=20Phas?= =?UTF-8?q?e=202=20=E2=80=94=20Users=20&=20Authentication?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- .../02-users-authentication/02-UI-SPEC.md | 57 ++++++++++--------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/.planning/phases/02-users-authentication/02-UI-SPEC.md b/.planning/phases/02-users-authentication/02-UI-SPEC.md index 4b5628d..622e2b8 100644 --- a/.planning/phases/02-users-authentication/02-UI-SPEC.md +++ b/.planning/phases/02-users-authentication/02-UI-SPEC.md @@ -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 (``) 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