diff --git a/.planning/phases/06-ux-polish/06-UI-SPEC.md b/.planning/phases/06-ux-polish/06-UI-SPEC.md index 080a619..7ee61d9 100644 --- a/.planning/phases/06-ux-polish/06-UI-SPEC.md +++ b/.planning/phases/06-ux-polish/06-UI-SPEC.md @@ -317,6 +317,138 @@ All new surfaces must meet: --- +## Brand Assets & Iconography + +Source: D-BRAND-01, D-BRAND-02 (locked user decisions, 2026-06-10). +In-app UI icons remain lucide-react — this section covers only the brand mark, app icons, and favicons. + +### Mark: Glyph + Wordmark + +**Concept (D-BRAND-01):** Two overlapping rounded shapes — circles or rounded rectangles — suggesting two members sharing a space (the family) or two overlapping calendar tiles. The overlap region is the accent color at full opacity; each outer shape is the accent color at reduced opacity (≈60%). The wordmark "FamilySync" sits to the right of the glyph in the lockup variant; the glyph alone is the source for all raster icons. + +**Geometry / construction:** +- Two circles, each 40px diameter on a 64px × 64px artboard (SVG viewBox="0 0 64 64"). +- Circle 1 center: (24, 32). Circle 2 center: (40, 32). +- Overlap: the intersection region formed by both paths. +- Rendering: use two `` elements with `fill-opacity="0.6"` for the individual shapes, then a ``-intersected shape (or a third overlapping path) at `fill-opacity="1"` for the overlap highlight. +- Wordmark: "FamilySync" set in the system-ui stack at 18px/600 in the lockup; the glyph and wordmark baseline-align. + +**Color usage:** + +| Surface | Glyph fill | Wordmark fill | +|---------|-----------|---------------| +| Light background (#FFFFFF) | #4A90D9 (accent) | #111318 (--color-text-primary) | +| Dark background (≥50% dark) | #FFFFFF | #FFFFFF | +| Monochrome (print / favicon .ico) | currentColor (#111318 on light, #FFFFFF on dark) — single flat shape, no opacity split | + +**Clear space:** Minimum clear space = 1× the glyph diameter (64px on the 64px artboard, i.e. one full glyph-width on all four sides at actual render size). + +**Minimum size:** +- Glyph-only: 24px × 24px rendered (below this, detail is lost; use the monochrome flat variant). +- Glyph + wordmark lockup: 120px wide minimum. + +**Monochrome fallback:** A single filled shape representing both circles merged (union path), no opacity split. Used for favicon.ico and any single-color context. + +### SVG Source-of-Truth Files + +| File | Contents | Used as source for | +|------|----------|--------------------| +| `apps/pwa/src/assets/logo-glyph.svg` | Glyph only, viewBox="0 0 64 64", color-variable fills (`currentColor` + CSS custom property override) | All raster icon exports; in-app glyph-only placements | +| `apps/pwa/src/assets/logo-lockup.svg` | Glyph + "FamilySync" wordmark, viewBox="0 0 240 64" | Auth splash, header lockup (if present) | +| `apps/pwa/src/assets/logo-monochrome.svg` | Flat union-path glyph, single fill, viewBox="0 0 64 64" | favicon.ico source layer | + +All SVG files: no embedded raster data, no `` elements, path-only. Minified with no comments before commit. + +### Raster Export Pipeline + +Source: `apps/pwa/src/assets/logo-glyph.svg` (light-mode fill: #4A90D9 shapes on transparent background). + +Export tool: any SVG-to-PNG renderer that preserves alpha (e.g. `sharp`, `Inkscape --export-png`, or `resvg`). A build-time script at `apps/pwa/scripts/export-icons.ts` (or equivalent Makefile target) must produce the full output list below from the SVG source — no manually-placed PNGs. + +**Output list:** + +| Output file | Size (px) | Format | Notes | +|-------------|-----------|--------|-------| +| `apps/pwa/public/icon-192.png` | 192×192 | PNG, RGBA | PWA manifest `any` icon | +| `apps/pwa/public/icon-512.png` | 512×512 | PNG, RGBA | PWA manifest `any` icon | +| `apps/pwa/public/icon-512-maskable.png` | 512×512 | PNG, RGBA | PWA manifest `maskable` icon — glyph centered in safe zone (see below) | +| `apps/pwa/public/apple-touch-icon.png` | 180×180 | PNG, RGB (no alpha), white background | iOS home screen; Apple ignores alpha | +| `apps/pwa/public/favicon-16.png` | 16×16 | PNG | favicon.ico source layer | +| `apps/pwa/public/favicon-32.png` | 32×32 | PNG | favicon.ico source layer | +| `apps/pwa/public/favicon-48.png` | 48×48 | PNG | favicon.ico source layer | +| `apps/pwa/public/favicon.svg` | — | SVG (copy of logo-glyph.svg) | Modern browsers; referenced as `` | +| `apps/pwa/public/favicon.ico` | 16+32+48 multi-res | ICO | Legacy browsers; bundle the three PNG layers into a single .ico using `png-to-ico` or equivalent | + +Replace the three existing placeholder files (`icon-192.png`, `icon-512.png`, `apple-touch-icon.png`) with the real exports. The script must be idempotent (re-running overwrites all outputs). + +### Maskable Safe Zone + +The maskable icon specification requires the primary visual to fit within the center 80% of the canvas (the "safe zone"). For a 512×512 canvas, the safe zone is the inner 409×409 px centered region. The glyph export for `icon-512-maskable.png` must: +- Scale the glyph to fit within 409×409 (≈80% of 512). +- Center it on the 512×512 canvas. +- Fill the outer 10% bleed area with the PWA `background_color` (#FFFFFF) so masked shapes (squircle, circle) show a clean white surround rather than transparency artifacts. + +Update `vite.config.ts` manifest `icons` array to reference `icon-512-maskable.png` for the `maskable` purpose entry (separate file from the `any` 512px icon): + +```ts +{ src: '/icon-192.png', sizes: '192x192', type: 'image/png' }, +{ src: '/icon-512.png', sizes: '512x512', type: 'image/png' }, +{ src: '/icon-512-maskable.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' }, +``` + +### Favicon Set — index.html Changes + +Current state: `index.html` has only `` and no ``. This is a gap. + +Add the following tags inside ``, after the existing `apple-touch-icon` line: + +```html + + +``` + +Keep the existing tags unchanged: + +```html + + + + + +``` + +Browser resolution order: SVG favicon first (Chrome 80+, Firefox 41+, Safari 12+), ICO fallback for IE/legacy. No `favicon-32.png` link needed in HTML — the ICO multi-res bundle covers the same use case and reduces link clutter. + +### In-App Logo Usage + +| Surface | Variant | Size | Placement | +|---------|---------|------|-----------| +| Auth splash (Surface 1) | Glyph + wordmark lockup (`logo-lockup.svg`) | 120px wide (auto height ~32px) | Centered above the spinner stack; 24px gap below lockup, then spinner | +| Session-expired interstitial (Surface 2) | Glyph only (`logo-glyph.svg`) | 32×32px | Centered above spinner; same vertical stack as auth splash | +| App header / nav bar (if present) | Glyph only (`logo-glyph.svg`) | 24×24px | Leading slot of the top nav bar, 16px from left edge, vertically centered | +| PWA install prompt / about screen | Glyph + wordmark lockup | 160px wide | Centered | + +Spacing tokens used: 24px gap (`--space-6`) between lockup and spinner on auth splash; 16px left inset (`--space-4`) for header placement. These are drawn from the established 8-point scale. + +Color: render SVGs using CSS `color` inheritance where possible so light/dark mode automatically applies the correct fill. Set `fill="currentColor"` on all path elements in the SVG source; parent container sets `color: var(--color-text-primary)` (light) or `color: #FFFFFF` (dark overlay contexts such as auth splash over `--color-surface` — use `--color-text-primary` here since the background is white). + +### Asset Manifest + +| File path | Format | Size(s) | Purpose | Referenced in | +|-----------|--------|---------|---------|---------------| +| `apps/pwa/src/assets/logo-glyph.svg` | SVG | 64×64 viewBox | SVG source of truth — glyph only | Export script, in-app `` or inline SVG | +| `apps/pwa/src/assets/logo-lockup.svg` | SVG | 240×64 viewBox | SVG source of truth — glyph + wordmark | Auth splash, install prompt | +| `apps/pwa/src/assets/logo-monochrome.svg` | SVG | 64×64 viewBox | Monochrome union-path variant | favicon.ico source | +| `apps/pwa/public/favicon.svg` | SVG | — | Modern browser favicon | `` in index.html | +| `apps/pwa/public/favicon.ico` | ICO | 16+32+48 multi-res | Legacy browser favicon | `` in index.html | +| `apps/pwa/public/icon-192.png` | PNG (RGBA) | 192×192 | PWA manifest any icon | vite.config.ts manifest `icons` | +| `apps/pwa/public/icon-512.png` | PNG (RGBA) | 512×512 | PWA manifest any icon | vite.config.ts manifest `icons` | +| `apps/pwa/public/icon-512-maskable.png` | PNG (RGBA) | 512×512 | PWA manifest maskable icon | vite.config.ts manifest `icons` | +| `apps/pwa/public/apple-touch-icon.png` | PNG (RGB, white bg) | 180×180 | iOS home screen icon | `` in index.html | +| `apps/pwa/scripts/export-icons.ts` | TypeScript | — | Build-time raster export pipeline | `make icons` or equivalent | + +--- + ## Registry Safety | Registry | Blocks Used | Safety Gate |