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.
This commit is contained in:
Roland 2026-08-05 17:26:12 -06:00
parent ef5a3a1dbc
commit cdb07dc94f
No known key found for this signature in database
GPG key ID: 1BA620CE6C9A4680
2 changed files with 74 additions and 20 deletions

View file

@ -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(<Panel><div style={{width: 40}}><Progress percent={40} label='CPU'/></div></Panel>,
{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);
});
});
});

View file

@ -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 {