From 805b3065167c0043af2bd524bdf704996e42ff3a Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Sun, 19 Apr 2026 19:22:56 +0200 Subject: [PATCH] docs(35): UI design contract for bug-fixes phase Co-Authored-By: Claude Sonnet 4.6 --- .planning/phases/35-bug-fixes/35-UI-SPEC.md | 190 ++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 .planning/phases/35-bug-fixes/35-UI-SPEC.md diff --git a/.planning/phases/35-bug-fixes/35-UI-SPEC.md b/.planning/phases/35-bug-fixes/35-UI-SPEC.md new file mode 100644 index 0000000..727e704 --- /dev/null +++ b/.planning/phases/35-bug-fixes/35-UI-SPEC.md @@ -0,0 +1,190 @@ +--- +phase: 35 +slug: bug-fixes +status: draft +shadcn_initialized: false +preset: none +created: 2026-04-19 +--- + +# Phase 35 — UI Design Contract + +> Visual and interaction contract for Phase 35: bug-fixes. Generated by gsd-ui-researcher, verified by gsd-ui-checker. +> +> **Phase scope:** No new UI. All 5 fixes restore or polish existing interactions. The contract enforces consistency with patterns already established in the codebase — executor must not introduce new design tokens or visual patterns. + +--- + +## Design System + +| Property | Value | +|----------|-------| +| Tool | none (no shadcn — Tailwind v4 direct) | +| Preset | not applicable | +| Component library | none (custom components in `src/client/components/`) | +| Icon library | Lucide (curated subset via `src/client/lib/iconData.ts`) | +| Font | system-ui (browser default, no custom font declared) | + +Source: `src/client/app.css` (`@import "tailwindcss"` — no additional config), `CONTEXT.md` code_context, codebase scan. + +--- + +## Spacing Scale + +Declared values (must be multiples of 4). This phase does not introduce any new spacing — all values are the existing codebase standard. + +| Token | Value | Usage | +|-------|-------|-------| +| xs | 4px | Icon gaps, inline padding (e.g. `px-1.5 py-0.5` badge padding) | +| sm | 8px | Compact element spacing (e.g. `gap-1.5` badge rows) | +| md | 16px | Default card content padding (`p-4`) | +| lg | 24px | Section padding, modal padding (`p-6`) | +| xl | 32px | Layout gaps | +| 2xl | 48px | Major section breaks | +| 3xl | 64px | Page-level spacing | + +Exceptions: none for this phase. + +Source: Codebase scan of `ItemCard.tsx`, `GlobalItemCard.tsx`, `CandidateCard.tsx`. + +--- + +## Typography + +Matches existing codebase usage — no new type styles introduced in this phase. + +| Role | Size | Weight | Line Height | +|------|------|--------|-------------| +| Body | 14px (text-sm) | 400 (normal) | 1.5 | +| Label | 12px (text-xs) | 500 (medium) | 1.5 | +| Heading | 14px (text-sm) | 600 (semibold) | 1.2 | +| Display | 20px (text-xl) | 600 (semibold) | 1.2 | + +Source: Codebase scan of `ItemCard.tsx` (`text-sm font-semibold text-gray-900`), `GlobalItemCard.tsx` (`text-xs font-medium text-gray-400`), `login.tsx` (`text-xl font-semibold`). + +--- + +## Color + +No new colors introduced. All values are the existing Tailwind gray/blue/green palette already used across card components. + +| Role | Value | Usage | +|------|-------|-------| +| Dominant (60%) | `gray-50` (`#f9fafb`) | Page backgrounds, fallback image areas | +| Secondary (30%) | `white` + `gray-100` border | Cards (`bg-white rounded-xl border border-gray-100`) | +| Accent (10%) | `blue-50`/`blue-400` | Weight badges only | +| Destructive | `red-100`/`red-500` | Remove-from-setup hover only (existing `ItemCard` remove button) | + +Accent reserved for: weight value badges (`bg-blue-50 text-blue-400`). Price badges use `green-50`/`green-500`. Category badges use `gray-50`/`gray-600`. No new accent usage introduced this phase. + +Source: Codebase scan of `ItemCard.tsx`, `GlobalItemCard.tsx`. + +--- + +## Image Skeleton Contract (FIX-03) + +This phase adds image-specific loading states to all card types. The skeleton pattern must be identical across all three card components. + +**Pattern:** `animate-pulse` gray placeholder fills the image area while `imageUrl` is resolving. Matches the existing `SkeletonGrid` pattern already used in `src/client/routes/global-items/index.tsx` and `src/client/routes/index.tsx`. + +| Card | Image Area Selector | Skeleton Class | +|------|---------------------|----------------| +| `ItemCard` | `.aspect-[4/3]` container | `bg-gray-100 animate-pulse` | +| `CandidateCard` | image area container | `bg-gray-100 animate-pulse` | +| `GlobalItemCard` | `.aspect-[4/3]` container | `bg-gray-100 animate-pulse` | + +**Loading state trigger:** When `imageUrl` is truthy but the `` `onLoad` has not yet fired. Use React `useState` for a `loaded` boolean on each image. + +**Loaded state transition:** Fade-in via `opacity-0 → opacity-100 transition-opacity duration-200` on the `` tag once loaded. + +**Fallback (no image):** Existing no-image placeholder (category icon centered on `bg-gray-50`) — unchanged. + +Source: `CONTEXT.md` D-07/D-08, existing `animate-pulse` pattern in codebase. + +--- + +## Cursor Contract (FIX-05) + +All interactive elements must show `cursor-pointer`. Use Tailwind utility `cursor-pointer` — not a global CSS rule. + +| Element Type | Cursor Rule | Notes | +|---|---|---| +| `