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.
This commit is contained in:
@@ -1,6 +1,40 @@
|
||||
import AxeBuilder from '@axe-core/playwright';
|
||||
import { expect, test } from '../helpers/serve-dist';
|
||||
|
||||
test('builds heading shares the first project surface and contact keeps a clear copy hierarchy', async ({ page, distUrl }) => {
|
||||
await page.emulateMedia({ reducedMotion: 'reduce' });
|
||||
await page.goto(distUrl);
|
||||
const joined = await page.locator('#selected-builds-heading').evaluate(heading => {
|
||||
const surface = heading.closest('.project-stage__lead');
|
||||
return Boolean(surface?.querySelector('.project-case-study') &&
|
||||
getComputedStyle(surface).backgroundColor !== 'rgba(0, 0, 0, 0)');
|
||||
});
|
||||
expect(joined).toBe(true);
|
||||
const hierarchy = await page.locator('#contact').evaluate(section => {
|
||||
const lead = section.querySelector('.contact-destination__copy');
|
||||
const availability = section.querySelector('.contact-destination__availability');
|
||||
return Boolean(lead && availability &&
|
||||
parseFloat(getComputedStyle(lead).fontSize) > parseFloat(getComputedStyle(availability).fontSize));
|
||||
});
|
||||
expect(hierarchy).toBe(true);
|
||||
});
|
||||
|
||||
test('the live reading mask measures the inset builds heading', async ({ page, distUrl }) => {
|
||||
await page.goto(distUrl);
|
||||
await expect(page.locator('html')).toHaveAttribute('data-expressway', 'live');
|
||||
const geometry = await page.locator('#selected-builds').evaluate(section => ({
|
||||
offset: parseFloat((section as HTMLElement).style.getPropertyValue('--chapter-heading-offset')) || 0,
|
||||
actual: section.querySelector('h2')!.getBoundingClientRect().top - section.getBoundingClientRect().top,
|
||||
}));
|
||||
expect(geometry.offset).toBeGreaterThan(20);
|
||||
expect(geometry.offset).toBeCloseTo(geometry.actual, 1);
|
||||
const disclosure = page.locator('.site-navigation summary');
|
||||
if (await disclosure.isVisible()) await disclosure.click();
|
||||
await page.locator('.site-navigation a[href="#selected-builds"]').click();
|
||||
await expect.poll(() => page.locator('#selected-builds-heading').evaluate(heading =>
|
||||
Number(getComputedStyle(heading).opacity))).toBe(1);
|
||||
});
|
||||
|
||||
test('start navigation, contact identity, and toolkit icons use the approved content', async ({ page, distUrl }) => {
|
||||
await page.emulateMedia({ reducedMotion: 'reduce' });
|
||||
const response = await page.goto(distUrl);
|
||||
@@ -11,6 +45,10 @@ test('start navigation, contact identity, and toolkit icons use the approved con
|
||||
await page.locator('.hero-journey__action--primary').click();
|
||||
await expect(page).toHaveURL(/#contact$/);
|
||||
await expect(page.locator('#contact')).toBeInViewport();
|
||||
await expect(page.locator('.journey-world')).toHaveAttribute('data-arrived', 'true');
|
||||
await expect(page.locator('.journey-world')).toHaveAttribute('data-static-fallback', 'true');
|
||||
await expect(page.locator('.journey-canvas')).toHaveCount(0);
|
||||
expect(await page.locator('.journey-poster').evaluate(poster => getComputedStyle(poster).filter)).not.toBe('none');
|
||||
await expect(page.locator('.contact-avatar')).toBeVisible();
|
||||
await expect(page.locator('.contact-social-link')).toHaveText(['LinkedIn', 'GitHub', 'GitLab']);
|
||||
const missing = await page.locator('[data-testid="toolkit-technology"]').evaluateAll(items => items.filter(item => !item.querySelector('svg')).map(item => item.textContent));
|
||||
|
||||
Reference in New Issue
Block a user