From 592641ef159774173c41aec3596e66639fdd805f Mon Sep 17 00:00:00 2001
From: SandObserver <260779319+SandObserver@users.noreply.github.com>
Date: Fri, 16 Oct 2026 14:55:52 -0400
Subject: [PATCH] Fix weather card edges in daylight and uneven System Summary
column gaps
- Mark a widget card light when the widget paints a light background, and
give it the light card tokens in the dark theme.
- Space chart columns with transparent inline borders instead of flex gap.
---
ui/css/dashboard.css | 5 +++++
ui/index.html | 2 +-
ui/js/widget-toolbox.js | 13 ++++++++-----
ui/test/widget-toolbox-card.test.mjs | 4 ++--
ui/test/widget-toolbox-columns.test.mjs | 10 ++++++++++
ui/widgets/backup/backup.html | 2 +-
ui/widgets/books/index.html | 2 +-
ui/widgets/clock/analog.html | 2 +-
ui/widgets/clock/digital.html | 2 +-
ui/widgets/connections/connections-map.html | 2 +-
ui/widgets/connections/connections-vpn.html | 2 +-
ui/widgets/dashboard-switch/index.html | 2 +-
ui/widgets/disk-health/index.html | 2 +-
ui/widgets/dns/index.html | 2 +-
ui/widgets/github/contributions.html | 2 +-
ui/widgets/github/pullrequests.html | 2 +-
ui/widgets/nowplaying/index.html | 2 +-
ui/widgets/system-summary/index.html | 2 +-
ui/widgets/weather/index.html | 2 +-
19 files changed, 40 insertions(+), 22 deletions(-)
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 @@