# Quickstart: UI Polish Visual Acceptance Scenarios Use this guide to manually verify each milestone after implementation. Run `docker compose up` before starting. Open the browser at `http://localhost:4200`. --- ## M1 — Design Token Layer **Goal**: Tokens exist; visual output is identical to before. 1. Open the library, upload, detail, and login pages. 2. Open browser DevTools → Elements → `` or ``. 3. Confirm `--bg`, `--surface`, `--accent`, `--danger` etc. are visible in computed styles. 4. Confirm no visible change in any view compared to before M1. --- ## M2 — Library View ### Loading skeleton 1. Open DevTools → Network → set throttle to "Slow 3G". 2. Hard-refresh the library page. 3. **Expect**: A grid of grey shimmer cards appears immediately; no blank white space; no layout jump when real images load in. ### Empty state 1. Ensure no images are uploaded (or use a fresh test database). 2. Open the library. 3. **Expect**: A centred empty-state panel with explanatory text and a prominent "Upload your first image" link. Clicking the link navigates to `/upload`. ### Error state 1. Stop the API container (`docker compose stop api`). 2. Hard-refresh the library. 3. **Expect**: An error card with a plain-language message and a "Retry" button. No blank grid, no raw status code. 4. Restart the API (`docker compose start api`) and click "Retry". 5. **Expect**: Images load successfully. ### Card polish 1. Hover over an image card. 2. **Expect**: Card lifts slightly (2 px translate) with a smooth transition. ### Broken image 1. Manually corrupt a storage key in the database (or unplug MinIO) and reload. 2. **Expect**: Card shows a grey placeholder graphic, not a broken-image browser icon. ### 375 px viewport 1. DevTools → Device toolbar → iPhone SE (375 × 667). 2. **Expect**: Cards stack, no horizontal scrollbar, all content readable. --- ## M3 — Upload View ### Drop-zone idle 1. Navigate to `/upload` (must be logged in). 2. **Expect**: A visually distinct drop-zone with dashed border and clear instructions. Submit button is disabled/greyed. ### In-progress 1. Select a large image file. 2. Click upload. 3. **Expect**: Button label changes to "Uploading…" and is disabled. A spinner or animated indicator is visible. ### Success 1. After a successful upload completes. 2. **Expect**: A green-tinted success banner with the filename, "Upload another" link, and "View in library" link. Banner disappears after 4 seconds. ### Validation error 1. Attempt to upload a `.txt` file. 2. **Expect**: An inline error message names the problem ("Unsupported file type"). The form is still usable — no page reload required. ### Network error 1. Stop the API mid-upload (or use DevTools → block the upload request). 2. **Expect**: A generic inline error with guidance to retry. Form remains usable. --- ## M4 — Detail View ### Loading skeleton 1. Set network throttle to Slow 3G. 2. Navigate directly to an image URL (e.g., `http://localhost:4200/images/`). 3. **Expect**: A grey rectangle skeleton for the image area and chip skeletons below. No blank page. ### Not-found state 1. Navigate to `http://localhost:4200/images/00000000-0000-0000-0000-000000000000`. 2. **Expect**: A styled not-found card with "Image not found" heading and a "Back to library" button. No blank page, no raw 404 text. ### Authenticated write controls 1. Log in and open a detail page. 2. **Expect**: Tag editing input and delete button are visible and clearly grouped. ### Unauthenticated view 1. Open the detail page in a private/incognito window (not logged in). 2. **Expect**: Image and read-only tag chips are visible. No tag input, no delete button. ### Tag error 1. While logged in, attempt to add a tag with invalid characters (e.g., `TAG!`). 2. **Expect**: An inline error in danger colour with a left accent border. Other tags and the image remain visible. ### Broken image 1. Open a detail page for an image whose storage object has been deleted. 2. **Expect**: A placeholder graphic replaces the image. Page layout is not broken. --- ## M5 — Login View ### Visual consistency 1. Open `/login` without being logged in. 2. **Expect**: Dark background matching the library; form centred in a surface card; same font and spacing as other pages. ### Field validation 1. Click the Sign In button without entering any credentials. 2. **Expect**: Inline validation messages appear on the username and password fields without a page reload. ### Invalid credentials error 1. Enter wrong credentials and submit. 2. **Expect**: A single error message below the form. Fields retain their values. ### In-progress state 1. Submit valid credentials (throttle network if needed to see the state). 2. **Expect**: Button label changes to "Signing in…" and is disabled while the request is in flight. --- ## M6 — App Shell ### Authenticated header 1. Log in and navigate between library, upload, and an image detail page. 2. **Expect**: A consistent 48 px header is present on all pages. Sign-out control is visible on the right. Header does not reflow content. ### Unauthenticated header 1. Open the library or detail page without logging in. 2. **Expect**: Header is present but sign-out control is absent. ### Sign out 1. Click the sign-out control in the header. 2. **Expect**: Redirected to `/login`. Header no longer shows sign-out control. Navigating to `/upload` redirects back to `/login`.