diff --git a/CHANGELOG.md b/CHANGELOG.md index ecdbc30..725ea43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 elsewhere they copy the link. A link that looks like it carries your own access to a paid feed asks before it goes anywhere. +### Changed + +- The Glass theme's panels catch the light: a bright rim along the top and left edges, a fainter + one along the far edges, and a soft sheen from the top-left corner. + ### Fixed - Swiping to the next or previous item takes a longer swipe, a quarter of the screen, so a diff --git a/tests/contrast.js b/tests/contrast.js index 67abd7e..72245e5 100644 --- a/tests/contrast.js +++ b/tests/contrast.js @@ -73,6 +73,8 @@ const over = (c, a, g) => g.map((x, i) => c[i] * a + x * (1 - a)); for (const [name, sel] of [['glass', ':root[data-theme="glass"]'], ['glass light', ':root[data-theme="glass"][data-mode="light"]']]) { const p = themes[name]; const tint = washes.map(w => [w, rgba(sel, w.n)]).reverse(); // the first listed is drawn on top + const sm = css.slice(css.indexOf(sel + ' {')).match(/--glass-sheen:rgba\((\d+),(\d+),(\d+),([\d.]+)\)/); + const sheen = [[+sm[1], +sm[2], +sm[3]], +sm[4]]; const worst = {}; for (const [W, H] of [[1440, 900], [390, 844], [1024, 1366]]) for (let x = 0; x <= W; x += W / 40) for (let y = 0; y <= H; y += H / 40) { @@ -81,8 +83,10 @@ for (const [name, sel] of [['glass', ':root[data-theme="glass"]'], ['glass light const d = Math.hypot((x - w.cx * W / 100) / (w.rx * W / 100), (y - w.cy * H / 100) / (w.ry * H / 100)); g = over(c, a * Math.max(0, 1 - d), g); } - // The list sits on the bare wash; the sidebar, detail pane and dialogs on 70% panel over it. - for (const [gn, gc] of [['the wash', g], ['a panel', over(rgb(p.panel), .7, g)]]) + // The list sits on the bare wash; the sidebar, detail pane and dialogs on 70% panel over it, + // and a panel's top-left corner under its sheen at full strength. + const panel = over(rgb(p.panel), .7, g); + for (const [gn, gc] of [['the wash', g], ['a panel', panel], ['a panel\'s sheen', over(...sheen, panel)]]) for (const fg of ['fg', 'dim', 'faint', 'accent', 'bad', 'warn']) { const r = ratio(p[fg], hex(gc)), k = `--${fg} on ${gn}`; if (!(k in worst) || r < worst[k]) worst[k] = r; diff --git a/web/app.css b/web/app.css index b88e460..a34f413 100644 --- a/web/app.css +++ b/web/app.css @@ -423,7 +423,9 @@ --bad:#ff6961; --shadow:0 10px 36px rgba(0,0,0,.45); --glass-edge:rgba(255,255,255,.10); - --glass-hi:rgba(255,255,255,.14); + --glass-hi:rgba(255,255,255,.26); + --glass-lo:rgba(255,255,255,.10); + --glass-sheen:rgba(255,255,255,.07); --wash1:rgba(64,120,255,.30); --wash2:rgba(175,82,222,.24); --wash3:rgba(48,176,199,.20); @@ -446,6 +448,8 @@ --shadow:0 10px 36px rgba(30,50,90,.16); --glass-edge:rgba(255,255,255,.65); --glass-hi:rgba(255,255,255,.9); + --glass-lo:rgba(255,255,255,.45); + --glass-sheen:rgba(255,255,255,.5); --wash1:rgba(64,120,255,.22); --wash2:rgba(175,82,222,.16); --wash3:rgba(48,176,199,.18); @@ -1082,7 +1086,10 @@ body:has(#player.on) #status{padding-bottom:3px} /* ---------- Glass: after Apple's Liquid Glass ---------- */ /* Translucent panels over a coloured wash, blurred and saturated the way macOS and iOS do it. Apple's glass also bends light at its edges; that needs an SVG displacement filter that only - Chromium applies to a backdrop, and only on a shape of fixed size, so it is left out (#43). */ + Chromium applies to a backdrop, and only on a shape of fixed size, so it is left out (#43). + What is kept is the light: the rim catches it at the top-left edges and, fainter, the far ones, + as it passes through (#51). */ +:root[data-theme="glass"]{--glass-rim:inset 1px 1px 0 var(--glass-hi),inset -1px -1px 0 var(--glass-lo)} :root[data-theme="glass"] body{ font-family:-apple-system,BlinkMacSystemFont,system-ui,Inter,"Segoe UI",Roboto,sans-serif; background: @@ -1099,11 +1106,14 @@ body:has(#player.on) #status{padding-bottom:3px} :root[data-theme="glass"] #detail, :root[data-theme="glass"] .card, :root[data-theme="glass"] .toast{ - background:color-mix(in srgb,var(--panel) 70%,transparent); + /* A sheen from the top-left corner, where the light comes from. It is a background layer rather + than a pseudo-element because the detail pane, the file list and the dialogs scroll, and an + absolutely placed layer inside a scroller scrolls away with the content. */ + background:linear-gradient(135deg,var(--glass-sheen),transparent 45%),color-mix(in srgb,var(--panel) 70%,transparent); -webkit-backdrop-filter:blur(24px) saturate(180%); backdrop-filter:blur(24px) saturate(180%); border-color:var(--glass-edge); - box-shadow:inset 0 1px 0 var(--glass-hi); + box-shadow:var(--glass-rim); } :root[data-theme="glass"] .toolbar, :root[data-theme="glass"] .ephead{ @@ -1113,7 +1123,7 @@ body:has(#player.on) #status{padding-bottom:3px} backdrop-filter:blur(20px) saturate(180%); } :root[data-theme="glass"] .card, -:root[data-theme="glass"] .toast{box-shadow:inset 0 1px 0 var(--glass-hi),var(--shadow)} +:root[data-theme="glass"] .toast{box-shadow:var(--glass-rim),var(--shadow)} :root[data-theme="glass"] .card{border-radius:20px} :root[data-theme="glass"] .toast{border-radius:14px} :root[data-theme="glass"] .toast.bad{border-color:var(--bad)} @@ -1126,7 +1136,7 @@ body:has(#player.on) #status{padding-bottom:3px} :root[data-theme="glass"] .tgroup, :root[data-theme="glass"] .tabs{ background:color-mix(in srgb,var(--panel2) 60%,transparent); - border-color:var(--glass-edge);border-radius:12px;box-shadow:inset 0 1px 0 var(--glass-hi); + border-color:var(--glass-edge);border-radius:12px;box-shadow:var(--glass-rim); } :root[data-theme="glass"] .btn, :root[data-theme="glass"] .sidetools button,