# Phase 29: Image Presentation - Context **Gathered:** 2026-04-12 **Status:** Ready for planning ## Phase Boundary Replace hard-crop image display (`object-cover`) with fit-within framing across all image surfaces. Images are scaled to fit inside the aspect ratio container with adaptive dominant-color background fill. Users can adjust image framing via a zoom+pan editor available during upload and from item detail pages. ## Implementation Decisions ### Fit Strategy & Fill Treatment - **D-01:** Replace `object-cover` with `object-contain` across all image surfaces — images scale to fit inside the frame without cropping. - **D-02:** Fill remaining space with the image's **dominant color** extracted server-side. This creates an adaptive background that makes the image feel intentional rather than letterboxed. - **D-03:** Dominant color extraction happens **server-side on upload**, stored as a field (e.g., `dominantColor: '#abc123'`) on the item/globalItem record. No client-side computation. - **D-04:** Existing images need a **backfill migration** — process all existing images to extract and store their dominant color. ### Aspect Ratio Policy - **D-05:** Claude's discretion on whether to keep different ratios (4:3 cards, 16:9 global detail) or unify. Choose what looks best for gear product images. ### Scope of Changes - **D-06:** Apply the new presentation to **every surface where images appear**: ItemCard, GlobalItemCard, CandidateCard, CandidateListItem, item detail pages, global item detail pages, comparison table, ImageUpload preview, catalog search overlay. Full consistency — no exceptions. ### User Crop Positioning - **D-07:** Implement a **zoom + pan editor** — users can zoom in/out and drag to position the image within the frame. - **D-08:** Editor available in **two places**: during image upload (ImageUpload component) and from item detail/edit pages (re-adjustable anytime). - **D-09:** Crop settings stored **per-image** (not per-context). One set of zoom/pan coordinates applied everywhere the image appears. Store as fields on the image record (e.g., `cropZoom`, `cropX`, `cropY`). - **D-10:** When crop settings exist, they override the default `object-contain` behavior — the image is displayed at the user-specified zoom and position within the frame, with dominant color fill for any remaining space. ### Claude's Discretion - Zoom+pan editor component implementation (library vs custom) - Dominant color extraction algorithm (Sharp, node-vibrant, or similar) - DB schema for crop fields (on items table, globalItems table, or a separate image_settings table) - Backfill migration strategy (background job, on-demand, or one-time script) - Whether to generate server-side thumbnails for performance or keep CSS-only rendering ## Canonical References **Downstream agents MUST read these before planning or implementing.** ### Image Display Components (all need updating) - `src/client/components/ItemCard.tsx` — `aspect-[4/3]` + `object-cover` (line ~164-170) - `src/client/components/GlobalItemCard.tsx` — `aspect-[4/3]` + `object-cover` (line ~31-37) - `src/client/components/CandidateCard.tsx` — uses `object-cover` pattern - `src/client/components/CandidateListItem.tsx` — uses `object-cover` pattern - `src/client/components/ImageUpload.tsx` — `aspect-[4/3]` + `object-cover` (line ~72-79) - `src/client/components/ComparisonTable.tsx` — uses `object-cover` pattern - `src/client/components/LinkToGlobalItem.tsx` — uses `object-cover` pattern - `src/client/components/CatalogSearchOverlay.tsx` — uses `object-cover` pattern ### Image Detail Pages - `src/client/routes/items/$itemId.tsx` — `aspect-[4/3]` + `object-cover` (line ~245-250) - `src/client/routes/global-items/$globalItemId.tsx` — `aspect-[16/9]` + `object-cover` (line ~65-70) - `src/client/routes/threads/$threadId/candidates/$candidateId.tsx` — uses `object-cover` ### Server-Side Image Handling - `src/server/routes/images.ts` — Image upload endpoint - `src/server/services/storage.service.ts` — S3/MinIO storage service ### Database Schema - `src/db/schema.ts` — Items, globalItems tables (need dominantColor + crop fields) ## Existing Code Insights ### Reusable Assets - `ImageUpload` component — upload preview with aspect ratio container. Will host the zoom+pan editor. - S3/MinIO storage pipeline — images uploaded via `/api/images`, stored in MinIO, served from `/uploads/`. - Consistent `aspect-[4/3]` pattern across cards — refactoring can target this pattern systematically. ### Established Patterns - All image containers use `aspect-[ratio]` + overflow-hidden + `object-cover` on the ``. Switching to `object-contain` with background color is a targeted CSS change per component. - No existing image processing on upload — adding dominant color extraction introduces a new server-side processing step. ### Integration Points - `src/server/routes/images.ts` — Add dominant color extraction after upload - `src/db/schema.ts` — Add `dominantColor` field to items and globalItems - All card/detail components — Update image rendering to use contain + dominant color bg - `ImageUpload` component — Add zoom+pan editor overlay ## Specific Ideas - The adaptive dominant-color background should make images feel like they belong in the frame, not like they're floating in empty space. - The zoom+pan editor should be intuitive — drag to move, pinch/scroll to zoom. Not a complex crop tool. - Existing images all need backfill for dominant color — this affects catalog items seeded by MCP agents too. ## Deferred Ideas None — discussion stayed within phase scope --- *Phase: 29-image-presentation* *Context gathered: 2026-04-12*