images: ['public/logo.svg'] — generator reads the source SVG
logo.svg
Generate the complete FamilySync branding asset set (D-03, D-04): hand-author a warm/rounded/at-home/caricature-family `logo.svg`, install `@vite-pwa/assets-generator` as a devDependency in `apps/pwa`, and derive the full icon/favicon set (`favicon.svg`, `favicon.ico`, `icon-192.png`, `icon-512.png`, a PROPER `icon-maskable-512.png` with real safe-zone padding, `apple-touch-icon.png`) using the `minimal-2023` preset. Then obtain explicit operator approval of the logo art AND the brand-accent selection at a blocking human checkpoint BEFORE any wiring is committed (wiring happens in plan 17-04).
This plan deliberately produces and commits ONLY the assets + generator config. The BrandSlot/index.html/vite.config.ts wiring is a separate plan (17-04) so the checkpoint gates the wiring, exactly as the phase contract requires.
Purpose: replace the placeholder icon stubs (icon-192.png 699 B, icon-512.png, apple-touch-icon.png 617 B) and fix the missing-favicon + improper-maskable defects with a coherent, approved brand identity.
Output: 8 files (logo.svg + 6 derived assets + pwa-assets.config.ts), package.json devDep + script, and a recorded operator approval (logo art + accent choice).
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/17-ui-optimization-polish/17-UI-SPEC.md
@.planning/phases/17-ui-optimization-polish/17-RESEARCH.md
@.planning/phases/17-ui-optimization-polish/17-CONTEXT.md
Task 1: Install @vite-pwa/assets-generator and author logo.svg + pwa-assets.config.ts
apps/pwa/package.json, apps/pwa/public/logo.svg, apps/pwa/pwa-assets.config.ts
- 17-CONTEXT.md §"Specific Ideas" (the brand brief: FamilySync is the brand; caricature-family vibes; warm tones; rounded corners/shapes; "comfortable and at home")
- 17-UI-SPEC.md §"Workstream B — Branding Assets" → "Brand brief" + "Logo asset contract" + "Maskable safe-zone rule"
- 17-RESEARCH.md §"Architecture Patterns" → "Recommended Asset-Generation Script Shape" + the minimal pwa-assets.config.ts excerpt + the generate command
- 17-RESEARCH.md §"Package Legitimacy Audit" (sharp SUS verdict is a documented false positive — 13-yr/65.6M-wk package; no human-verify checkpoint needed for the package itself)
- apps/pwa/public/ (the 3 placeholder stubs being replaced)
Install `@vite-pwa/assets-generator@1.0.2` as a devDependency in `apps/pwa` (pulls `sharp` + `sharp-ico` transitively — these are the only new packages; per the Package Legitimacy Audit they are Approved, no human-verify gate required for the package). Add a `"pwa:icons": "pwa-assets-generator generate"` script to `apps/pwa/package.json`.
Hand-author `apps/pwa/public/logo.svg` as a square-viewBox SVG mark to the brand brief: warm tones, rounded corners/shapes, a friendly caricature-family feel that "makes you feel comfortable and at home" — NOT cold/corporate/geometric. Use a square viewBox so the generator's safe-zone arithmetic works. The mark is text-free art (the app name lives in the `<h1>`, not the logo). Keep the SVG self-contained (no external font/image refs).
Create `apps/pwa/pwa-assets.config.ts` using `defineConfig` + `minimal2023Preset` from `@vite-pwa/assets-generator/config`, with `images: ['public/logo.svg']`. Do NOT set `overrideManifestIcons: true` — the manifest is maintained by hand in vite.config.ts (plan 17-04); auto-override would stomp the explicit entries (RESEARCH anti-pattern).
test -f apps/pwa/public/logo.svg && test -f apps/pwa/pwa-assets.config.ts && node -e "const p=require('./apps/pwa/package.json'); if(!p.devDependencies['@vite-pwa/assets-generator']) process.exit(1); if(!p.scripts['pwa:icons']) process.exit(1)" && head -c 5 apps/pwa/public/logo.svg | grep -q '<'
- `@vite-pwa/assets-generator` present in `apps/pwa/package.json` devDependencies; `pwa:icons` script present.
- `apps/pwa/public/logo.svg` exists, is valid SVG (parses; square viewBox), self-contained, text-free art.
- `apps/pwa/pwa-assets.config.ts` exists, imports `defineConfig`/`minimal2023Preset` from `@vite-pwa/assets-generator/config`, sets `images: ['public/logo.svg']`, and does NOT set `overrideManifestIcons: true`.
The generator devDep + script are installed, logo.svg matches the brand brief as a valid square SVG, and pwa-assets.config.ts is configured for the minimal-2023 preset against the source logo.
Task 2: Generate the full icon/favicon set and validate formats
apps/pwa/public/favicon.svg, apps/pwa/public/favicon.ico, apps/pwa/public/icon-192.png, apps/pwa/public/icon-512.png, apps/pwa/public/icon-maskable-512.png, apps/pwa/public/apple-touch-icon.png
- 17-UI-SPEC.md §"Workstream B" → "Logo asset contract" table (all 7 filenames + dimensions)
- 17-RESEARCH.md §"Architecture Patterns" → the minimal-2023 preset output list + "Maskable safe-zone math" + "ICO generation"
- 17-VALIDATION.md §"Per-Task Verification Map" rows D-03/D-04 (asset existence ls, ICO >100 bytes, maskable 512x512 via sharp metadata)
- apps/pwa/pwa-assets.config.ts (authored in Task 1)
Run the generator from `apps/pwa/` (`pnpm pwa:icons` or `npx @vite-pwa/assets-generator generate`) to produce the derived assets into `apps/pwa/public/`: `favicon.svg`, `favicon.ico`, `icon-192.png`, `icon-512.png`, `icon-maskable-512.png` (separate maskable file with safe-zone padding — this fixes the defect where the manifest reused icon-512.png for the maskable purpose), and `apple-touch-icon.png` (180x180). The minimal-2023 preset handles the safe-zone math, maskable background fill, and ICO encoding via sharp-ico.
Validate the outputs: confirm all 7 files (logo.svg + 6 generated) exist in `apps/pwa/public/`; confirm `favicon.ico` is non-trivial (>100 bytes); confirm `icon-maskable-512.png` is exactly 512x512 via sharp metadata (proving real safe-zone generation, not a stub). The 48px single-size ICO from the preset is acceptable (RESEARCH documents this as adequate vs 16+32 multi-size for this household app; favicon.svg covers modern browsers).
Do NOT wire anything yet (no BrandSlot/index.html/vite.config.ts edits — that is plan 17-04, gated behind the Task 3 checkpoint).
ls apps/pwa/public/logo.svg apps/pwa/public/favicon.svg apps/pwa/public/favicon.ico apps/pwa/public/icon-192.png apps/pwa/public/icon-512.png apps/pwa/public/icon-maskable-512.png apps/pwa/public/apple-touch-icon.png && test $(wc -c < apps/pwa/public/favicon.ico) -gt 100 && node -e "require('sharp')('apps/pwa/public/icon-maskable-512.png').metadata().then(m=>{if(m.width!==512||m.height!==512){console.error('bad maskable dims',m.width,m.height);process.exit(1)}console.log('maskable',m.width+'x'+m.height)})"
- `ls` of all 7 asset paths succeeds (logo.svg + favicon.svg + favicon.ico + icon-192.png + icon-512.png + icon-maskable-512.png + apple-touch-icon.png).
- `test $(wc -c < apps/pwa/public/favicon.ico) -gt 100` passes (ICO is real, not zero-byte).
- `icon-maskable-512.png` reports exactly 512x512 via sharp metadata.
- `icon-maskable-512.png` is a DISTINCT file from `icon-512.png` (different bytes — the maskable has safe-zone padding).
All 7 branding assets exist with correct formats; favicon.ico is non-trivial; the maskable icon is a separate, properly-padded 512x512 file.
Task 3: Checkpoint — operator approves logo art + selects brand accent + logo border-radius
apps/pwa/public/logo.svg, apps/pwa/public/favicon.svg, apps/pwa/public/favicon.ico, apps/pwa/public/icon-192.png, apps/pwa/public/icon-512.png, apps/pwa/public/icon-maskable-512.png, apps/pwa/public/apple-touch-icon.png
Present the generated branding assets and the brand-accent decision to the operator for blocking approval before any wiring is committed (see how-to-verify). Capture the approved logo, the selected accent hex, and the chosen --brand-logo-border-radius for plan 17-04.
A complete FamilySync branding asset set generated from a hand-authored warm/rounded/at-home logo: `logo.svg` (source), `favicon.svg`, `favicon.ico`, `icon-192.png`, `icon-512.png`, `icon-maskable-512.png` (proper safe-zone maskable), and `apple-touch-icon.png` — all in `apps/pwa/public/`. Nothing is wired into the app yet; this checkpoint gates the wiring (plan 17-04).
1. Use the playwright-cli skill to render the logo in context: open the logo.svg directly and/or stage it in the BrandSlot region of `/login` for a preview screenshot, and show the favicon at small sizes.
2. Present the generated logo to the operator against the acceptance lens: does it feel warm / rounded / at-home / caricature-family (not cold/corporate/geometric)? Confirm the maskable icon keeps the mark inside the safe zone (no clipping of meaningful art).
3. Present the BRAND ACCENT decision (UI-SPEC §Color "Brand accent checkpoint", Q1) — two comparable options:
- Variant A — keep cool-blue: `--color-member-0: #4a90d9`, `theme-color #4A90D9` (default if no choice).
- Variant B — warm: `--color-member-0: #f25c7a` (rose, already `--color-shared-family`) OR amber `#e8915a`; contrast ≥3:1 on #ffffff.
Ask the operator to pick A or one of the B candidates. Record the chosen accent hex for plan 17-04 to apply to tokens.css `--color-member-0`, index.html `theme-color`, and vite.config.ts `theme_color`.
4. Also confirm the intended `--brand-logo-border-radius` for the logo shape (0 if the SVG draws its own rounded shape, or 12px if it's a square mark needing rounding) — record for plan 17-04.
Operator types "approved" with: (a) logo accepted (or revise instructions), (b) the selected brand-accent hex, (c) the --brand-logo-border-radius value. These three feed plan 17-04.
<threat_model>
Trust Boundaries
Boundary
Description
Build tooling → repo
@vite-pwa/assets-generator (+ sharp, sharp-ico) runs at design time and writes static assets into public/. New devDependency = supply-chain surface.
Per RESEARCH Package Legitimacy Audit all three are Approved: assets-generator (official vite-pwa project, 231K/wk), sharp (13-yr, 65.6M/wk — the too-new SUS flag is a documented false positive from the latest version's publish date), sharp-ico (431K/wk). No [SLOP]/unverified packages; no human-verify gate required for the package legitimacy. Generated assets are static images served as files — no executable content.
T-17-02-02
Information disclosure
generated assets
accept
Assets are public-by-design brand images; no secrets or PII.
No new high-severity threats. devDependencies only; zero new runtime dependencies; generated output is static images.
</threat_model>
- `ls` all 7 asset paths — present
- `test $(wc -c < apps/pwa/public/favicon.ico) -gt 100` — ICO non-trivial
- sharp metadata on `icon-maskable-512.png` — exactly 512x512
- `@vite-pwa/assets-generator` in apps/pwa devDependencies; `pwa:icons` script present
- Blocking human checkpoint: logo art approved + brand-accent hex selected + --brand-logo-border-radius value recorded
<success_criteria>
The full branding asset set is generated and committed (logo.svg + 6 derived assets), the assets-generator devDep + script are in place, format/dimension checks pass, and the operator has approved the logo and selected the brand accent + logo border-radius — unblocking the wiring plan (17-04).
</success_criteria>
Create `.planning/phases/17-ui-optimization-polish/17-02-SUMMARY.md` when done. Record the approved brand-accent hex and --brand-logo-border-radius value in the SUMMARY (plan 17-04 reads them).