diff --git a/apps/docs-tests/docs/favorites-hover.spec.ts b/apps/docs-tests/docs/favorites-hover.spec.ts index e81c0a40..74917f62 100644 --- a/apps/docs-tests/docs/favorites-hover.spec.ts +++ b/apps/docs-tests/docs/favorites-hover.spec.ts @@ -4,7 +4,8 @@ import type { Locator, Page } from '@playwright/test'; /** * The homepage favorites come alive on hover (SHA-183): the scene mounts under * its poster on hover or focus, pauses when the visitor leaves, stays paused - * when another favorite plays, and never mounts on touch. The no-WebGPU case + * when another favorite plays, and never mounts on touch. At the mock's + * width, the scene renders at its window's size (SHA-220). The no-WebGPU case * is in homepage.spec.ts, which runs without the adapter this file turns on. */ @@ -133,6 +134,38 @@ test('focusing a favorite mounts its scene behind the poster until the first fra await expect(poster(favorite)).toHaveCSS('opacity', '0'); }); +// --------------------------------------------- +// The scene's size +// --------------------------------------------- + +// At the mock's 1728px the grid is at its full 1440px, and a card's window +// is 320 by 233. The scene renders at that size, so useCoverScale +// (favorites-list.tsx) leaves it at a scale of 1. A larger scene, scaled +// down to fit, draws pixels the window never shows, and shrinks the cells +// that Dither and LED Wall size in CSS pixels. +test.describe('at the mock’s 1728px width', () => { + test.use({ viewport: { width: 1728, height: 1117 } }); + + test('a favorite’s scene renders at its window’s size, with no scale', async ({ page }) => { + await open(page); + + const favorite = favorites(page).first(); + + await favorite.hover(); + // useCoverScale writes the scale once it learns the window's size. + await expect(scene(favorite)).toHaveCSS('scale', '1'); + expect( + await scene(favorite).evaluate((sceneLayer: HTMLElement) => ({ + scene: [sceneLayer.offsetWidth, sceneLayer.offsetHeight], + sceneWindow: [ + sceneLayer.parentElement!.clientWidth, + sceneLayer.parentElement!.clientHeight, + ], + })), + ).toEqual({ scene: [320, 233], sceneWindow: [320, 233] }); + }); +}); + // --------------------------------------------- // Pausing when the visitor leaves // --------------------------------------------- diff --git a/apps/docs-tests/docs/homepage.spec.ts b/apps/docs-tests/docs/homepage.spec.ts index 20ca4d97..4b51777f 100644 --- a/apps/docs-tests/docs/homepage.spec.ts +++ b/apps/docs-tests/docs/homepage.spec.ts @@ -144,7 +144,7 @@ test('a favorite is named for its component', async ({ page }) => { // Each favorite shows its card poster, not the small square the components // index uses, and every one has loaded. A card poster is captured at the live -// scene's shape, 376 by 275, so the spec checks the file's shape too. +// scene's shape, 320 by 233, so the spec checks the file's shape too. test('each favorite shows its poster', async ({ page }) => { await open(page); await expect(favorites(page)).toHaveCount(FAVORITES.length); @@ -174,7 +174,7 @@ test('each favorite shows its poster', async ({ page }) => { expect(image.src, image.href).toMatch( new RegExp(`/posters/${FAVORITES[index]}-card\\.(jpg|png)`), ); - expect(image.shape, image.href).toBeCloseTo(376 / 275, 2); + expect(image.shape, image.href).toBeCloseTo(320 / 233, 2); } }); diff --git a/apps/docs/public/posters/dither-card.jpg b/apps/docs/public/posters/dither-card.jpg index 4feea714..a635a846 100644 Binary files a/apps/docs/public/posters/dither-card.jpg and b/apps/docs/public/posters/dither-card.jpg differ diff --git a/apps/docs/public/posters/god-rays-card.jpg b/apps/docs/public/posters/god-rays-card.jpg index a2c74654..fa518a06 100644 Binary files a/apps/docs/public/posters/god-rays-card.jpg and b/apps/docs/public/posters/god-rays-card.jpg differ diff --git a/apps/docs/public/posters/led-wall-card.jpg b/apps/docs/public/posters/led-wall-card.jpg index 289ee43f..7a2c5a88 100644 Binary files a/apps/docs/public/posters/led-wall-card.jpg and b/apps/docs/public/posters/led-wall-card.jpg differ diff --git a/apps/docs/public/posters/mesh-gradient-card.jpg b/apps/docs/public/posters/mesh-gradient-card.jpg index 3a01a25c..d02b3f01 100644 Binary files a/apps/docs/public/posters/mesh-gradient-card.jpg and b/apps/docs/public/posters/mesh-gradient-card.jpg differ diff --git a/apps/docs/public/posters/simplex-noise-card.png b/apps/docs/public/posters/simplex-noise-card.png index 5f432ed0..afcba4fc 100644 Binary files a/apps/docs/public/posters/simplex-noise-card.png and b/apps/docs/public/posters/simplex-noise-card.png differ diff --git a/apps/docs/public/posters/voronoi-card.jpg b/apps/docs/public/posters/voronoi-card.jpg index 42b06ab5..50250540 100644 Binary files a/apps/docs/public/posters/voronoi-card.jpg and b/apps/docs/public/posters/voronoi-card.jpg differ diff --git a/apps/docs/public/posters/wave-lines-card.jpg b/apps/docs/public/posters/wave-lines-card.jpg index b41cc05b..27f6b4a7 100644 Binary files a/apps/docs/public/posters/wave-lines-card.jpg and b/apps/docs/public/posters/wave-lines-card.jpg differ diff --git a/apps/docs/src/components/favorites/favorites-list.tsx b/apps/docs/src/components/favorites/favorites-list.tsx index 60decbbf..0c25f8a1 100644 --- a/apps/docs/src/components/favorites/favorites-list.tsx +++ b/apps/docs/src/components/favorites/favorites-list.tsx @@ -222,14 +222,16 @@ function LiveScene({ slug, paused }: { slug: FavoriteSlug; paused: boolean }) { // The size every favorite renders at, in CSS pixels, and the size // scripts/build-posters.sh captures every favorite's card poster at. It is -// the card window's size on a 1664px grid, 17.5% larger than the 320 by 233 -// window at the grid's full 1440px, so useCoverScale scales the scene down -// to cover the window, as it does at every narrower width. Larger draws -// more pixels than the window shows, and shrinks patterns sized in CSS -// pixels, such as Dither's and LED Wall's cells. Smaller than the window -// makes useCoverScale scale the scene up, which blurs it. SHA-220 sets it -// to the window's size. -const SCENE_SIZE = { width: 376, height: 275 }; +// the card window's size at the grid's full 1440px, where useCoverScale +// leaves the scene at a scale of 1, so Dither's and LED Wall's cells, which +// are sized in CSS pixels, show at their real size. Four narrower columns +// scale it down. Larger would draw more pixels than the window shows, and +// smaller would scale the scene up, which softens it. The two-column and +// one-column grids already scale it up at their wider widths, as far as +// about 1.8 times for the 579 by 427 window at 639px. A change to the +// grid's width or the card's padding moves the window, so change this with +// it and recapture the card posters. +const SCENE_SIZE = { width: 320, height: 233 }; /** * Scales the scene to cover its window, the way `object-fit: cover` scales diff --git a/scripts/build-posters.sh b/scripts/build-posters.sh index f7e02fef..c89e386f 100755 --- a/scripts/build-posters.sh +++ b/scripts/build-posters.sh @@ -122,7 +122,8 @@ $CLI \ --height 696 \ --background "#0b0f1a" -# The homepage favorites render every live scene at 376 by 275 (SCENE_SIZE in +# The homepage favorites render every live scene at 320 by 233, the card +# window's size at the grid's full 1440px (SCENE_SIZE in # apps/docs/src/components/favorites/favorites-list.tsx), and scale it with # the card. Each favorite's card poster is captured at that size, over the # ground its scene sits on there, so the poster frames and crops the scene @@ -140,13 +141,13 @@ for pair in \ "god-rays:jpg:linear-gradient(to top, #131b31, #0b0f1a)" \ "led-wall:jpg:#0b0f0d"; do IFS=':' read -r name format background <<< "$pair" - echo "==> ${name}-card ($format, 376x275)" + echo "==> ${name}-card ($format, 320x233)" card_args=( --source "${COMPONENTS_DIR}/${name}/scene.tsx" --output "${OUT_DIR}/${name}-card.${format}" --format "$format" - --width 376 - --height 275 + --width 320 + --height 233 ) if [ -n "$background" ]; then card_args+=(--background "$background")