# Blurred Image Placeholders

## Objective

Fade images in from a soft blurred preview instead of leaving a blank hole.

A tiny blurred preview generated at upload and shown in the image's exact final box until the full file loads.

## Before You Begin

This feature is being added to an application that already exists and already
works. Do not scaffold a new project, and do not assume a blank slate.

Inspect the codebase first and establish:

- The existing application structure and where code of this kind already lives.
- The framework and version in use.
- The existing design system — colours, spacing, typography, and component conventions.
- Existing UI components you can reuse instead of writing new ones.
- The existing database structure, if this feature needs to persist anything.
- The existing authentication and authorization system, if this feature is user-scoped.
- Dependencies already installed, so you don't add a library that duplicates one.
- The existing test setup and conventions.

Only start writing code once you understand the above. If the application
already implements part of this feature, extend it rather than replacing it.

## Implementation Instructions

1. Generate a very small preview once, during the upload pipeline that already produces the app's other image renditions, and store it alongside the image record.
2. Keep the preview small enough to be delivered with the page markup itself. If loading the placeholder needs its own request, it arrives no sooner than the image and buys nothing.
3. Reserve the image's exact final dimensions from its stored width and height, so the placeholder, the loaded image, and the failure state all occupy the same box.
4. Apply this to the image surfaces users actually wait on — feeds, galleries, article bodies, product grids — and reuse whatever Attachment Gallery or lightbox already exists rather than wrapping images in a second layer.
5. Do not generate previews on request. Producing a blurred thumbnail per page view puts image processing on the critical path of every render for no benefit.

## UI and UX Requirements

Match the application's existing design system exactly. Reuse its components,
spacing, and typography. This feature should look like it was always there.

## Responsive Requirements

Works on mobile, tablet, and desktop. Touch targets are large enough to hit on a
phone, and nothing overflows horizontally at 320px.

## Accessibility Requirements

- Fully keyboard navigable.
- Correct semantic elements and ARIA roles.
- Visible focus states.
- Meets WCAG AA contrast.
- Dynamic changes are announced to screen readers.
- Respects prefers-reduced-motion.

## Edge Cases

- The preview must be produced once at upload and stored, never derived per request, or it becomes a per-view cost on the busiest pages.
- Record the intrinsic width and height with the image and reserve that exact aspect ratio, so nothing on the page moves when the full file lands.
- An image already in the browser cache appears instantly, and fading it in from a blur makes a fast load look slow. Detect the completed load and skip the transition.
- Images that predate this feature, or whose processing failed, have no preview. Fall back to a flat neutral colour in the reserved box rather than leaving an empty gap.
- Cut the fade entirely when reduced motion is requested and show the image as soon as it is ready.
- A failed image load must resolve to a visible fallback with its alt text, not sit blurred forever as if it were still loading.
- The blurred preview is decorative. Alt text must describe the real image and must not be replaced or duplicated by the placeholder.
- Very small previews of sensitive images still convey their content. Serve them under the same access rules as the full image, not from an open path.

## Testing

Exercise the feature end to end in the running application. Cover every edge case
above, then run the existing test suite and confirm nothing regressed.

## Acceptance Criteria

- [ ] Every image with a stored preview shows it before the full file arrives.
- [ ] Previews are generated once during upload processing and never per request.
- [ ] Image boxes are reserved at their true aspect ratio and the page does not shift on load.
- [ ] Cached images render immediately with no fade.
- [ ] Images without a preview fall back to a flat colour placeholder.
- [ ] Reduced-motion preferences remove the transition.
- [ ] A failed load shows a visible fallback with alt text rather than a permanent blur.
- [ ] The feature matches the existing design system.
- [ ] No existing functionality is broken.

## Adaptation Rules

- Match the existing design system. Do not introduce a new colour palette,
  spacing scale, or component library.
- Reuse existing components and utilities wherever they fit.
- Follow the naming, file layout, and code style already present.
- Do not upgrade, replace, or remove existing dependencies to make this
  feature fit. Adapt the feature to the app, not the app to the feature.
- Do not break existing functionality. If a change is genuinely required in
  existing code, make the smallest one that works and say so.
- If something in these instructions conflicts with how the application is
  built, follow the application and explain the deviation.

## Final Verification

Before you report the work as done:

1. Re-read the acceptance criteria above and check each one against what you
   actually built.
2. Run the application and exercise the feature end to end.
3. Run the existing test suite and confirm you have broken nothing.
4. Check the feature on mobile, tablet, and desktop widths.
5. Check keyboard navigation and focus handling.
6. Summarize what changed: files added, files modified, and anything you
   deliberately did differently because of how this application is built.

If any acceptance criterion is unmet, fix it before reporting completion.
