diff --git a/CHANGELOG.md b/CHANGELOG.md index e493a88..289264d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ --- develop --- +* issue#449: Contrast Hollyberry details dots against a neutral badge and darken amber and grey row markers * issue#447: Improve Deepness green and red row marker contrast on dark surfaces * issue#444: Brighten Dark theme red row markers while preserving panel header colors * issue#442: Keep Carrot theme status indicators legible on coral details titlebars and light table rows diff --git a/README.md b/README.md index b43a4e2..bf9da71 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,11 @@ The Deepness fixture checks contrasting green and red row markers on dark surfaces. See [Deepness theme validation](docs/deepness-theme.md) for screenshots and reproduction commands. +![Hollyberry dashboard with seeded measurements and alerts](images/hollyberry-theme-dashboard.png) + +The Hollyberry fixture checks readable details dots and row markers. See +[Hollyberry theme validation](docs/hollyberry-theme.md) for screenshots and tests. + ## Requirements - Cacti 1.2.17 or later (the `compat` value in the `INFO` file). The plugin is diff --git a/css/hollyberry.css b/css/hollyberry.css index f76a72d..18fdb35 100644 --- a/css/hollyberry.css +++ b/css/hollyberry.css @@ -54,7 +54,7 @@ } .color_yellow_bubble { - color: #e08a1e; + color: #9a5700; } .color_yellow, .color_yellow i { @@ -66,9 +66,23 @@ } .color_grey_bubble { - color: #a1a1a1; + color: #737373; } .color_grey, .color_grey i { color: black !important; } + +/* A neutral badge separates details dots from the title gradient. */ +#overlay_detail .cactiTableButton { + background: #fff; + border-radius: 11px; +} + +.inpa_sq.color_yellow { + background: #a45d00 !important; +} + +.inpa_sq.color_grey { + background: #737373 !important; +} diff --git a/docs/hollyberry-theme.md b/docs/hollyberry-theme.md new file mode 100644 index 0000000..fc99d10 --- /dev/null +++ b/docs/hollyberry-theme.md @@ -0,0 +1,47 @@ +# Hollyberry theme validation + +Hollyberry details dots sit on a neutral badge so their colors remain visible +against the core title gradient. Amber and grey row markers use darker colors +on light rows. Panel header backgrounds and text colors retain their palette. + +## Populated browser fixtures + +The screenshots use actual core and plugin Hollyberry stylesheets with synthetic +devices, response times, poller totals, and warning/error messages. They show +browser fixtures, not a live installation or production monitoring data. + +![Hollyberry dashboard with populated monitoring panels](../images/hollyberry-theme-dashboard.png) + +The browser test opens the real jQuery UI details dialog, checks its alert data, +and closes it. + +![Hollyberry details with synthetic warning and error rows](../images/hollyberry-theme-status.png) + +## Reproduce + +Use the setup in [Cacti theme validation](cacti-theme.md). Set `CACTI_ROOT` to the +adjacent core checkout and run from the plugin root: + +```sh +NODE_PATH="$CACTI_ROOT/tests/e2e/node_modules" mise exec node@22 -- node \ + "$CACTI_ROOT/tests/e2e/node_modules/@playwright/test/cli.js" test \ + --config tests/e2e/playwright.config.cjs +``` + +Set `UPDATE_THEME_SCREENSHOTS=1` to regenerate committed screenshots. The tests +check all four severity colors on odd, even, and page surfaces, including alpha +composition and samples at sRGB gradient endpoints and quarter points. Indicators +must meet 3:1 contrast against their backgrounds. Sampling does not prove contrast +at every pixel or against arbitrary background images. + +After committing, measure changed CSS line coverage: + +```sh +mise exec python@3.12 -- python "$CACTI_ROOT/tests/tools/report_changed_coverage.py" \ + --base fix/deepness-theme-status-contrast --language css --minimum 100 \ + --browser-root tests/e2e/test-results +``` + +This PR extends the shared browser and alpha-aware contrast tests above the +Deepness layer. Use `origin/develop` after the lower stack layers merge. The gate +measures executed changed CSS lines, not whole-plugin or branch coverage. diff --git a/images/hollyberry-theme-dashboard.png b/images/hollyberry-theme-dashboard.png new file mode 100644 index 0000000..1c83324 Binary files /dev/null and b/images/hollyberry-theme-dashboard.png differ diff --git a/images/hollyberry-theme-status.png b/images/hollyberry-theme-status.png new file mode 100644 index 0000000..4018468 Binary files /dev/null and b/images/hollyberry-theme-status.png differ diff --git a/tests/e2e/cacti-theme.spec.cjs b/tests/e2e/cacti-theme.spec.cjs index 186bfa6..750b4e8 100644 --- a/tests/e2e/cacti-theme.spec.cjs +++ b/tests/e2e/cacti-theme.spec.cjs @@ -11,7 +11,7 @@ async function installCss(page, file) { await page.addStyleTag({ url: 'http://cacti-theme.test/' + path }); } -for (const theme of ['cacti', 'carrot', 'dark', 'deepness']) { +for (const theme of ['cacti', 'carrot', 'dark', 'deepness', 'hollyberry']) { test(`${theme} seeded dashboard displays measurements and opens alert details`, async ({ page }, info) => { await page.setViewportSize({ width: 1140, height: 520 }); await page.setContent(readFileSync(resolve(root, 'tests/fixtures/Ui/cacti-theme.html'), 'utf8').replaceAll('Cacti theme', `${theme[0].toUpperCase() + theme.slice(1)} theme`)); @@ -65,7 +65,7 @@ test.afterEach(async ({ page }, info) => { writeFileSync(info.outputPath('changed-coverage.json'), JSON.stringify(coverage)); }); -for (const theme of ['cacti', 'carrot', 'dark', 'deepness']) { +for (const theme of ['cacti', 'carrot', 'dark', 'deepness', 'hollyberry']) { for (const row of ['odd', 'even', '']) { test(`${theme} severity indicators contrast with titlebars and ${row || 'white'} rows`, async ({ page }) => { await page.setContent('
' + ['green', 'red', 'yellow', 'grey'].map(color => @@ -97,7 +97,9 @@ for (const theme of ['cacti', 'carrot', 'dark', 'deepness']) { const image = getComputedStyle(e).backgroundImage; if (image.startsWith('linear-gradient(')) { const stops = (image.match(/rgba?\([^)]+\)/g) || []).map(rgba); - surfaces = surfaces.flatMap(bg => stops.map(stop => over(stop, bg))); + const samples = stops.flatMap((stop, index) => index === 0 ? [stop] : + [.25, .5, .75, 1].map(t => stop.map((v, channel) => stops[index - 1][channel] * (1 - t) + v * t))); + surfaces = surfaces.flatMap(bg => samples.map(stop => over(stop, bg))); } } const style = getComputedStyle(element), color = rgba(square ? style.backgroundColor : style.color);