biohazard-vfx/docs/stories/1.3.sticky-split-pattern-library.md
nicholai ed55376b9c
Some checks are pending
Build and Push to Docker Hub / Push Docker image to Docker Hub (push) Waiting to run
Build and Push Docker Image / build-and-push (push) Waiting to run
bmad-planning-complete
2025-09-24 10:54:01 -06:00

108 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Status
Draft
Story
**As a** storyteller,
**I want** a sticky split component,
**so that** narrative and media synchronize during scroll.
Acceptance Criteria
1. <StickySplit.Section/Sticky/Track/Panel> components implemented with docs.
2. Reducedmotion mode disables heavy animations gracefully.
3. Mobile layout stacks content; performance budget respected.
Tasks / Subtasks
- [ ] Scaffold pattern components (AC: 1)
- [ ] Create folder `src/components/patterns/sticky-split/`
- [ ] Add files: `Section.tsx`, `Sticky.tsx`, `Track.tsx`, `Panel.tsx`, `index.ts`
- [ ] Export namespaced API: `StickySplit = { Section, Sticky, Track, Panel }`
- [ ] Define props:
- Section: `{ id: string; height?: string | number; side?: 'left'|'right'; className?: string }`
- Sticky: `{ offset?: string; className?: string }`
- Track: `{ className?: string }`
- Panel: `{ index: number; inVariants?: Variants; outVariants?: Variants; className?: string }`
- [ ] Provide sensible defaults: `height='200vh'`, `side='left'`, sticky `offset='var(--sticky-top, 8vh)'`
- [ ] Progress computation (AC: 1)
- [ ] Implement `useSectionProgress(id)` in `src/components/patterns/sticky-split/useSectionProgress.ts`
- [ ] Use `IntersectionObserver` on Section root to compute 0..1 progress across its scroll range
- [ ] Ensure passive listeners only; no wheel/touch blocking; avoid scrolljacking
- [ ] Animation integration (AC: 1)
- [ ] Use `framer-motion` `motion.div` inside `Panel` and map `progress` to opacity/transform via `useTransform`
- [ ] Add optional `inVariants/outVariants` props; default to subtle fade/scale
- [ ] Reduced motion (AC: 2)
- [ ] Use `useReducedMotion()` from framermotion; if true, disable transforms and show panels as static stack
- [ ] Mobile layout + fallbacks (AC: 3)
- [ ] Apply responsive CSS: below `md`, stack vertically; disable sticky; panels visible with light revealonview (IO add/remove `is-visible` class)
- [ ] Above `md`, twocolumn grid with left sticky narrative and right scroll track; allow `side='right'` to invert
- [ ] Tokens and base styles (AC: 1,3)
- [ ] Define CSS vars in `src/app/globals.css`: `--sticky-top`, `--panel-gap`, `--reveal-duration`, `--reveal-ease`
- [ ] Provide utility classes for the pattern container grid and gaps using Tailwind + CSS vars
- [ ] Documentation & example (AC: 1)
- [ ] Add `src/components/patterns/sticky-split/README.md` with usage, props, and examples
- [ ] Include a small demo component `Demo.tsx` showcasing 3 panels; do not add a new route in this story
- [ ] Quality gates & perf
- [ ] Lint passes: `npm run lint`
- [ ] Build passes: `npm run build`
- [ ] Verify no main thread jank on midrange device; keep frame budget ≈16ms for panel transitions
- [ ] Confirm passive listeners; no prevention of native scroll
Dev Notes
- Context
- Derived from PRD Story 1.3. Target: reusable pattern inspired by “Basement Foundry” style; no scrolljacking.
- Typography and tokens are handled in Story 1.1; this story may add a few CSS vars specific to the pattern.
- Relevant Source Tree
- Component home: `src/components/patterns/sticky-split/*`
- Tailwind/CSS tokens: `src/app/globals.css`, `tailwind.config.ts`
- Motion libs: `framer-motion`/`motion` available in dependencies.
- Implementation Guidance
- Section renders a responsive twocolumn grid (`md:grid md:grid-cols-2`) with a fixed sticky column and a scrolling track column; swap order with `side`.
- Sticky uses `position: sticky; top: var(--sticky-top)` and inherits tokenized spacing/gap via CSS vars.
- Track holds `Panel` children; each Panel uses the section progress (or perpanel thresholds like `index / (N-1)`) to compute in/out.
- Reduced motion: early return static markup; prefer opacity reveals without transforms.
- Accessibility: preserve DOM order for reading; keep focusable content reachable; avoid trapping focus.
- Performance: avoid large fixed layers; prefer `will-change: transform` only while animating; lazyload media inside panels.
Testing
- Manual
- With default motion: panels fade/transform smoothly as the section scrolls; no scroll blocking.
- With `prefers-reduced-motion`: panels render as static stack; no transforms/animations.
- Mobile: stacked layout; sticky disabled; content readable and navigable.
- Perf
- Inspect Chrome Performance or DevTools frame rate timeline; verify transitions stay near 60fps on typical hardware.
- A11y
- Keyboard navigation reaches all interactive elements inside Sticky and Track; focus order logical.
Change Log
| Date | Version | Description | Author |
|------|---------|-------------|--------|
| 2025-09-24 | v1 | Initial draft from PRD Story 1.3 | Scrum Master |
Dev Agent Record
Agent Model Used
{{agent_model_name_version}}
Debug Log References
Completion Notes List
File List
QA Results