From f46131723b6bbeb666b8588fd9de033b312d2a39 Mon Sep 17 00:00:00 2001 From: LucaP Date: Sat, 5 Sep 2026 22:16:42 +0300 Subject: [PATCH] feat: finish portfolio title, navigation, and destination effects Add the approved name animation, rounded docking navigation, localized keyboard focus cue, unified builds card, and destination copy hierarchy. Introduce lightweight one-shot meteors and a sun ring that repeats every three seconds at Contact. Open the email action in a separate browsing context. Preserve reduced-motion behavior and keep private handoff and visual-check artifacts out of version control. Validation: 649 tests passed; all 20 failures in world-models and SceneCanvas reproduce on baseline 103427f. Typecheck, artifact verification, and transfer budgets passed. Client/SSR preview build passed before this commit. Existing baseline test failures remain unchanged. --- .gitignore | 5 + playwright.content.config.ts | 2 +- scripts/verify-artifacts.mjs | 2 +- src/App.vue | 1 + src/components/AppShell.vue | 16 ++- src/components/ContactDestination.vue | 5 +- src/components/HeroJourney.vue | 8 +- src/components/NameParticles.vue | 132 +++++++++++++++++++ src/components/PortfolioSection.vue | 11 +- src/components/SceneCanvas.vue | 1 + src/components/SiteNavigation.vue | 23 +++- src/scene/world/arrival-meteors.ts | 91 +++++++++++++ src/scene/world/environment.ts | 1 + src/scene/world/sign-stage.ts | 2 +- src/scene/world/sun.ts | 65 +++++++-- src/styles/base.css | 5 +- src/styles/chromatic.css | 10 +- src/styles/evolution.css | 16 +-- src/styles/refinements.css | 33 +++++ src/styles/title-navigation.css | 145 +++++++++++++++++++++ src/styles/world.css | 7 +- tests/browser/arrival-finale.spec.ts | 76 +++++++++++ tests/browser/contact-refinements.spec.ts | 38 ++++++ tests/browser/keyboard.spec.ts | 4 +- tests/browser/no-javascript.spec.ts | 6 +- tests/browser/title-navigation.spec.ts | 143 ++++++++++++++++++++ tests/helpers/serve-dist.ts | 13 ++ tests/integration/verify-artifacts.test.ts | 6 +- tests/unit/AppShell.test.ts | 4 +- tests/unit/contact-interaction.test.ts | 2 + tests/unit/sun-arrival.test.ts | 126 ++++++++++++++++++ 31 files changed, 948 insertions(+), 51 deletions(-) create mode 100644 src/components/NameParticles.vue create mode 100644 src/scene/world/arrival-meteors.ts create mode 100644 src/styles/title-navigation.css create mode 100644 tests/browser/arrival-finale.spec.ts create mode 100644 tests/browser/title-navigation.spec.ts create mode 100644 tests/unit/sun-arrival.test.ts diff --git a/.gitignore b/.gitignore index e0c97ec..eb0b7e1 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,8 @@ inputs/original-assets/ *.map .worktrees/ .superpowers/sdd/ + +# Private source material and local visual-check artifacts are never published. +/private-handoff/ +/.playwright-mcp/ +/baseline-infrastructure.png diff --git a/playwright.content.config.ts b/playwright.content.config.ts index 1a49a85..6c4a328 100644 --- a/playwright.content.config.ts +++ b/playwright.content.config.ts @@ -4,7 +4,7 @@ import { defineConfig, devices } from '@playwright/test'; // These do not need the full suite's fault-injection client. export default defineConfig({ testDir: './tests/browser', - testMatch: 'contact-refinements.spec.ts', + testMatch: ['contact-refinements.spec.ts', 'title-navigation.spec.ts', 'arrival-finale.spec.ts'], workers: 2, use: { serviceWorkers: 'block' }, projects: [ diff --git a/scripts/verify-artifacts.mjs b/scripts/verify-artifacts.mjs index d30dae9..37cd08f 100644 --- a/scripts/verify-artifacts.mjs +++ b/scripts/verify-artifacts.mjs @@ -218,7 +218,7 @@ const assertSemanticHtml = (html, inputs, contract) => { } continue; } - const headings = [...section.querySelectorAll(':scope > h2')] + const headings = [...section.querySelectorAll('h2')] .filter((node) => isVisibleSemanticNode(node, appRoot)); if (label === undefined || headings.length !== 1 || headings[0].id !== `${section.id}-heading` || headings[0].textContent !== label) { diff --git a/src/App.vue b/src/App.vue index 23f431e..6df9779 100644 --- a/src/App.vue +++ b/src/App.vue @@ -9,6 +9,7 @@ import './styles/world.css'; import './styles/evolution.css'; import './styles/chromatic.css'; import './styles/refinements.css'; +import './styles/title-navigation.css'; import './styles/print.css'; defineProps<{ inputs: Readonly }>(); diff --git a/src/components/AppShell.vue b/src/components/AppShell.vue index 5d28b8b..bbe978f 100644 --- a/src/components/AppShell.vue +++ b/src/components/AppShell.vue @@ -219,6 +219,7 @@ onUnmounted(() => removePageListeners()); @@ -226,7 +227,7 @@ onUnmounted(() => removePageListeners());
- +

{{ content.profile.introduction }}

@@ -247,12 +248,20 @@ onUnmounted(() => removePageListeners());
+
@@ -281,6 +290,7 @@ onUnmounted(() => removePageListeners());
+ => {