From 4e5a065777f7175393b9de2b799ed23502f2c845 Mon Sep 17 00:00:00 2001
From: SandObserver <260779319+SandObserver@users.noreply.github.com>
Date: Tue, 20 Oct 2026 12:28:58 -0400
Subject: [PATCH 1/2] Fix: fit the colour pickers on narrow phones
Put each colour row's label above its swatches in the phone layout, size
the swatch grid tracks to the 34px touch swatches, and let the HSB sliders
shrink below their intrinsic width. Add a 320px reflow test for the icon
colour picker in French and Persian.
---
e2e/reflow.spec.js | 33 +++++++++++++++++++++++++++++++++
ui/admin/index.html | 2 +-
ui/css/admin.css | 6 +++++-
3 files changed, 39 insertions(+), 2 deletions(-)
diff --git a/e2e/reflow.spec.js b/e2e/reflow.spec.js
index 738f26f7..0d8fcb5d 100644
--- a/e2e/reflow.spec.js
+++ b/e2e/reflow.spec.js
@@ -63,6 +63,39 @@ test.describe('on a touch screen', () => {
expect(wide, 'the page does not scroll sideways').toBeLessThanOrEqual(320);
});
}
+
+ for (const language of ['fr', 'fa']) {
+ test(`the icon colour picker fits at 320px in ${language}`, async ({ page, request }) => {
+ await seedConfig(request, { items: [app('one', 'One')], settings: { language } });
+ await dismissSetupPrompt(request);
+ await page.setViewportSize({ width: 320, height: 700 });
+ await page.goto('/admin/#dashboard');
+ await page.locator('#al [data-act="edit"]').first().click();
+ await page.locator('#icon-color-slot .cc-swatch[data-v="custom"]').click();
+ const picker = page.locator('#icon-color-slot');
+ await picker.locator('.cc-hues').waitFor({ state: 'visible' });
+ await animationsDone(page);
+ const misfits = await picker.evaluate(slot => {
+ const out = [];
+ for (const row of slot.querySelectorAll('.row')) {
+ const r = row.getBoundingClientRect();
+ for (const n of row.querySelectorAll('.cc-swatch, .rl, .hsb-ico, .hsb-range')) {
+ const b = n.getBoundingClientRect();
+ if (!b.width) continue;
+ if (b.left < r.left - 0.5 || b.right > r.right + 0.5) out.push(`${n.className} leaves its row`);
+ if (n.classList.contains('rl') && n.scrollWidth > n.clientWidth)
+ out.push(`label "${n.textContent}" is cut off`);
+ }
+ const sw = [...row.querySelectorAll('.cc-swatch')].map(n => n.getBoundingClientRect());
+ for (let i = 1; i < sw.length; i++)
+ if (Math.abs(sw[i].top - sw[i - 1].top) < 1 && Math.abs(sw[i].left - sw[i - 1].left) < sw[i].width)
+ out.push('two swatches overlap');
+ }
+ return out;
+ });
+ expect(misfits).toEqual([]);
+ });
+ }
});
test('the desktop Settings sidebar stays on screen while the list scrolls', async ({ page, request }) => {
diff --git a/ui/admin/index.html b/ui/admin/index.html
index 998dbaa3..7147b287 100644
--- a/ui/admin/index.html
+++ b/ui/admin/index.html
@@ -11,7 +11,7 @@
-
+
diff --git a/ui/css/admin.css b/ui/css/admin.css
index d195fea5..1ef5821a 100644
--- a/ui/css/admin.css
+++ b/ui/css/admin.css
@@ -366,7 +366,7 @@ button.rnm{font-family:inherit;color:inherit;background:none;border:0;padding:0;
.hsb-row{min-height:46px}
.hsb-track{display:flex;align-items:center;gap:10px;margin-inline-start:auto;flex:2;max-width:none}
.hsb-ico{width:17px;height:17px;color:var(--dm);flex-shrink:0;display:flex}
-.hsb-range{flex:1;height:6px;-webkit-appearance:none;appearance:none;border-radius:3px;cursor:pointer;outline:none}
+.hsb-range{flex:1;min-width:0;height:6px;-webkit-appearance:none;appearance:none;border-radius:3px;cursor:pointer;outline:none}
.hsb-hue{background:linear-gradient(var(--slider-dir),#f00,#ff0,#0f0,#0ff,#00f,#f0f,#f00)}
/* ── Segmented control (Container/Ping, Disk Health/System Summary) ──
@@ -734,6 +734,7 @@ a:focus-visible,button:focus-visible,[role="button"]:focus-visible{outline:2px s
.adm-range { height:44px; padding-block:19px; background-clip:content-box; border-radius:3px / 22px; }
.hsb-range { height:44px; padding-block:19px; background-clip:content-box; border-radius:3px / 22px; }
.cc-swatch { width:34px; height:34px; }
+ .cc-sw { grid-template-columns:repeat(auto-fit,34px); }
.cc-kw-clear { background-size:10px 10px; }
.cc-hues .cc-sw { grid-template-columns:repeat(7,34px); }
.cc-hues .cc-swatch { width:34px; height:34px; }
@@ -754,6 +755,9 @@ html.is-mobile .cp{width:100%}
html.is-mobile .sec{padding-block:16px calc(132px + env(safe-area-inset-bottom));padding-inline:max(16px,env(safe-area-inset-left)) max(16px,env(safe-area-inset-right))}
html.is-mobile .sec-title{font-size:var(--fs-large-title);line-height:var(--lh-large-title);letter-spacing:var(--tr-large-title)}
html.is-mobile .hsb-track{max-width:min(240px,62%)}
+html.is-mobile .cc-row{flex-wrap:wrap;row-gap:10px}
+html.is-mobile .cc-row .rl{flex:0 0 100%}
+html.is-mobile .cc-sw{flex:1 1 100%;grid-template-columns:repeat(7,auto);column-gap:0;justify-content:space-between}
html.is-mobile .sec-hdr{padding:10px 0 10px;flex-wrap:wrap;row-gap:6px}
html.is-mobile .sec-title{order:1;flex-basis:100%;min-width:0;overflow-wrap:break-word}
html.is-mobile .sec-hdr-actions{margin-inline-start:auto}
From b7224d4cd7dee0617bda14de0469b81db37bed87 Mon Sep 17 00:00:00 2001
From: SandObserver <260779319+SandObserver@users.noreply.github.com>
Date: Tue, 20 Oct 2026 17:29:25 -0400
Subject: [PATCH 2/2] Fix: give the colour sliders one shared length
Lay the HSB slider rows out on a subgrid of the tune area, so the label
column takes the longest label and every slider gets the same remaining
width. Labels no longer shrink to make room for a slider.
---
e2e/reflow.spec.js | 17 +++++++++++++++++
ui/admin/index.html | 2 +-
ui/css/admin.css | 7 ++++---
3 files changed, 22 insertions(+), 4 deletions(-)
diff --git a/e2e/reflow.spec.js b/e2e/reflow.spec.js
index 0d8fcb5d..be08028b 100644
--- a/e2e/reflow.spec.js
+++ b/e2e/reflow.spec.js
@@ -98,6 +98,23 @@ test.describe('on a touch screen', () => {
}
});
+test('the colour sliders share one length', async ({ page, request }) => {
+ await seedConfig(request, { items: [app('one', 'One')] });
+ await dismissSetupPrompt(request);
+ await page.setViewportSize({ width: 1280, height: 900 });
+ await openDashboardList(page);
+ await page.locator('#al [data-act="edit"]').first().click();
+ await page.locator('#icon-color-slot .cc-swatch[data-v="custom"]').click();
+ const sliders = page.locator('#icon-color-slot .hsb-range');
+ await expect(sliders).toHaveCount(3);
+ await animationsDone(page);
+ const boxes = await Promise.all((await sliders.all()).map(box));
+ for (const b of boxes.slice(1)) {
+ expect(b.x, 'every slider starts where the first does').toBeCloseTo(boxes[0].x, 0);
+ expect(b.width, 'every slider is as long as the first').toBeCloseTo(boxes[0].width, 0);
+ }
+});
+
test('the desktop Settings sidebar stays on screen while the list scrolls', async ({ page, request }) => {
await seedConfig(request, { items: MANY });
await dismissSetupPrompt(request);
diff --git a/ui/admin/index.html b/ui/admin/index.html
index 7147b287..28a56421 100644
--- a/ui/admin/index.html
+++ b/ui/admin/index.html
@@ -11,7 +11,7 @@
-
+
diff --git a/ui/css/admin.css b/ui/css/admin.css
index 1ef5821a..08b2ac4d 100644
--- a/ui/css/admin.css
+++ b/ui/css/admin.css
@@ -363,8 +363,10 @@ button.rnm{font-family:inherit;color:inherit;background:none;border:0;padding:0;
.cc-swatch.on{outline-color:Highlight}
}
.cc-rainbow{background:conic-gradient(from 90deg,#ff3b30,#ffcd00,#35c759,#0289ff,#cb30df,#ff3b30)}
-.hsb-row{min-height:46px}
-.hsb-track{display:flex;align-items:center;gap:10px;margin-inline-start:auto;flex:2;max-width:none}
+.cc-tune > .reveal-in{display:grid;grid-template-columns:max-content minmax(0,1fr);column-gap:10px}
+.cc-tune > .reveal-in > *{grid-column:1/-1}
+.hsb-row{min-height:46px;display:grid;grid-template-columns:subgrid;align-items:center}
+.hsb-track{display:flex;align-items:center;gap:10px}
.hsb-ico{width:17px;height:17px;color:var(--dm);flex-shrink:0;display:flex}
.hsb-range{flex:1;min-width:0;height:6px;-webkit-appearance:none;appearance:none;border-radius:3px;cursor:pointer;outline:none}
.hsb-hue{background:linear-gradient(var(--slider-dir),#f00,#ff0,#0f0,#0ff,#00f,#f0f,#f00)}
@@ -754,7 +756,6 @@ html.is-mobile .sb{display:none}
html.is-mobile .cp{width:100%}
html.is-mobile .sec{padding-block:16px calc(132px + env(safe-area-inset-bottom));padding-inline:max(16px,env(safe-area-inset-left)) max(16px,env(safe-area-inset-right))}
html.is-mobile .sec-title{font-size:var(--fs-large-title);line-height:var(--lh-large-title);letter-spacing:var(--tr-large-title)}
-html.is-mobile .hsb-track{max-width:min(240px,62%)}
html.is-mobile .cc-row{flex-wrap:wrap;row-gap:10px}
html.is-mobile .cc-row .rl{flex:0 0 100%}
html.is-mobile .cc-sw{flex:1 1 100%;grid-template-columns:repeat(7,auto);column-gap:0;justify-content:space-between}