docs(plan): spec ParallaxImage shared component (Phase 7)

Add product note and PLAN.md Phase 7 entry for a reusable scroll-parallax
image window in DeepDrftShared.Client — full-width flag, hover crossfade,
IntersectionObserver-gated scroll math, accessibility.
This commit is contained in:
daniel-c-harvey
2026-06-11 08:36:00 -04:00
parent c835a54652
commit bd6bd4d827
2 changed files with 430 additions and 0 deletions
+14
View File
@@ -135,6 +135,20 @@ See `COMPLETED.md` for Phase 6 (§6.1, §6.3) and entity-prep (§6.2 model layer
---
## Phase 7 — Shared UI Components
Reusable presentational components in `DeepDrftShared.Client` (the RCL consumed by both the public site and the CMS). Distinct from the player stack and CMS surfaces — these are host-agnostic building blocks both apps compose.
### 7.1 ParallaxImage component
- **What:** A thin viewport-height container that reveals different portions of an image as the user scrolls — the classic CSS parallax window. As the window scrolls up through the viewport, the image pans through it faster than the page scrolls (top of image on entry, bottom of image by the time the window reaches the top of the viewport). An optional second image crossfades in on hover (intended use: grayscale at rest, colour on hover). A critical `FullWidth` flag stretches the window to `100vw`, breaking out of parent padding. Full signature and design in `product-notes/parallax-image-component.md`.
- **Why it matters:** A reusable scroll flourish for hero/section surfaces on both the public site and the CMS, landing the visual identity work without bespoke per-page CSS. It is the first genuinely shared presentational component in `DeepDrftShared.Client` — establishes the pattern (and the RCL static-asset JS-module seam) for shared UI that both hosts consume.
- **Shape:** `ParallaxImage.razor` (+ `.razor.cs`, `.razor.css`) in `DeepDrftShared.Client/Components/`. Scroll-driven `background-position` (never `background-attachment: fixed` — broken on iOS Safari), gated by an `IntersectionObserver` so off-screen instances cost nothing. Scroll math lives in a small JS module; lifecycle owned by Blazor via `ElementReference` + an imported `IJSObjectReference`, mirroring the existing audio interop seam. Crossfade is pure CSS. `IAsyncDisposable` tears down the listener. Full parameter table, parallax math, interop contract, full-width breakout technique, accessibility (reduced-motion, alt text), and edge cases (mobile Safari, preload timing) are specified in the product note.
- **Prerequisite:** None functionally. Additive — no existing surface changes to adopt it.
- **Constraint:** **One blocking decision before implementation** — where the JS module ships from. The component lives in the shared RCL but the brief places the TS at `DeepDrftPublic/Interop/parallax/`, whose compiled JS ships *only* from the public host, leaving the CMS with a no-op component. The product note (§6a/§11.1) recommends co-locating the JS as an RCL static web asset (`DeepDrftShared.Client/wwwroot/js/parallax/parallax.js`, plain ES module, served from `_content/DeepDrftShared.Client/…` to both hosts) instead. A second smaller decision: the spec corrects the sign of the brief's parallax formula to match the stated visual intent (§3/§11.2). Both want a Daniel nod before code lands.
---
## Cross-cutting / not yet themed
A small set of items that are real but don't fit a phase yet. Surface them when they become relevant rather than committing now.