docs(04): UI design contract
This commit is contained in:
parent
16bc4aa5c9
commit
bf780790d5
1 changed files with 18 additions and 9 deletions
|
|
@ -50,7 +50,8 @@ Declared values (multiples of 4). Pulled from existing mobile page patterns and
|
|||
|-------|-------|---------------------|
|
||||
| xs | 4px | Icon gaps (`gap-1`), badge padding (`px-1.5 py-0.5`) |
|
||||
| sm | 8px | Row internal gaps (`gap-2`), filter chip gaps (`gap-2`) |
|
||||
| md | 16px | Horizontal page padding (`px-4`), filter panel `px-4` |
|
||||
| sm+ | 12px (3 × 4) | Secondary stacking, filter strip `pb-3` |
|
||||
| md | 16px | Horizontal page padding (`px-4`), filter panel `px-4`, list row vertical padding (`py-4`) |
|
||||
| lg | 24px | Vertical section breaks between filter header and list |
|
||||
| xl | 32px | Empty-state vertical padding (`py-8`) |
|
||||
| 2xl | 48px | Empty-state with no-results full-screen centering (`py-12`) |
|
||||
|
|
@ -65,13 +66,15 @@ Exceptions: 4px-wide priority stripe is a visual mark, not an interactive target
|
|||
|
||||
All sizes are Tailwind defaults read from `DESIGN.md §2` and matched to the existing `app/mobile/tickets/page.tsx` patterns.
|
||||
|
||||
Two weights only: `font-normal` (400) and `font-semibold` (600). `font-medium` (500) is not used in this phase.
|
||||
|
||||
| Role | Size class | Weight | Line Height | Font | Usage |
|
||||
|------|-----------|--------|-------------|------|-------|
|
||||
| Row title | `text-sm` (14px) | `font-semibold` (600) | `leading-snug` (1.375) | IBM Plex Sans | Ticket title in list row — 1-line truncate |
|
||||
| Body / secondary | `text-xs` (12px) | `font-normal` (400) | `leading-normal` (1.5) | IBM Plex Sans | Company name, age, assignee in row |
|
||||
| Ticket number / ID | `text-[10px]` (10px) | `font-mono` / `font-normal` (400) | `leading-normal` | IBM Plex Mono | Ticket # badge in row (`font-mono`) |
|
||||
| Detail breadcrumb | `text-sm` (14px) | `font-medium` (500) | `leading-normal` | IBM Plex Sans | "Tickets / #T20250001" in detail header |
|
||||
| Filter label | `text-xs` (12px) | `font-medium` (500) | `leading-none` | IBM Plex Sans | Chip labels (Open, High, etc.) |
|
||||
| Detail breadcrumb | `text-sm` (14px) | `font-semibold` (600) | `leading-normal` | IBM Plex Sans | "Tickets / #T20250001" in detail header |
|
||||
| Filter label | `text-xs` (12px) | `font-semibold` (600) | `leading-none` | IBM Plex Sans | Chip labels (Open, High, etc.) |
|
||||
| Count / status line | `text-xs` (12px) | `font-normal` (400) | `leading-normal` | IBM Plex Sans | "N open tickets" beneath filter strip |
|
||||
|
||||
Heading note: No `text-2xl` page title is rendered by the list page — the shell HeaderBar owns the brand mark; the list page opens directly with the filter strip. The detail page retains its existing `text-base font-bold` ticket title (body is out of scope).
|
||||
|
|
@ -85,7 +88,7 @@ All colors use CSS variable tokens from `app/globals.css` + `app/styles/brand.cs
|
|||
| Role | Token / Class | Usage |
|
||||
|------|--------------|-------|
|
||||
| Dominant surface (60%) | `bg-background` | Page background, filter strip background, row background |
|
||||
| Secondary surface (30%) | `bg-muted` / `bg-muted/50` | Ticket number badge, filter chip hover, row hover (`hover:bg-accent`) |
|
||||
| Secondary surface (30%) | `bg-muted` / `bg-muted/50` | Ticket number badge, filter chip hover, row hover (`hover:bg-muted/50`) |
|
||||
| Primary accent (10%) | `text-primary` / `bg-primary` / `bg-primary/15` | Active filter chip selected state (`bg-primary text-primary-foreground`) |
|
||||
| Muted text | `text-muted-foreground` | Company, age, assignee — all secondary row fields |
|
||||
| Border | `border-border` | Row dividers, filter strip border-b, filter chip borders |
|
||||
|
|
@ -108,6 +111,10 @@ These are direct Tailwind palette references (not CSS variable tokens) per the D
|
|||
|
||||
## Component Inventory
|
||||
|
||||
### Primary Visual Anchor
|
||||
|
||||
The primary focal point on the list page is the ticket title row. The `text-sm font-semibold` title creates a clear hierarchy against the `text-xs font-normal` secondary metadata (company, age, queue) beneath it. Every row is visually anchored to this title line — readers land there first, then scan down to context.
|
||||
|
||||
### Filter Strip [D-01 through D-04] — Collapsible
|
||||
|
||||
**Container:** `sticky top-0 bg-background z-10 border-b px-4 pt-4 pb-3 space-y-2`
|
||||
|
|
@ -131,13 +138,13 @@ These are direct Tailwind palette references (not CSS variable tokens) per the D
|
|||
| Queue | `Select` (shadcn) | Sourced from existing `/api/mobile/tickets` queue list | `queue` (int) |
|
||||
| Assigned to me | `Switch` or `Toggle` (shadcn) | on/off | `mine` (`1`/absent) |
|
||||
|
||||
Filter chips: `shrink-0 px-3 py-1 rounded-full text-xs font-medium border transition-colors` — active: `bg-primary text-primary-foreground border-primary`; inactive: `border-border hover:bg-accent`
|
||||
Filter chips: `shrink-0 px-3 py-1 rounded-full text-xs font-semibold border transition-colors` — active: `bg-primary text-primary-foreground border-primary`; inactive: `border-border hover:bg-muted/50`
|
||||
|
||||
"Clear all" button: `text-xs text-muted-foreground underline`, appears only when ≥1 filter is active [D-04]
|
||||
|
||||
### Ticket List Row [D-15 through D-17]
|
||||
|
||||
Outer wrapper: `<Link>` — `flex items-start border-l-4 {priority-border-class} px-4 py-3.5 hover:bg-accent transition-colors active:bg-accent`
|
||||
Outer wrapper: `<Link>` — `flex items-start border-l-4 {priority-border-class} px-4 py-4 hover:bg-muted/50 transition-colors active:bg-muted/50`
|
||||
|
||||
Row interior structure:
|
||||
```
|
||||
|
|
@ -157,7 +164,7 @@ Row dividers: `divide-y` on the list container — matches existing pattern.
|
|||
- Sentinel: `<div ref={sentinelRef} aria-hidden="true" />` at list end
|
||||
- `IntersectionObserver` with `rootMargin: '200px'` fires `fetchNextPage()` when sentinel enters viewport
|
||||
- Guard: no-op if `loadingMore || !hasMore`
|
||||
- Load more button: `w-full py-3 rounded-xl border text-sm font-medium hover:bg-accent transition-colors disabled:opacity-50` — always rendered when `hasMore`, focusable, triggers same fetch path
|
||||
- Load more button: `w-full py-3 rounded-xl border text-sm font-semibold hover:bg-muted/50 transition-colors disabled:opacity-50` — always rendered when `hasMore`, focusable, triggers same fetch path
|
||||
- Loading more indicator: small `Loader2` icon (`w-4 h-4 animate-spin text-muted-foreground`) centered above the Load more button during in-flight requests
|
||||
|
||||
### Detail Page In-Page Header [D-18]
|
||||
|
|
@ -168,7 +175,7 @@ New header bar: `flex items-center justify-between px-4 py-3 border-b`
|
|||
|
||||
Left slot: `<button>` with `ArrowLeft` icon (h-4 w-4) + "Tickets" text, `router.back()`, `text-sm text-muted-foreground hover:text-foreground`
|
||||
|
||||
Center slot: breadcrumb — `"Tickets / #" + ticket_number` in `text-sm font-medium` (truncate if needed)
|
||||
Center slot: breadcrumb — `"Tickets / #" + ticket_number` in `text-sm font-semibold` (truncate if needed)
|
||||
|
||||
Right slot: `<a>` to desktop ticket URL (`/analyzer/ticket/{id}` or Autotask direct URL) with `ExternalLink` icon (h-4 w-4), `text-muted-foreground hover:text-foreground`, `target="_blank" rel="noopener noreferrer"`, `aria-label="Open on desktop"`
|
||||
|
||||
|
|
@ -234,6 +241,7 @@ Error state: `toast.error()` via sonner + Load more button label changes to "Ret
|
|||
- Detail back button: `aria-label="Back to Tickets"`
|
||||
- Detail external link: `aria-label="Open ticket on desktop"`
|
||||
- Priority stripe: decorative only; priority is also conveyed textually in the ticket number badge row context
|
||||
- Empty state refresh affordance: icon-only button (`RefreshCw` h-4 w-4) with `aria-label="Refresh ticket list"` — no visible text label
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -252,7 +260,7 @@ All copy locked from D-20, D-21, and REQUIREMENTS.md.
|
|||
| Empty state — filters active | "No tickets match your filters" | D-20 |
|
||||
| Empty state CTA — filters active | "Clear filters" (button) | D-20 |
|
||||
| Empty state — no filters, no tickets | "No tickets to triage right now" | D-20 |
|
||||
| Empty state refresh affordance | "Refresh" (icon button) | D-20 |
|
||||
| Empty state refresh affordance | icon-only (`RefreshCw`) with `aria-label="Refresh ticket list"` — no visible text | D-20 |
|
||||
| Initial load state | 5 skeleton rows (no text) | D-21 |
|
||||
| Load more button (idle) | "Load more" | D-14 |
|
||||
| Load more button (loading) | "Loading…" (Loader2 spinner) | D-21 |
|
||||
|
|
@ -355,4 +363,5 @@ Per D-19 and phase boundary:
|
|||
|
||||
*Phase: 04-tickets-restyle*
|
||||
*UI-SPEC created: 2026-05-03*
|
||||
*UI-SPEC revised: 2026-05-03 (checker blocking fixes — typography weights, spacing, color contradiction, visual anchor, copywriting, spacing scale)*
|
||||
*Source decisions: 04-CONTEXT.md D-01 through D-21 (all locked)*
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue