Files
Portfolio/docs/superpowers/plans/2026-10-10-night-shift.md

56 lines
3.6 KiB
Markdown

# Night Shift Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans. Steps use checkbox syntax.
**Goal:** Layer a cinematic, kinetic "Night Shift" identity onto the existing night-drive portfolio without breaking content, budgets or tests.
**Architecture:** Additive. One pure helper module with tests, one pointer composable, two small Vue components, one stylesheet imported last in `App.vue`. All motion honors `prefers-reduced-motion` and the existing motion policy.
**Tech Stack:** Vue 3, TypeScript, Vitest, CSS scroll-driven animations (progressive enhancement).
**Spec:** `docs/redesign/direction.md`
## Global Constraints
- No new runtime dependencies; no new font files (fonts budget 120 KB).
- Initial JS budget 120 KB; HTML+critical CSS 100 KB.
- Never alter content strings in `content/public/portfolio-content.json`.
- Overlay UI is `aria-hidden` or non-blocking; focus rings stay visible; text contrast >= 4.5:1.
- Reduced motion, forced colors, touch and webdriver (tests) show the plain site.
## Review Focus
- Hydration mismatch from time-based text: render stable text on the server, fill on mount.
- Boot overlay trapping clicks/keys or hiding content from no-JS and crawlers.
- Pointer effects on touch devices.
- Uptime when clock is before the start date: clamp to zero.
- Repeat visits: boot shown once per session.
### Task 1: Operations helpers
**Files:** Create `src/lib/operations.ts`; Test `tests/unit/operations.test.ts`
**Produces:** `uptimeSince(start: Date, now: Date): { months: number; days: number }`; `bucharestClock(now: Date): string` (HH:MM, 24h, Europe/Bucharest)
- [ ] Test: `uptimeSince(2025-08-01, 2025-08-01)` is 0 months 0 days; `2026-10-10` gives 14 months 9 days; a `now` before start clamps to zero.
- [ ] Test: `bucharestClock(new Date('2026-01-15T10:05:00Z'))` is `12:05`.
- [ ] Implement; run `npx vitest run tests/unit/operations.test.ts`; commit.
### Task 2: Headlight + pointer composable
**Files:** Create `src/composables/useHeadlight.ts`
**Produces:** `useHeadlight(enabled: Ref<boolean>): void` sets `--hx`/`--hy` on `document.documentElement` and `--mx`/`--my` (percent) on the hovered `.project-case-study, .project-card, .portfolio-section` ancestor, via one rAF-throttled passive `pointermove`; no-op unless `(pointer: fine)` and enabled.
- [ ] Implement, typecheck, commit.
### Task 3: Operations strip
**Files:** Create `src/components/OperationsStrip.vue`; Modify `AppShell.vue`
Renders `Bucharest HH:MM · On shift 14 mo · Route NN%` after mount, static `Bucharest` text on the server. `aria-hidden`. Start date constant `2025-08-01` (Trupa lui Răzuș, from the content file).
- [ ] Implement, mount in AppShell beside JourneyHud, run unit tests, commit.
### Task 4: Boot sequence
**Files:** Create `src/components/BootSequence.vue`; Modify `AppShell.vue`
Shown only after mount when motion allowed, not `navigator.webdriver`, not seen this session (sessionStorage guarded by try/catch). Lines use real facts. Auto-dismiss, any key/click/Escape skips, `pointer-events` only while visible, `aria-hidden`.
- [ ] Implement, commit.
### Task 5: nightshift.css
**Files:** Create `src/styles/nightshift.css`; Modify `src/App.vue` (import last)
Kinetic hero weight, outlined chapter numerals via CSS counter, headlight layer, grain/vignette, card spotlight/tilt, scroll-driven reveals under `@supports (animation-timeline: view())`, all inside `prefers-reduced-motion: no-preference` and `(pointer: fine)` where relevant.
- [ ] Implement, commit.
### Task 6: Verify
- [ ] `npm run typecheck`, `npm run test:unit`, `npm run build:preview`; browser check at 1440 and 390; run browser tests; code review pass.