Design pass: fix dead site config, add hero image support, polish cards
Deploy to Dev / Deploy & Smoke Test (push) Successful in 21s

- Fix applyConfig self-recursion (const _origApply captured the hoisted
  wrapper itself): site.json config — theme, portfolio, socials, bio —
  was never applied; site silently fell back to hardcoded brown defaults
- Strengthen theme test to assert the configured primary color exactly
- Portfolio covers: styled placeholder on missing image (like locations),
  restore img when Immich carousel kicks in; overlay always visible on touch
- Optional hero photo via site.heroImage with soft scrim (activates only
  when the image loads); site.json points at /images/hero.jpg
- Option cards: equal height, centered content
- /#book deep link also works via hashchange

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-16 00:39:36 -06:00
parent a67fdba170
commit 9dcc73ecfe
3 changed files with 84 additions and 23 deletions
+10 -12
View File
@@ -45,19 +45,17 @@ test.describe('Portfolio site', () => {
expect(json.ok).toBe(true);
});
test('pink theme is applied (primary color is rose/pink)', async ({ page }) => {
test('configured theme is applied (primary color matches site.json)', async ({ page, request }) => {
const config = await (await request.get('/config/site.json')).json();
const expected = (config.theme?.primaryColor || '').toLowerCase();
expect(expected).toMatch(/^#[a-f0-9]{6}$/);
await page.goto('/');
const primary = await page.evaluate(() =>
getComputedStyle(document.documentElement).getPropertyValue('--color-primary').trim()
// Config is fetched on window load; wait until the var flips from the CSS default
await page.waitForFunction(
exp => getComputedStyle(document.documentElement).getPropertyValue('--color-primary').trim().toLowerCase() === exp,
expected,
{ timeout: 8_000 }
);
// Should be a pink/rose hex — starts with #B or #b (our dusty rose #B06A7A)
expect(primary.toLowerCase()).toMatch(/^#[a-f0-9]{6}$/i);
// Hue should be in the red-pink range: R > G and R > B
const hex = primary.replace('#', '');
const r = parseInt(hex.slice(0, 2), 16);
const g = parseInt(hex.slice(2, 4), 16);
const b = parseInt(hex.slice(4, 6), 16);
expect(r).toBeGreaterThan(g);
expect(r).toBeGreaterThan(b);
});
});