feat(ui): add visual asset pack and documentation (T03)
|
|
@ -0,0 +1,78 @@
|
|||
# Visual Asset Audit — T03 (Asset Pack)
|
||||
|
||||
Date: 2026-03-26
|
||||
Task: T03 — Visual Asset Pack
|
||||
Owner: agent-assets
|
||||
|
||||
## Summary
|
||||
|
||||
Implemented a legal-safe asset pack under `frontend/public/assets/` to support the redesign with:
|
||||
|
||||
1. Food imagery placeholders
|
||||
2. Category illustrations/icons
|
||||
3. Empty-state graphics
|
||||
4. Placeholder strategy for missing images
|
||||
|
||||
All new visual files are simple, in-repo SVG illustrations generated specifically for this project.
|
||||
No copyrighted third-party photos or icons were imported.
|
||||
|
||||
## Asset Inventory
|
||||
|
||||
### Food placeholders
|
||||
- `/assets/food/placeholder-recipe.svg` (wide hero fallback)
|
||||
- `/assets/food/placeholder-recipe-4x3.svg` (list/card ratio fallback)
|
||||
- `/assets/food/placeholder-recipe-1x1.svg` (square thumb fallback)
|
||||
- `/assets/food/placeholder-upload-dropzone.svg` (upload UI placeholder)
|
||||
|
||||
### Category icons
|
||||
- `/assets/category/icon-breakfast.svg`
|
||||
- `/assets/category/icon-lunch.svg`
|
||||
- `/assets/category/icon-dinner.svg`
|
||||
- `/assets/category/icon-dessert.svg`
|
||||
- `/assets/category/icon-snack.svg`
|
||||
|
||||
### Empty-state graphics
|
||||
- `/assets/empty-state/no-recipes.svg`
|
||||
- `/assets/empty-state/no-favorites.svg`
|
||||
- `/assets/empty-state/no-results-search.svg`
|
||||
|
||||
## Source & Attribution Notes
|
||||
|
||||
- **Source:** Created in-house (manual SVG composition)
|
||||
- **Attribution required:** None
|
||||
- **Third-party dependencies:** None for these files
|
||||
- **Legal status:** Safe to use and modify within this repository
|
||||
|
||||
## Icon Strategy
|
||||
|
||||
Current strategy for redesign wave:
|
||||
|
||||
- Use lightweight, project-owned SVG icons for category visuals and illustrative states
|
||||
- Keep semantic UI icons in code (emoji/text) where already present to avoid functional churn during visual phase
|
||||
- Optionally standardize on one icon library in a later task (e.g., Lucide React) if/when design system tokenization introduces centralized icon components
|
||||
|
||||
## Placeholder Strategy (Missing Images)
|
||||
|
||||
When recipe images are missing or fail to load:
|
||||
|
||||
1. **Primary display:** Use `/assets/food/placeholder-recipe-4x3.svg` for list cards
|
||||
2. **Detail hero fallback:** Use `/assets/food/placeholder-recipe.svg`
|
||||
3. **Square contexts (avatars/thumbs):** Use `/assets/food/placeholder-recipe-1x1.svg`
|
||||
4. **Upload workflows:** Show `/assets/food/placeholder-upload-dropzone.svg` before image selection
|
||||
5. **Error fallback:** On image load error, swap `src` to matching placeholder and set descriptive `alt` text (e.g., `"Recipe image placeholder"`)
|
||||
|
||||
Recommended accessibility behavior:
|
||||
- Keep `alt` text contextual to the recipe title when available
|
||||
- For decorative empty-state graphics, use empty alt (`alt=""`) plus nearby descriptive copy
|
||||
|
||||
## Usage Guidance
|
||||
|
||||
- Public URL pattern: `/assets/<group>/<file>.svg`
|
||||
- Prefer SVG scaling with CSS `object-fit: cover` for card contexts
|
||||
- Keep placeholders as deterministic defaults so UI is never image-empty
|
||||
|
||||
## Follow-up Suggestions
|
||||
|
||||
- Wire fallback logic in card/detail components (T04–T06)
|
||||
- Add visual regression screenshots once integrated
|
||||
- If future photography is required, only use assets with explicit commercial licenses and record attribution here
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
# Visual Asset Pack (T03)
|
||||
|
||||
This directory contains **copyright-safe, generated-in-project assets** for the visual redesign.
|
||||
|
||||
## Structure
|
||||
|
||||
- `food/` — recipe/photo placeholders for cards, detail hero, and upload dropzone states
|
||||
- `category/` — category icon SVGs (breakfast, lunch, dinner, dessert, snack)
|
||||
- `empty-state/` — illustrations for no recipes, no favorites, and no search results
|
||||
- `ui/` — reserved for additional decorative UI assets
|
||||
|
||||
## Usage Notes
|
||||
|
||||
- Reference from frontend as absolute public URLs, e.g. `/assets/food/placeholder-recipe.svg`
|
||||
- Prefer SVG placeholders over external stock images until approved imagery is available
|
||||
- Keep files lightweight and editable (plain SVG)
|
||||
|
||||
## License Safety
|
||||
|
||||
All assets in this folder were authored for this project and are safe for internal/commercial use under repository ownership.
|
||||
No third-party copyrighted graphics are included.
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Breakfast icon">
|
||||
<circle cx="64" cy="64" r="60" fill="#FEF3C7"/>
|
||||
<circle cx="64" cy="64" r="28" fill="#F59E0B"/>
|
||||
<circle cx="64" cy="64" r="16" fill="#FDE68A"/>
|
||||
<path d="M24 96H104" stroke="#92400E" stroke-width="8" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 378 B |
|
|
@ -0,0 +1,6 @@
|
|||
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Dessert icon">
|
||||
<circle cx="64" cy="64" r="56" fill="#FCE7F3"/>
|
||||
<path d="M34 84H94L86 50H42L34 84Z" fill="#F472B6"/>
|
||||
<circle cx="64" cy="44" r="12" fill="#FB7185"/>
|
||||
<rect x="61" y="22" width="6" height="14" rx="3" fill="#BE123C"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 367 B |
|
|
@ -0,0 +1,5 @@
|
|||
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Dinner icon">
|
||||
<circle cx="64" cy="64" r="52" fill="#E0E7FF"/>
|
||||
<circle cx="64" cy="64" r="30" stroke="#4F46E5" stroke-width="10"/>
|
||||
<path d="M38 92C46 84 54 80 64 80C74 80 82 84 90 92" stroke="#4F46E5" stroke-width="8" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 377 B |
|
|
@ -0,0 +1,5 @@
|
|||
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Lunch icon">
|
||||
<rect x="14" y="20" width="100" height="88" rx="18" fill="#DCFCE7"/>
|
||||
<rect x="36" y="34" width="56" height="60" rx="12" fill="#22C55E"/>
|
||||
<path d="M64 12V26" stroke="#166534" stroke-width="8" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 364 B |
|
|
@ -0,0 +1,7 @@
|
|||
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Snack icon">
|
||||
<circle cx="64" cy="64" r="56" fill="#FFEDD5"/>
|
||||
<rect x="36" y="30" width="56" height="68" rx="16" fill="#FB923C"/>
|
||||
<circle cx="64" cy="50" r="8" fill="#FDBA74"/>
|
||||
<circle cx="50" cy="68" r="6" fill="#FDBA74"/>
|
||||
<circle cx="78" cy="68" r="6" fill="#FDBA74"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 409 B |
|
|
@ -0,0 +1,6 @@
|
|||
<svg width="960" height="640" viewBox="0 0 960 640" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="No favorite recipes">
|
||||
<rect width="960" height="640" rx="32" fill="#FFF1F2"/>
|
||||
<path d="M480 418L309 247C267 205 267 137 309 95C351 53 419 53 461 95L480 114L499 95C541 53 609 53 651 95C693 137 693 205 651 247L480 418Z" fill="#FB7185"/>
|
||||
<circle cx="480" cy="260" r="82" fill="#FECDD3"/>
|
||||
<text x="480" y="530" text-anchor="middle" font-family="Inter, sans-serif" font-size="36" fill="#9F1239">No favorites yet</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 551 B |
|
|
@ -0,0 +1,12 @@
|
|||
<svg width="960" height="640" viewBox="0 0 960 640" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="title desc">
|
||||
<title id="title">No recipes found</title>
|
||||
<desc id="desc">Illustration of a cookbook and magnifier to show empty search results.</desc>
|
||||
<rect width="960" height="640" rx="32" fill="#F8FAFC"/>
|
||||
<rect x="210" y="170" width="300" height="320" rx="24" fill="#E2E8F0"/>
|
||||
<rect x="238" y="208" width="244" height="24" rx="12" fill="#94A3B8"/>
|
||||
<rect x="238" y="256" width="188" height="18" rx="9" fill="#CBD5E1"/>
|
||||
<rect x="238" y="288" width="224" height="18" rx="9" fill="#CBD5E1"/>
|
||||
<circle cx="612" cy="320" r="88" stroke="#F97316" stroke-width="20"/>
|
||||
<path d="M672 382L742 452" stroke="#F97316" stroke-width="20" stroke-linecap="round"/>
|
||||
<text x="480" y="560" text-anchor="middle" font-family="Inter, sans-serif" font-size="38" fill="#334155">No recipes match your filters</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 942 B |
|
|
@ -0,0 +1,10 @@
|
|||
<svg width="960" height="640" viewBox="0 0 960 640" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Search returned no results">
|
||||
<rect width="960" height="640" rx="32" fill="#EFF6FF"/>
|
||||
<rect x="170" y="160" width="620" height="90" rx="45" fill="#DBEAFE"/>
|
||||
<circle cx="250" cy="205" r="24" stroke="#3B82F6" stroke-width="10"/>
|
||||
<path d="M266 221L287 242" stroke="#3B82F6" stroke-width="10" stroke-linecap="round"/>
|
||||
<rect x="310" y="186" width="350" height="38" rx="19" fill="#BFDBFE"/>
|
||||
<circle cx="480" cy="380" r="120" stroke="#93C5FD" stroke-width="16" stroke-dasharray="14 12"/>
|
||||
<path d="M432 380L468 416L536 348" stroke="#1D4ED8" stroke-width="16" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<text x="480" y="560" text-anchor="middle" font-family="Inter, sans-serif" font-size="36" fill="#1E3A8A">Try a different keyword</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 880 B |
|
|
@ -0,0 +1,11 @@
|
|||
<svg width="900" height="900" viewBox="0 0 900 900" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Recipe placeholder square">
|
||||
<rect width="900" height="900" rx="40" fill="#F8FAFC"/>
|
||||
<rect x="90" y="90" width="720" height="720" rx="32" fill="#ECFEFF"/>
|
||||
<ellipse cx="450" cy="460" rx="220" ry="126" fill="#E2E8F0"/>
|
||||
<ellipse cx="450" cy="445" rx="200" ry="110" fill="#FFF"/>
|
||||
<circle cx="366" cy="430" r="30" fill="#F97316"/>
|
||||
<circle cx="425" cy="398" r="26" fill="#22C55E"/>
|
||||
<circle cx="485" cy="442" r="24" fill="#FB7185"/>
|
||||
<circle cx="546" cy="410" r="20" fill="#EAB308"/>
|
||||
<text x="450" y="650" text-anchor="middle" font-family="Inter, sans-serif" font-size="40" fill="#334155">Recipe Image</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 747 B |
|
|
@ -0,0 +1,10 @@
|
|||
<svg width="800" height="600" viewBox="0 0 800 600" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Recipe placeholder 4 by 3">
|
||||
<rect width="800" height="600" rx="28" fill="#FFF7ED"/>
|
||||
<rect x="70" y="70" width="660" height="460" rx="24" fill="#FFEDD5"/>
|
||||
<ellipse cx="400" cy="330" rx="170" ry="98" fill="#F8FAFC"/>
|
||||
<circle cx="330" cy="300" r="24" fill="#F97316"/>
|
||||
<circle cx="390" cy="282" r="22" fill="#22C55E"/>
|
||||
<circle cx="446" cy="308" r="20" fill="#EAB308"/>
|
||||
<circle cx="500" cy="287" r="18" fill="#FB7185"/>
|
||||
<text x="400" y="470" text-anchor="middle" font-family="Inter, sans-serif" font-size="28" fill="#475569">No photo yet</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 685 B |
|
|
@ -0,0 +1,21 @@
|
|||
<svg width="1200" height="800" viewBox="0 0 1200 800" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Recipe image placeholder</title>
|
||||
<desc id="desc">Abstract food bowl illustration used as a safe placeholder image.</desc>
|
||||
<defs>
|
||||
<linearGradient id="bg" x1="0" y1="0" x2="1200" y2="800" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFF7ED"/>
|
||||
<stop offset="1" stop-color="#FEF3C7"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="1200" height="800" rx="36" fill="url(#bg)"/>
|
||||
<ellipse cx="600" cy="470" rx="280" ry="150" fill="#E2E8F0"/>
|
||||
<ellipse cx="600" cy="450" rx="250" ry="125" fill="#F8FAFC"/>
|
||||
<circle cx="500" cy="430" r="28" fill="#F97316"/>
|
||||
<circle cx="560" cy="405" r="34" fill="#84CC16"/>
|
||||
<circle cx="630" cy="445" r="30" fill="#22C55E"/>
|
||||
<circle cx="690" cy="410" r="26" fill="#FB7185"/>
|
||||
<circle cx="740" cy="445" r="22" fill="#F59E0B"/>
|
||||
<rect x="410" y="560" width="380" height="20" rx="10" fill="#CBD5E1"/>
|
||||
<text x="600" y="662" text-anchor="middle" font-family="Inter, system-ui, -apple-system, Segoe UI, Roboto, sans-serif" font-size="46" fill="#475569">Recipe Photo Placeholder</text>
|
||||
<text x="600" y="712" text-anchor="middle" font-family="Inter, system-ui, -apple-system, Segoe UI, Roboto, sans-serif" font-size="28" fill="#64748B">Generated in-project, copyright-safe</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
|
|
@ -0,0 +1,8 @@
|
|||
<svg width="960" height="540" viewBox="0 0 960 540" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Upload photo placeholder">
|
||||
<rect width="960" height="540" rx="24" fill="#F8FAFC"/>
|
||||
<rect x="40" y="40" width="880" height="460" rx="20" fill="#EEF2FF" stroke="#94A3B8" stroke-width="4" stroke-dasharray="10 10"/>
|
||||
<path d="M480 198V302" stroke="#475569" stroke-width="16" stroke-linecap="round"/>
|
||||
<path d="M428 250L480 198L532 250" stroke="#475569" stroke-width="16" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<text x="480" y="360" text-anchor="middle" font-family="Inter, sans-serif" font-size="36" fill="#334155">Drop image or click to upload</text>
|
||||
<text x="480" y="408" text-anchor="middle" font-family="Inter, sans-serif" font-size="24" fill="#64748B">JPG, PNG, WebP — placeholder graphic</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 848 B |
|
|
@ -0,0 +1,7 @@
|
|||
<svg width="1600" height="900" viewBox="0 0 1600 900" fill="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Soft decorative background pattern">
|
||||
<rect width="1600" height="900" fill="#F8FAFC"/>
|
||||
<circle cx="180" cy="120" r="180" fill="#DBEAFE"/>
|
||||
<circle cx="1440" cy="180" r="220" fill="#FFEDD5"/>
|
||||
<circle cx="320" cy="780" r="240" fill="#DCFCE7"/>
|
||||
<circle cx="1320" cy="760" r="200" fill="#FCE7F3"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 433 B |