From e1b5b76189dc8c33e5ae1a1105ac04545b110965 Mon Sep 17 00:00:00 2001 From: Muhammad Bappi Date: Mon, 14 Sep 2026 19:49:48 +0600 Subject: [PATCH] feat: pin the backdrop to the viewport and give the blocks depth The field no longer scrolls away with the page: all three layers are background-attachment: fixed, so it stays a backdrop. iOS Safari has historically ignored that and fallen back to scrolling, which is a fine degradation since the pattern tiles either way. The tile is redrawn at 300x900, taller than most viewports, so a layer shows about one pass of the size gradient instead of cycling it two or three times down the screen. Blocks are grouped into five opacity bands to give the field depth, mostly following size since a nearer block is both bigger and more solid, with a few off that curve so it does not read as one flat fade. Sizes are mixed at every height rather than sorted, because clustering one size in one area reads as banding, and heights are capped near 85 tile units, since the largest layer scales by about 1.5 and anything taller became a streak down a third of the screen. --- src/app/global.css | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/app/global.css b/src/app/global.css index e60ab23..c710fb6 100644 --- a/src/app/global.css +++ b/src/app/global.css @@ -42,15 +42,25 @@ to lose and nothing to exclude from pointer events. It also replaces ocean's dark-mode gradient wash, which was the only non-flat surface on the site. + Blocks are grouped into five opacity bands so the field has depth rather than being one flat + stencil. The bands mostly follow size, since a nearer block is both bigger and more solid, but + a few sit off that curve on purpose: a strict size-to-opacity correlation reads as a fade down + the page instead of as blocks at different distances. + + Two things about the rect list look like untidiness and are not. Large, medium and small blocks + are mixed at every height rather than sorted, because clustering one size in one area reads as + banding. And heights are capped around 85 tile units: the largest layer scales by about 1.5, so + anything taller stops being a block and becomes a streak down a third of the screen. + The cost is that moving `background-position` repaints the viewport each frame. At this size and speed that is cheap, and it buys a backdrop with no element, no canvas and no JS. If it ever shows up in a profile, the upgrade is a fixed pseudo-element moved with `transform` instead. */ :root { - --blocks: url("data:image/svg+xml,"); + --blocks: url("data:image/svg+xml,"); } .dark { - --blocks: url("data:image/svg+xml,"); + --blocks: url("data:image/svg+xml,"); } /* `.dark body` is named explicitly because ocean sets both `background-image` and @@ -59,10 +69,14 @@ body, .dark body { background-image: var(--blocks), var(--blocks), var(--blocks); background-repeat: repeat; + /* Anchor the field to the viewport so it stays a backdrop instead of scrolling off with the page. + iOS Safari has historically ignored this and fallen back to scrolling, which is a fine + degradation here: the pattern tiles, so it is still there either way. */ + background-attachment: fixed; background-size: - 320px 533px, - 210px 350px, - 470px 783px; + 300px 900px, + 380px 1140px, + 460px 1380px; background-position: 0 0, 70px 0, @@ -72,9 +86,9 @@ body, @keyframes blockfall { to { background-position: - 0 533px, - 70px 350px, - 160px 783px; + 0 900px, + 70px 1140px, + 160px 1380px; } }