From b9d0cd34b987e83a480d312db30db81d3c0205bc Mon Sep 17 00:00:00 2001 From: Martin Dvorak Date: Thu, 15 Oct 2026 09:37:26 +0000 Subject: [PATCH] Aero glass goes back to glass The uniform wash (background-glass) made the rail and the chat chrome nearly opaque - "why is the sidebar so white and non-translucent... its supposed to be translucent and blurry!". The rail, the chat header, the welcome card and the composer now use the same recipe as every other surface in the app (background-blur under the shared gloss + veil + --glass-bg, exactly what the deck cards do), and the dedicated wash asset is deleted. Answer bubbles keep a thin 0.3 veil over the same blurred wallpaper. Measured: the rail runs 181,229,241 over the sky to 133,174,75 over the grass (translucent, d166) and the chat/library screens still scan at 0 below-minimum text-contrast findings in all three themes. The chat suite now pins both the shared recipe and the translucency, so a future "make it uniform" pass cannot quietly reintroduce an opaque panel. Phone touch keeps the flat 32% glass. Version 0.6.3, SW v85. --- README.md | 14 +++++++++++ app/globals.css | 35 ++++----------------------- package.json | 2 +- public/aero/background-glass.webp | Bin 1768 -> 0 bytes public/sw.js | 2 +- tests/chat-test.mjs | 39 ++++++++++++++++++++++++++++-- 6 files changed, 58 insertions(+), 34 deletions(-) delete mode 100644 public/aero/background-glass.webp diff --git a/README.md b/README.md index 8059642..5992133 100644 --- a/README.md +++ b/README.md @@ -1434,6 +1434,20 @@ Things I chose while building, with the reasoning: (measured: 268..1280 at a 1280 window) — a flat pane floating 40px inside the scene read as "cut off next to the sidebar". +94. **Aero glass goes back to glass (the "even wash" read as a white pane).** The uniform wash + (`background-glass.webp`) from decisions 91/93 made the rail and the chat chrome nearly + opaque: "why is the sidebar so white and non-translucent on the frutiger theme now? and the + chat mode looks even worse... its supposed to be translucent and blurry!". The rail, the chat + header, the welcome card and the composer now use the *same* recipe as every other surface in + the app — `background-blur.webp` under the shared gloss + veil + `--glass-bg`, exactly what + the deck cards do — and the dedicated wash asset is deleted. Measured on the pixels: the rail + runs from `181,229,241` over the sky to `133,174,75` over the grass (Δ166, unmistakably + translucent), while the chat and library screens still scan at 0 below-minimum text-contrast + findings in all three themes (Aero's near-black text tokens carry the thin glass). The chat + suite pins both halves of that: the shared recipe on all four panels *and* a translucency + check that the rail's tint follows the scene, so a future "make it uniform" pass cannot + quietly reintroduce an opaque panel. Phone touch keeps the flat 32% Ask-pill glass from 93. + 93. **Phone Aero glass, and a timed run you can actually see.** Two follow-ups from a phone. - *The chat read as a white pane.* On touch devices the chat header, welcome card and composer carried a 55% flat white veil while every other surface in the app used the thin 32% diff --git a/app/globals.css b/app/globals.css index f7f6c22..74deede 100644 --- a/app/globals.css +++ b/app/globals.css @@ -452,6 +452,7 @@ .aero .btn-secondary, .aero .chat-header, .aero .chat-empty, +.aero .chat-composer-band, .aero .segmented, /* Hand-rolled frost fills in the study, exam, chat and homework flows: white/xx utilities and the surface tokens — without this they render as @@ -564,15 +565,14 @@ /* Chat: the longest text in the app sits straight on the wallpaper. The assistant answers are deliberately bubble-less in the flat themes, and the user bubble's thin tint washes out over the bright parts of the scene — in - Aero both get a real pane: the same even wash as the chrome, a touch more - opaque (this is where the reading happens), so an answer reads as its own - bubble and its colour does not drift with the photo behind it. */ + Aero both get a real pane: the shared blurred wallpaper under a veil thick + enough for reading, still translucent enough to read as glass. */ .aero .chat-bubble, .aero .chat-answer { border-color: rgb(255 255 255 / 0.85); background-image: - linear-gradient(rgb(255 255 255 / 0.6), rgb(255 255 255 / 0.6)), - url("/aero/background-glass.webp?v=4"); + linear-gradient(rgb(255 255 255 / 0.3), rgb(255 255 255 / 0.3)), + url("/aero/background-blur.webp?v=4"); background-size: 100% 100%, cover; background-position: 0 0, center 42%; background-attachment: scroll, fixed; @@ -604,31 +604,6 @@ background-repeat: no-repeat, no-repeat; } -/* Chat chrome and the rail: one even frosted recipe. The pre-blurred, - extra-soft wash of the wallpaper (background-glass) sits under a uniform - white veil, so the surface colour cannot drift with the photo behind it — - the rail used to run from clear blue into a muddy green smear, the composer - picked up the river's teal and the header a saturated stripe of sky. */ -.aero .sidebar-surface, -.aero .chat-header, -.aero .chat-empty, -.aero .chat-composer-band { - background-image: - linear-gradient( - 180deg, - rgb(255 255 255 / 0.3) 0%, - rgb(255 255 255 / 0.1) 46%, - rgb(255 255 255 / 0.05) 53%, - rgb(255 255 255 / 0.12) 100% - ), - linear-gradient(rgb(255 255 255 / 0.46), rgb(255 255 255 / 0.46)), - url("/aero/background-glass.webp?v=4"); - background-size: 100% 100%, 100% 100%, cover; - background-position: 0 0, 0 0, center 42%; - background-attachment: scroll, scroll, fixed; - background-repeat: no-repeat, no-repeat, no-repeat; -} - /* The chat welcome (title, subtitle, suggestion chips): a card like the library's, stretched across the conversation column so it lines up with the floating header above and composer below it. */ diff --git a/package.json b/package.json index a102cc5..663bc8a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "freezmo", - "version": "0.6.2", + "version": "0.6.3", "private": true, "license": "GPL-3.0-only", "scripts": { diff --git a/public/aero/background-glass.webp b/public/aero/background-glass.webp deleted file mode 100644 index e7a075429c44694b49f6b060bba075ffb89fbf43..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1768 zcmaJ>Ycv#E6h1Q^%}g4`E0xC>Watu-2w{jw8k3PnDU8s~7|%wYcPb&}T__W?(jX;@ zEEK6clHQR?rBXL0TXw}>r7_;4g65^~5Q9PUQ3g_s60 zG4L1c_Y3xCgp2s;kYf>YC`xpeNJO`j3ZPSW!5IZr8AJdIZ~!FH{oxu)C0B#-!K#8~H(<}x+`WOIpkC%K)Z^G~m_x+hp99rao0|4;30Icu?K<)_u81J7k zX#S%%JPawrx2mU}GA|qe`41o#kFrE+dKiLY35W0!TOvDyfVzz?@ zr=$dr0t^f~nEpO26t`PUE4FnCyTvC&H#p|`C?fdK`4hMM%%tOM!d2&KgO<~s9kF9M znKQ4o)hFssx_a7eP~g_N*uGn&VWnjAt}--5w05D71*jhrcaS|7S{tqyJQFR@GPWs(w}vOQS_!^!-G?zZu?vYcDJ;bxJv+17Un7lg2{F2)g-kqVK&h2U(){jkp zlpT^9uiu;9RK!)D2x}9h7WVPap1X&MOnO_BQ{UE&lvOYV3-DkQ$LS7hCj2oYfw_Ap7S(BXy=$8nPi<6V4w)+($ z5E>HHM)l~N7_2AE|bh> z7fdic!`op8n6>TN>doJVC8OHc8ZkLFd#hF|542bw6y^T=Kpcs&A9lHoGIkjMsD7}G z@~LpEmY%Le_L(O@D6C%pa>pq`v(lAH>tY1$Sdr|hlggFmGu|>q`)*{hMh4|c&r-TP zJ|p`p0McgDrv-tw9mjQ>mR3Me{+lFg5TRZsl#_3~W%xrdO{f@=GC^-x_?pWx^9tuV z@ufOHgFRCd!>dcKNw*13u~Iu!SGDtrQx)7zZ||g1eX%BXXOc!5vn|-DYi0+pn@)b% zREzce^fuRO_JRc>w!4;p*;q2{hK8f1(MOzJkYxdrnqXCx8&D|Gng6sea6?mvLq%v} ztf6d$TI)skSGe_B#npkQb74|5spAhjBz&a;m%CL+)MaLdTAX76iBvsDdKAUeM;Xt|m! zKIJsBt7K{q8F+Y2?K6QXkGF`ALH@r_$;ol7A6Rf2hZFOhRgJv~f4Y z`)2wh(E-)Z)#^&eB8ak)8wK*$n?XKdR!W3OC_Ju{a)} W)EiezBmu>my9K=S%Q9i~U-A#i0hzJ@ diff --git a/public/sw.js b/public/sw.js index 9296924..f8f9b0e 100644 --- a/public/sw.js +++ b/public/sw.js @@ -9,7 +9,7 @@ * * Bumping CACHE_VERSION is how a new deploy invalidates old assets. */ -const CACHE_VERSION = "freezmo-v84"; +const CACHE_VERSION = "freezmo-v85"; const SHELL_CACHE = `${CACHE_VERSION}-shell`; const ASSET_CACHE = `${CACHE_VERSION}-assets`; diff --git a/tests/chat-test.mjs b/tests/chat-test.mjs index 38fa311..a146c75 100644 --- a/tests/chat-test.mjs +++ b/tests/chat-test.mjs @@ -320,7 +320,7 @@ try { bl: style.borderBottomLeftRadius, br: style.borderBottomRightRadius, border: style.borderTopWidth, - wash: style.backgroundImage.includes("background-glass"), + wash: style.backgroundImage.includes("background-blur"), }; }; return { @@ -354,8 +354,43 @@ try { ), JSON.stringify(chrome), ); + /* Translucency, measured on the pixels: an opaque (or near-opaque) panel + reads nearly the same colour over the bright sky and over the dark + grass; glass follows the scene behind it. */ + const railShot = await aero.page.screenshot({ type: "png", scale: "css" }); + const railData = `data:image/png;base64,${railShot.toString("base64")}`; + const railTint = await aero.page.evaluate(async ({ dataUrl }) => { + const image = await new Promise((resolve, reject) => { + const img = new Image(); + img.onload = () => resolve(img); + img.onerror = reject; + img.src = dataUrl; + }); + const canvas = document.createElement("canvas"); + canvas.width = image.width; + canvas.height = image.height; + const ctx = canvas.getContext("2d"); + ctx.drawImage(image, 0, 0); + const strip = (y) => { + const data = ctx.getImageData(30, y, 200, 12).data; + let r = 0, g = 0, b = 0, n = 0; + for (let i = 0; i < data.length; i += 4) { + r += data[i]; g += data[i + 1]; b += data[i + 2]; n += 1; + } + return [Math.round(r / n), Math.round(g / n), Math.round(b / n)]; + }; + return { upper: strip(440), lower: strip(680) }; + }, { dataUrl: railData }); + const tintDistance = Math.max( + ...[0, 1, 2].map((index) => Math.abs(railTint.upper[index] - railTint.lower[index])), + ); check( - "desktop: the rail and the chat chrome share one even wash", + "desktop: the rail is still translucent (its tint follows the scene)", + tintDistance >= 30, + `${railTint.upper.join(",")} → ${railTint.lower.join(",")} (Δ${tintDistance})`, + ); + check( + "desktop: the rail and the chat chrome share the app's glass recipe", chrome !== null && [chrome.card, chrome.header, chrome.empty, chrome.band].every((panel) => panel.wash), chrome ? [chrome.card.wash, chrome.header.wash, chrome.empty.wash, chrome.band.wash].join(",") : "missing", );