diff --git a/ui/css/dashboard.css b/ui/css/dashboard.css index abe272f4..df4a86e2 100644 --- a/ui/css/dashboard.css +++ b/ui/css/dashboard.css @@ -1165,6 +1165,11 @@ html[data-theme="light"] .mob-widget-card[data-appearance="dark"] { } html[data-theme="light"] .widget[data-appearance="dark"][data-card="translucent"], html[data-theme="light"] .mob-widget-card[data-appearance="dark"][data-card="translucent"] { background:var(--card-fixed-dark) } +html:not([data-theme="light"]) .widget[data-appearance="light"], +html:not([data-theme="light"]) .mob-widget-card[data-appearance="light"] { + --glass-bg:#FFFFFF; --glass-border:rgba(0,0,0,.06); --card-dark:#FFFFFF; + --card-edge-hi:rgba(255,255,255,.9); --card-edge-lo:rgba(0,0,0,.05); +} @media (prefers-contrast: more) { html[data-theme="light"] { --glass-bg:rgba(255,255,255,.97); --glass-border:rgba(0,0,0,.35) } html[data-theme="light"] .widget:not([data-appearance="dark"]), diff --git a/ui/index.html b/ui/index.html index 76ab4b22..ad42ddbf 100644 --- a/ui/index.html +++ b/ui/index.html @@ -26,7 +26,7 @@ Stackyard - + diff --git a/ui/js/widget-toolbox.js b/ui/js/widget-toolbox.js index 2ed09a24..28e13ce8 100644 --- a/ui/js/widget-toolbox.js +++ b/ui/js/widget-toolbox.js @@ -78,15 +78,14 @@ export function theme() { const NS = 'http://www.w3.org/2000/svg'; const _params = new URLSearchParams(location.search); -/** Gives the card the dark card's edge and border while the widget paints its own dark background. +/** Matches the card to the background the widget paints, dark or light, in either theme. @param {boolean} dark */ export function setCardAppearance(dark) { const card = /** @type {HTMLElement | null | undefined} */ ( window.frameElement?.closest('.widget, .mob-widget-card') ); if (!card) return; - if (dark) card.dataset.appearance = 'dark'; - else delete card.dataset.appearance; + card.dataset.appearance = dark ? 'dark' : 'light'; } export function widgetId() { @@ -319,7 +318,8 @@ export function columns(opts = {}) { const color = colorOrFallback(opts.color, '#0a84ff'); const el = document.createElement('div'); el.className = 'tb-cols'; - el.style.cssText = `display:flex;align-items:stretch;height:100%;gap:${opts.gap != null ? opts.gap : 3}px`; + const half = (opts.gap != null ? opts.gap : 3) / 2; + el.style.cssText = `display:flex;align-items:stretch;height:100%;margin:0 -${half}px`; /** @type {HTMLElement[]} */ const fills = []; /** @type {HTMLElement[]} */ @@ -329,7 +329,10 @@ export function columns(opts = {}) { for (let i = 0; i < count; i++) { const col = document.createElement('div'); col.className = 'tb-col'; - col.style.cssText = `flex:1 1 0;position:relative;overflow:hidden;border-radius:${radius}px`; + col.style.cssText = + `flex:1 1 0;box-sizing:content-box;position:relative;overflow:hidden;` + + `border:solid transparent;border-width:0 ${half}px;background-clip:padding-box;` + + `border-radius:${radius + half}px / ${radius}px`; if (opts.track !== null) col.style.backgroundColor = opts.track || 'rgba(255,255,255,0.10)'; const fill = document.createElement('div'); fill.className = 'tb-col-fill'; diff --git a/ui/test/widget-toolbox-card.test.mjs b/ui/test/widget-toolbox-card.test.mjs index fbebc9d0..2d3fe3a5 100644 --- a/ui/test/widget-toolbox-card.test.mjs +++ b/ui/test/widget-toolbox-card.test.mjs @@ -8,13 +8,13 @@ const { setCardAppearance } = await import('../js/widget-toolbox.js'); const frameIn = card => ({ closest: sel => (sel === '.widget, .mob-widget-card' ? card : null) }); -test('a dark widget page marks its card dark, and a light one clears it', () => { +test('a widget page marks its card dark or light to match its own background', () => { const card = { dataset: {} }; globalThis.window = { frameElement: frameIn(card) }; setCardAppearance(true); assert.equal(card.dataset.appearance, 'dark'); setCardAppearance(false); - assert.equal('appearance' in card.dataset, false); + assert.equal(card.dataset.appearance, 'light'); }); test('a widget page outside a dashboard card changes nothing', () => { diff --git a/ui/test/widget-toolbox-columns.test.mjs b/ui/test/widget-toolbox-columns.test.mjs index 4ad4dc39..8a251c4d 100644 --- a/ui/test/widget-toolbox-columns.test.mjs +++ b/ui/test/widget-toolbox-columns.test.mjs @@ -66,6 +66,16 @@ test('more values than columns keeps the newest', () => { assert.deepEqual(heights(c), ['max(2px, 20.00%)', 'max(2px, 30.00%)']); }); +test('the gap between columns is transparent borders, so a fractional zoom keeps gaps equal', () => { + const c = columns({ count: 2, gap: 4 }); + assert.doesNotMatch(c.el.style.cssText, /gap:/); + assert.match(c.el.style.cssText, /margin:0 -2px/); + for (const col of c.el.children) { + assert.match(col.style.cssText, /border-width:0 2px/); + assert.match(col.style.cssText, /background-clip:padding-box/); + } +}); + test('a colour carrying a second declaration is refused', () => { const c = columns({ count: 1, color: 'red; background-image: url(x)' }); assert.equal(c.el.children[0].children[0].style.backgroundColor, '#0a84ff'); diff --git a/ui/widgets/backup/backup.html b/ui/widgets/backup/backup.html index 53a39c46..9061cbf6 100644 --- a/ui/widgets/backup/backup.html +++ b/ui/widgets/backup/backup.html @@ -121,7 +121,7 @@