From cdb07dc94f572a2b952721e657635e9daf9f9637 Mon Sep 17 00:00:00 2001 From: Roland Date: Wed, 5 Aug 2026 17:26:12 -0600 Subject: [PATCH] review: hold the track to what each theme actually shows Three review points on the border removal. The width half of "the size it was" read `style.minWidth` back, which is the same string whether or not the box it produces is right -- a content-box regression would have passed it. The bar is now mounted in a 40px wrapper so the min-width floor is exercised by layout and `getBoundingClientRect` measures it; reverting to 5rem fails all four themes. "`--head` is off both surfaces in every theme" overstated the monochrome case: amber is 1.11 against a Panel and 1.04 against the page, night 1.05 and 1.01. Amber can afford a brighter track and now takes `--table-line` (1.34 / 1.43). Night cannot -- the label crosses the empty track and measures 5.25 at `--head`, 4.22 at `--table-line`, 3.42 at `--border`, so every track with a visible edge puts the label under 4.5. That is recorded as a deliberate near-flat track rather than described as an edge it does not have. And the single `> 1.05` floor had no slack in night, where the real ratio is 1.053, so it asserted "not the same color" while reading like a visibility claim. Each theme is now held to its own measured floor, against the page as well as the Panel -- bars land on both -- with night's 1.04 / 1.005 naming what that theme really does. --- app/src/components/ui/ui-layout.cy.js | 67 +++++++++++++++++++++------ app/src/components/ui/ui.css | 27 +++++++++-- 2 files changed, 74 insertions(+), 20 deletions(-) diff --git a/app/src/components/ui/ui-layout.cy.js b/app/src/components/ui/ui-layout.cy.js index e391befc..00776d7a 100644 --- a/app/src/components/ui/ui-layout.cy.js +++ b/app/src/components/ui/ui-layout.cy.js @@ -955,19 +955,37 @@ describe('a progress bar has room for the label inside it', () => { }); }); + /* + * What the track really separates from, per theme, and NOT a single floor: the monochrome + * themes collapse the surface ramp, so one number either lets light and dark regress or + * fails night for being what it deliberately is. These are the measured ratios rounded + * down, so each theme is held to its own behaviour -- night's near-flat 1.05 / 1.01 is + * recorded as such rather than passed off as an edge. See the note in ui.css for why + * night cannot have a brighter track: the label crossing it drops below 4.5 if it does. + */ + const trackFloors = { + light: {panel: 1.2, page: 1.1}, + dark: {panel: 1.2, page: 1.08}, + amber: {panel: 1.3, page: 1.4}, + night: {panel: 1.04, page: 1.005}, + }; + themeNames.forEach((theme) => { it(`draws no outline of its own in ${theme}, and is the size it was with one`, () => { /* - * The border came off. Both halves matter: the bar must not be the 1px smaller - * inside that removing an outline usually makes it -- it is not, because - * `box-sizing: border-box` is global and the declared height was always the outer - * box -- and the empty part of the track has to keep an edge of its own now that - * no line is drawing one, which is why the track paints `--head` rather than the - * `--bg` / `--panel` pair that a bar sits on. + * The border came off, and the bar must not be the 1px smaller inside that + * removing an outline usually makes it -- it is not, because `box-sizing: + * border-box` is global and the declared size was always the outer box. + * + * The bar is mounted in a wrapper NARROWER than its min-width so that the floor is + * exercised by layout. Reading `style.minWidth` instead, as the first version of + * this did, only repeats the declaration back: it is the same string whether or not + * the box it produces is right, so a content-box regression would have passed. * * Measured against `rem`, not px: 1.375rem and 7.5rem move with `--ui-scale`. */ - cy.mountUI(bar({percent: 40, label: 'CPU Usage'}), {theme}); + cy.mountUI(
, + {theme}); cy.get('.wrolpi-progress').should(($bar) => { const style = getComputedStyle($bar[0]); @@ -976,20 +994,39 @@ describe('a progress bar has room for the label inside it', () => { .to.equal('0px')); const rem = 16 * uiScale(); - expect($bar[0].getBoundingClientRect().height, 'the bar is the height it was') - .to.be.closeTo(1.375 * rem, 0.5); - expect(parseFloat(style.minWidth), 'and the width it was') - .to.be.closeTo(7.5 * rem, 0.5); + const box = $bar[0].getBoundingClientRect(); + expect(box.height, 'the bar is the height it was').to.be.closeTo(1.375 * rem, 0.5); + expect(box.width, 'and no narrower than it was, laid out against a 40px parent') + .to.be.at.least(7.5 * rem - 0.5); }); + }); + + it(`separates the empty track from what the bar sits on in ${theme}`, () => { + /* + * The other half of removing the border: with no line around it, a bar at 0% is + * only the track, so the track has to be off BOTH surfaces a bar lands on -- a + * Panel on Status and Downloads, the page itself for the overall upload bar. + */ + cy.mountUI(bar({percent: 40, label: 'CPU Usage'}), {theme}); - // The Panel the bar sits on, which is what the track has to stay off. cy.get('.wrolpi-progress').should(($bar) => { const track = getComputedStyle($bar[0]).backgroundColor; - const panel = getComputedStyle($bar[0].closest('.wrolpi-panel')).backgroundColor; expect(track, 'the track paints a surface of its own') .to.not.match(/^rgba\(.*,\s*0(\.0+)?\)$/); - expect(contrast(toRgb(track), toRgb(panel)), 'track against the panel under it') - .to.be.greaterThan(1.05); + + const panel = getComputedStyle($bar[0].closest('.wrolpi-panel')).backgroundColor; + expect(contrast(toRgb(track), toRgb(panel)), 'track against a Panel under it') + .to.be.at.least(trackFloors[theme].panel); + + /* + * The page is read from `--bg`, not from the root's backgroundColor: `body` + * carries the page color and the root element carries none, so computing it + * gives `rgba(0, 0, 0, 0)` and any comparison against it is unfalsifiable. + */ + const page = hexToRgb(getComputedStyle(document.documentElement) + .getPropertyValue('--bg').trim()); + expect(contrast(toRgb(track), page), 'track against the page under it') + .to.be.at.least(trackFloors[theme].page); }); }); }); diff --git a/app/src/components/ui/ui.css b/app/src/components/ui/ui.css index a3289aa2..645a1360 100644 --- a/app/src/components/ui/ui.css +++ b/app/src/components/ui/ui.css @@ -1161,11 +1161,13 @@ html .mantine-Notification-closeButton:hover { * band slides from `margin-left: -35%` to `100%` in full view, so the animation ran from * outside the left edge to outside the right one, painting over whatever sat beside it. * - * No border: the track is drawn by its own fill instead, so `--head` rather than `--bg`. - * A bar sits on a Panel as often as on the page, and `--bg` is invisible against one of - * those two by definition -- `--head` is off both in every theme. Height and min-width - * are the outer box (`box-sizing: border-box` is global), so dropping the 1px border left - * the bar exactly the size it was. + * No border, so the EMPTY part of the track has to be its own edge. A bar sits on a Panel + * (Status, Downloads) as often as on the page (the overall upload bar), and `--bg` is by + * definition invisible against one of those two, so the track is `--head`: 1.14 against the + * page and 1.25 against a Panel in light, 1.10 / 1.24 in dark. + * + * Height and min-width are the outer box -- `box-sizing: border-box` is global -- so + * dropping the 1px border left the bar exactly the size it was. */ .wrolpi-progress { position: relative; @@ -1269,6 +1271,21 @@ html[data-theme="light"] .wrolpi-progress-fill { filter: brightness(1.75) saturate(0.55); } +/* + * The monochrome themes collapse the surface ramp, so `--head` barely separates from what + * the bar sits on: in amber 1.11 against a Panel and 1.04 against the page, in night 1.05 + * and 1.01 -- a 0% bar has next to no silhouette. Amber can afford a brighter track and + * takes one; night cannot, and MEASURED is why. The label crosses the empty track, and + * against night's `--text` the candidates go 5.25 at `--head`, 4.22 at `--table-line`, + * 3.42 at `--border` -- everything with a visible edge is below the 4.5 the label needs. + * A hairline would buy the edge back, but a hairline is the border this removed. + * + * So night's empty track is deliberately near-flat, and the bar's own fill is what shows. + */ +html[data-theme="amber"] .wrolpi-progress { + background: var(--table-line); +} + /* Night: a mostly-full bar must not become a large bright surface. */ html[data-theme="night"] .wrolpi-progress-fill, html[data-theme="amber"] .wrolpi-progress-fill {