From 93b64b15b1adc374fab7e1d0f83103fbfc50970b Mon Sep 17 00:00:00 2001 From: kavehtoyser Date: Mon, 29 Jun 2026 19:01:47 +0200 Subject: [PATCH 1/6] Spec: dashboard main-page polish Frontend-only polish of the manager dashboard: widen Services, slim and explain the Notifications rail, make LOGS/DOCS read as buttons, and vertically center short content to fix the top-heavy empty void. Co-Authored-By: Claude Opus 4.8 --- ...06-29-dashboard-main-page-polish-design.md | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 docs/superpowers/specs/2026-06-29-dashboard-main-page-polish-design.md diff --git a/docs/superpowers/specs/2026-06-29-dashboard-main-page-polish-design.md b/docs/superpowers/specs/2026-06-29-dashboard-main-page-polish-design.md new file mode 100644 index 0000000..16e4d30 --- /dev/null +++ b/docs/superpowers/specs/2026-06-29-dashboard-main-page-polish-design.md @@ -0,0 +1,91 @@ +# Dashboard main-page polish — design + +**Date:** 2026-06-29 +**Branch:** `dashboard-main-page-polish` → `develop` +**Scope:** Frontend-only polish of the manager dashboard (the Dash "main page"). No backend, no API, no new data. The logs *view* page is explicitly out of scope (unchanged). + +## Problem + +On a wide monitor the dashboard reads top-heavy and sparse: content hugs the top with a large empty void below. Secondary issues raised by the user: + +1. The **Services** panel feels too narrow relative to its importance. +2. The **Notifications** panel is unexplained — an empty "No notifications." reads as "what is this section?" rather than "nothing wrong." +3. The per-service **LOGS / DOCS** links are not obviously clickable (tiny uppercase text, underline only on hover). + +## Goals + +- Make Services the clear hero (wider) and Notifications a slim, self-explanatory right rail. +- Remove the top-heavy empty void without inventing new content. +- Make LOGS / DOCS read as actionable controls. + +## Non-goals (YAGNI) + +- No new stats/summary bar, service counts, or notification badges. +- No structural rewrite of the Dash layout or callbacks. +- No change to the logs view page, login, or any backend/endpoint. + +## Design + +All changes are in `manager/routers/dashboard_api.py` (layout markup) and +`manager/assets/dashboard_styles.css` (styling). The design tokens in +`tokens.css` are reused; no new colors. + +### 1. Widen Services, slim the sidebar +`.grid` columns change from `1.85fr 1fr` to **`2.6fr 0.9fr`**. Services gets +the dominant width; Notifications becomes a narrow rail. The existing +`@media (max-width:760px)` rule already collapses to a single column, so the +mobile path is unchanged. + +### 2. Explain Notifications +Add a one-line muted subtitle beneath the "Notifications" heading in the panel +head, e.g.: + +> Alerts services raise via the SDK `notify()` — info, warnings, critical. + +Implemented as a small `

` element in the Notifications +panel head. A new `.panel-sub` style (muted, ~11px) renders it quietly. The +Services panel head is left as-is (no subtitle needed). + +### 3. Make LOGS / DOCS obvious +Restyle the per-service `.lnk` links to small bordered pill buttons that reuse +the look of the existing `.act` controls (1px border, rounded, accent on +hover) instead of underline-on-hover text. Keep them compact so the row height +is unchanged. Markup stays the same (``); only CSS changes, +plus an accessible `:focus-visible` ring to match `.act`. + +### 4. Fix top-heavy emptiness +Vertically center the page content when it is shorter than the viewport, while +falling back to normal top-aligned scrolling once there are enough services to +fill the screen. Implemented with flex auto-margins (which center but never +clip): + +- The body becomes a flex column filling `min-height:100vh` (banner + page). +- `.page` becomes a flex child with `flex:1` and `display:flex; + flex-direction:column`. +- The inner `.grid` gets `margin-block:auto`. Auto margins center the grid + vertically when there is spare room; when the grid is taller than the + available space the margins collapse to zero and the page scrolls normally — + no overflow clipping. + +This removes the void below the cards on a tall screen without adding content. + +## Testing + +Extend the existing guard tests in `tests/manager_test/test_ui_redesign.py` +(asset/markup string asserts, consistent with the current approach): + +- Notifications subtitle text (`panel-sub`) is present in the rendered layout. +- `.panel-sub` style exists in `dashboard_styles.css`. +- `.lnk` is styled as a bordered button (border + border-radius) and has a + `:focus-visible` rule. +- `.grid` uses the new `2.6fr 0.9fr` template. +- Vertical-fill rule present (`min-height:100vh` on body, `flex:1` on `.page`). + +Run locally before pushing: `black --check`, `flake8`, `pylint manager`, and +`pytest tests/manager_test/test_ui_redesign.py`. + +## Rollout + +Frontend-only; ships in the next `develop` → `master` release alongside the +logs-viewer work. The live server picks it up on the next manager image build +(connectors untouched). From 49f2a039b5ae144005b58b49f0455d5e077fb33d Mon Sep 17 00:00:00 2001 From: kavehtoyser Date: Mon, 29 Jun 2026 19:09:24 +0200 Subject: [PATCH 2/6] Plan: dashboard main-page polish Implementation plan (4 tasks) plus a spec refinement: vertical fill uses a viewport-relative min-height on .page (vh is ancestor-independent, so it works through Dash's wrapper divs) rather than a body flex chain. Co-Authored-By: Claude Opus 4.8 --- .../2026-06-29-dashboard-main-page-polish.md | 307 ++++++++++++++++++ ...06-29-dashboard-main-page-polish-design.md | 20 +- 2 files changed, 320 insertions(+), 7 deletions(-) create mode 100644 docs/superpowers/plans/2026-06-29-dashboard-main-page-polish.md diff --git a/docs/superpowers/plans/2026-06-29-dashboard-main-page-polish.md b/docs/superpowers/plans/2026-06-29-dashboard-main-page-polish.md new file mode 100644 index 0000000..bf91e15 --- /dev/null +++ b/docs/superpowers/plans/2026-06-29-dashboard-main-page-polish.md @@ -0,0 +1,307 @@ +# Dashboard Main-Page Polish Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Polish the manager dashboard so Services is the clear hero, Notifications is a slim self-explanatory rail, LOGS/DOCS read as buttons, and short content no longer leaves a top-heavy void. + +**Architecture:** Frontend-only. Two files: `manager/routers/dashboard_api.py` (Dash layout markup — one added subtitle element) and `manager/assets/dashboard_styles.css` (all styling). Guard tests are string-assert checks in `tests/manager_test/test_ui_redesign.py`, matching the existing pattern (read the CSS file / `str(app.layout)` and assert substrings). + +**Tech Stack:** Dash (Plotly) layout in Python, plain CSS with design tokens from `tokens.css`, pytest guard tests via `test_client` fixtures in `tests/conftest.py`. + +## Global Constraints + +- No backend, API, endpoint, or data-model changes. CSS + one markup element only. +- No external resources: CSS must contain no `http://` or `https://` (enforced by `test_dashboard_css_uses_tokens`). +- Reuse existing design tokens from `tokens.css` (`var(--muted)`, `var(--line)`, `var(--accent)`, etc.) — introduce no new colors. +- The logs view page (`logs.html`), login, and `max-width:1600px` on `.page` stay as-is. Do not reintroduce `max-width:1180px` (guarded by `test_dashboard_page_width_widened`). +- Run before pushing: `/home/kaveh/miniconda3/bin/python -m black --check`, `... -m flake8`, `... -m pylint manager`, and `./venv/bin/python -m pytest tests/manager_test/test_ui_redesign.py`. +- Mobile rule `@media (max-width:760px){ .grid{grid-template-columns:1fr} ... }` must keep collapsing the grid to one column. + +--- + +### Task 1: Widen Services / slim sidebar + vertical fill + +**Files:** +- Modify: `manager/assets/dashboard_styles.css` (the `.page` rule at line 32 and the `.grid` rule at line 33) +- Test: `tests/manager_test/test_ui_redesign.py` + +**Interfaces:** +- Consumes: nothing from other tasks. +- Produces: the `.grid` / `.page` CSS shape that Task 2 and Task 3 visually sit inside; no code symbols. + +Current CSS (lines 31-33): + +```css +/* ---------- page / grid ---------- */ +.page{max-width:1600px;margin:0 auto;padding:1.8rem 1.6rem 4rem} +.grid{display:grid;grid-template-columns:1.85fr 1fr;gap:1.4rem;align-items:start} +``` + +- [ ] **Step 1: Write the failing test** + +Add to `tests/manager_test/test_ui_redesign.py`: + +```python +def test_dashboard_grid_widens_services_and_fills_viewport(): + css = (ASSETS / "dashboard_styles.css").read_text().replace(" ", "") + # Services hero is wider than the slim Notifications rail + assert "grid-template-columns:2.6fr0.9fr" in css + assert "grid-template-columns:1.85fr1fr" not in css + # Page fills the viewport height (minus the banner) and centers when short + assert "min-height:calc(100vh-4rem)" in css + assert "margin-block:auto" in css + assert "box-sizing:border-box" in css +``` + +- [ ] **Step 2: Run test to verify it fails** + +Run: `./venv/bin/python -m pytest "tests/manager_test/test_ui_redesign.py::test_dashboard_grid_widens_services_and_fills_viewport" -v` +Expected: FAIL (`assert "grid-template-columns:2.6fr0.9fr" in css` is False — file still has `1.85fr 1fr`). + +- [ ] **Step 3: Write minimal implementation** + +Replace lines 31-33 of `manager/assets/dashboard_styles.css` with: + +```css +/* ---------- page / grid ---------- */ +.page{ + max-width:1600px;margin:0 auto;padding:1.8rem 1.6rem 4rem; + box-sizing:border-box;min-height:calc(100vh - 4rem); + display:flex;flex-direction:column; +} +.grid{ + display:grid;grid-template-columns:2.6fr 0.9fr;gap:1.4rem;align-items:start; + margin-block:auto;width:100%; +} +``` + +Notes for the implementer: +- `vh` is viewport-relative, so `min-height:calc(100vh - 4rem)` makes `.page` nearly fill the screen without touching `body` or Dash's wrapper divs. The `4rem` budgets for the top banner. +- `box-sizing:border-box` is required because there is no global reset and `.page` has padding; without it the padding would add to the `100vh` height and force a scrollbar. +- `margin-block:auto` on `.grid` centers it vertically inside the tall `.page` when there is spare room; when the grid is taller than the page the auto margins collapse to 0 and the page grows/scrolls normally. +- `width:100%` keeps the grid full-width inside the flex column (a flex item would otherwise shrink to content width). + +- [ ] **Step 4: Run test to verify it passes** + +Run: `./venv/bin/python -m pytest "tests/manager_test/test_ui_redesign.py::test_dashboard_grid_widens_services_and_fills_viewport" -v` +Expected: PASS + +- [ ] **Step 5: Commit** + +```bash +git add manager/assets/dashboard_styles.css tests/manager_test/test_ui_redesign.py +git commit -m "Dashboard: widen Services, slim sidebar, fill viewport height" +``` + +--- + +### Task 2: Explain the Notifications panel + +**Files:** +- Modify: `manager/routers/dashboard_api.py` (the Notifications panel head in `app.layout`, around lines 334-340) +- Modify: `manager/assets/dashboard_styles.css` (add `.panel-sub` near the `.panel-head` rules, ~line 44) +- Test: `tests/manager_test/test_ui_redesign.py` + +**Interfaces:** +- Consumes: nothing from other tasks. +- Produces: a `

` element with the literal subtitle text below; relied on only by its own test. + +Current markup (lines 334-345 of `dashboard_api.py`): + +```python + # Notifications panel + html.Section( + className="panel", + children=[ + html.Div( + className="panel-head", + children=[html.H2("Notifications")], + ), + html.Div( + id="notifications-content", + children=[generate_table_notifications()], + ), + ], + ), +``` + +- [ ] **Step 1: Write the failing test** + +Add to `tests/manager_test/test_ui_redesign.py`: + +```python +def test_notifications_panel_has_explanation(): + from manager.routers import dashboard_api + + layout = str(dashboard_api.app.layout) + assert "Alerts services raise" in layout # one-line description of the panel + css = (ASSETS / "dashboard_styles.css").read_text() + assert ".panel-sub" in css +``` + +- [ ] **Step 2: Run test to verify it fails** + +Run: `./venv/bin/python -m pytest "tests/manager_test/test_ui_redesign.py::test_notifications_panel_has_explanation" -v` +Expected: FAIL (`assert "Alerts services raise" in layout` is False — no subtitle yet). + +- [ ] **Step 3: Write minimal implementation** + +In `manager/routers/dashboard_api.py`, change the Notifications `panel-head` children to include a subtitle (replace the single-child `panel-head` Div shown above): + +```python + html.Div( + className="panel-head", + children=[ + html.Div( + [ + html.H2("Notifications"), + html.P( + "Alerts services raise via the SDK" + " notify() — info, warnings, critical.", + className="panel-sub", + ), + ] + ) + ], + ), +``` + +Then add the `.panel-sub` style to `manager/assets/dashboard_styles.css` immediately after the `.panel-head h2` rule (line 44): + +```css +.panel-sub{margin:.25rem 0 0;font-size:11px;line-height:1.4;color:var(--muted)} +``` + +Note: keep the `()` in `notify()` but do not include any URL — the CSS/markup must stay free of `http(s)://` (Task constraint). The text uses a plain hyphen. + +- [ ] **Step 4: Run test to verify it passes** + +Run: `./venv/bin/python -m pytest "tests/manager_test/test_ui_redesign.py::test_notifications_panel_has_explanation" -v` +Expected: PASS + +- [ ] **Step 5: Commit** + +```bash +git add manager/routers/dashboard_api.py manager/assets/dashboard_styles.css tests/manager_test/test_ui_redesign.py +git commit -m "Dashboard: explain the Notifications panel with a subtitle" +``` + +--- + +### Task 3: Make LOGS / DOCS read as buttons + +**Files:** +- Modify: `manager/assets/dashboard_styles.css` (the `.lnk` rules at lines 90-95) +- Test: `tests/manager_test/test_ui_redesign.py` + +**Interfaces:** +- Consumes: nothing from other tasks. +- Produces: restyled `.lnk` (markup unchanged — `get_service_log_link` / `get_service_docs_link` still emit ``). + +Current CSS (lines 88-95): + +```css +/* ---------- service actions ---------- */ +.svc-actions{grid-area:actions;display:flex;gap:.4rem} +.lnk{ + font-family:var(--mono);font-size:10.5px;letter-spacing:.08em;text-transform:uppercase; + color:var(--muted);text-decoration:none;border-bottom:1px solid transparent;padding:.1rem 0; + transition:color .15s,border-color .15s; +} +.lnk:hover{color:var(--accent);border-color:var(--accent)} +``` + +- [ ] **Step 1: Write the failing test** + +Add to `tests/manager_test/test_ui_redesign.py`: + +```python +def test_service_links_look_like_buttons(): + css = (ASSETS / "dashboard_styles.css").read_text().replace(" ", "") + # .lnk is now a bordered pill, not underline-on-hover text + assert ".lnk{" in css + lnk_block = css.split(".lnk{", 1)[1].split("}", 1)[0] + assert "border:1px solid" in lnk_block + assert "border-radius:" in lnk_block + # keyboard focus ring, matching .act + assert ".lnk:focus-visible{" in css +``` + +- [ ] **Step 2: Run test to verify it fails** + +Run: `./venv/bin/python -m pytest "tests/manager_test/test_ui_redesign.py::test_service_links_look_like_buttons" -v` +Expected: FAIL (the current `.lnk` block has `border-bottom`, not `border:1px solid`, and no `:focus-visible`). + +- [ ] **Step 3: Write minimal implementation** + +Replace lines 90-95 of `manager/assets/dashboard_styles.css` with: + +```css +.lnk{ + font-family:var(--mono);font-size:10px;letter-spacing:.1em;text-transform:uppercase; + color:var(--muted);text-decoration:none; + border:1px solid var(--line);border-radius:7px;padding:.34rem .6rem; + transition:color .15s,border-color .15s,background .15s; +} +.lnk:hover{color:var(--accent);border-color:var(--accent-dim);background:rgba(94,230,208,.06)} +.lnk:focus-visible{outline:2px solid var(--accent);outline-offset:2px} +``` + +Note: this mirrors the existing `.act` button look (border + rounded + accent hover + focus ring) so the two LOGS/DOCS controls clearly read as clickable. `.svc-actions` already has `gap:.4rem`, which spaces the two pills correctly — no change needed there. + +- [ ] **Step 4: Run test to verify it passes** + +Run: `./venv/bin/python -m pytest "tests/manager_test/test_ui_redesign.py::test_service_links_look_like_buttons" -v` +Expected: PASS + +- [ ] **Step 5: Commit** + +```bash +git add manager/assets/dashboard_styles.css tests/manager_test/test_ui_redesign.py +git commit -m "Dashboard: style LOGS/DOCS as buttons with focus ring" +``` + +--- + +### Task 4: Full guard-suite + lint pass + +**Files:** +- No new code. Verification only. + +- [ ] **Step 1: Run the full UI guard suite** + +Run: `./venv/bin/python -m pytest tests/manager_test/test_ui_redesign.py -v` +Expected: PASS (all existing tests + the three new ones). Confirms no earlier guard (e.g. `test_dashboard_layout_builds`, `test_dashboard_page_width_widened`, `test_notifications_panel_is_live`) regressed. + +- [ ] **Step 2: Lint the changed files** + +Run: +```bash +/home/kaveh/miniconda3/bin/python -m black --check manager/routers/dashboard_api.py tests/manager_test/test_ui_redesign.py +/home/kaveh/miniconda3/bin/python -m flake8 manager/routers/dashboard_api.py tests/manager_test/test_ui_redesign.py +/home/kaveh/miniconda3/bin/python -m pylint manager +``` +Expected: black "would be left unchanged", flake8 no output, pylint 10.00/10. (CSS is not linted.) If black reports a reformat on `dashboard_api.py`, run it without `--check` to apply, then re-commit. + +- [ ] **Step 3: Commit any formatting fixup (only if Step 2 changed files)** + +```bash +git add -A +git commit -m "Dashboard polish: black formatting" +``` + +--- + +## Self-Review + +**Spec coverage:** +- Widen Services / slim sidebar → Task 1 (`2.6fr 0.9fr`). ✓ +- Explain Notifications → Task 2 (`panel-sub` subtitle). ✓ +- LOGS/DOCS obvious → Task 3 (bordered buttons + focus ring). ✓ +- Fix top-heavy emptiness → Task 1 (`min-height:calc(100vh - 4rem)` + `margin-block:auto`). ✓ +- Testing section (guard-test asserts) → Tasks 1-3 each add one test; Task 4 runs the full suite + lint. ✓ +- Non-goals (no stats bar, no badges, no logs-view change, no backend) → respected; no task touches them. ✓ + +**Placeholder scan:** No TBD/TODO; every code step shows full CSS/markup and exact commands. ✓ + +**Type/string consistency:** The subtitle literal "Alerts services raise via the SDK notify() — info, warnings, critical." in Task 2's implementation matches the test's substring "Alerts services raise". The class names `.panel-sub`, `.lnk`, `.grid`, `.page` are used identically across implementation and tests. The `2.6fr 0.9fr` value (space-stripped to `2.6fr0.9fr` in tests) is consistent. ✓ diff --git a/docs/superpowers/specs/2026-06-29-dashboard-main-page-polish-design.md b/docs/superpowers/specs/2026-06-29-dashboard-main-page-polish-design.md index 16e4d30..111829f 100644 --- a/docs/superpowers/specs/2026-06-29-dashboard-main-page-polish-design.md +++ b/docs/superpowers/specs/2026-06-29-dashboard-main-page-polish-design.md @@ -56,12 +56,17 @@ plus an accessible `:focus-visible` ring to match `.act`. ### 4. Fix top-heavy emptiness Vertically center the page content when it is shorter than the viewport, while falling back to normal top-aligned scrolling once there are enough services to -fill the screen. Implemented with flex auto-margins (which center but never -clip): - -- The body becomes a flex column filling `min-height:100vh` (banner + page). -- `.page` becomes a flex child with `flex:1` and `display:flex; - flex-direction:column`. +fill the screen. Implemented with a viewport-relative min-height plus flex +auto-margins (which center but never clip): + +- The dashboard is a Dash app: the layout is mounted inside Dash's own wrapper + divs, so a `body { flex }` chain would not reliably reach `.page`. Instead, + size `.page` directly against the viewport. +- `.page` gets `min-height:calc(100vh - 4rem)` (the `4rem` budgets for the top + banner), `box-sizing:border-box` (there is no global reset, and `.page` has + padding), and `display:flex; flex-direction:column`. `vh` is + viewport-relative regardless of ancestor heights, so this needs no changes to + body or the Dash wrappers. - The inner `.grid` gets `margin-block:auto`. Auto margins center the grid vertically when there is spare room; when the grid is taller than the available space the margins collapse to zero and the page scrolls normally — @@ -79,7 +84,8 @@ Extend the existing guard tests in `tests/manager_test/test_ui_redesign.py` - `.lnk` is styled as a bordered button (border + border-radius) and has a `:focus-visible` rule. - `.grid` uses the new `2.6fr 0.9fr` template. -- Vertical-fill rule present (`min-height:100vh` on body, `flex:1` on `.page`). +- Vertical-fill rule present (`.page` has `min-height:calc(100vh - 4rem)` and + `.grid` has `margin-block:auto`). Run locally before pushing: `black --check`, `flake8`, `pylint manager`, and `pytest tests/manager_test/test_ui_redesign.py`. From 28d28f05ca134bd48565f1a107e87509847b9136 Mon Sep 17 00:00:00 2001 From: kavehtoyser Date: Mon, 29 Jun 2026 19:12:15 +0200 Subject: [PATCH 3/6] Dashboard: widen Services, slim sidebar, fill viewport height --- manager/assets/dashboard_styles.css | 11 +++++++++-- tests/manager_test/test_ui_redesign.py | 11 +++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/manager/assets/dashboard_styles.css b/manager/assets/dashboard_styles.css index bf3f0ef..de801ad 100644 --- a/manager/assets/dashboard_styles.css +++ b/manager/assets/dashboard_styles.css @@ -29,8 +29,15 @@ a{color:inherit;text-decoration:none;} .act.danger:hover{color:var(--crit);border-color:var(--crit)} /* ---------- page / grid ---------- */ -.page{max-width:1600px;margin:0 auto;padding:1.8rem 1.6rem 4rem} -.grid{display:grid;grid-template-columns:1.85fr 1fr;gap:1.4rem;align-items:start} +.page{ + max-width:1600px;margin:0 auto;padding:1.8rem 1.6rem 4rem; + box-sizing:border-box;min-height:calc(100vh - 4rem); + display:flex;flex-direction:column; +} +.grid{ + display:grid;grid-template-columns:2.6fr 0.9fr;gap:1.4rem;align-items:start; + margin-block:auto;width:100%; +} /* ---------- panels ---------- */ .panel{ diff --git a/tests/manager_test/test_ui_redesign.py b/tests/manager_test/test_ui_redesign.py index e754f71..be457fc 100644 --- a/tests/manager_test/test_ui_redesign.py +++ b/tests/manager_test/test_ui_redesign.py @@ -165,3 +165,14 @@ def test_logs_toolbar_has_search_and_export(): assert "EXPORT_BASENAME" in html and "FULL_URL" in html assert "a.download" in html # triggers a file download assert "createTextNode" in html # XSS-safe highlight + + +def test_dashboard_grid_widens_services_and_fills_viewport(): + css = (ASSETS / "dashboard_styles.css").read_text().replace(" ", "") + # Services hero is wider than the slim Notifications rail + assert "grid-template-columns:2.6fr0.9fr" in css + assert "grid-template-columns:1.85fr1fr" not in css + # Page fills the viewport height (minus the banner) and centers when short + assert "min-height:calc(100vh-4rem)" in css + assert "margin-block:auto" in css + assert "box-sizing:border-box" in css From bcf1b3a07e2b0fb6172e54fb0f2c0a1fbd9e6f64 Mon Sep 17 00:00:00 2001 From: kavehtoyser Date: Mon, 29 Jun 2026 19:14:49 +0200 Subject: [PATCH 4/6] Dashboard: explain the Notifications panel with a subtitle Co-Authored-By: Claude Opus 4.8 --- manager/assets/dashboard_styles.css | 1 + manager/routers/dashboard_api.py | 13 ++++++++++++- tests/manager_test/test_ui_redesign.py | 9 +++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/manager/assets/dashboard_styles.css b/manager/assets/dashboard_styles.css index de801ad..bb89f38 100644 --- a/manager/assets/dashboard_styles.css +++ b/manager/assets/dashboard_styles.css @@ -49,6 +49,7 @@ a{color:inherit;text-decoration:none;} padding:1rem 1.2rem .85rem;border-bottom:1px solid var(--line-soft); } .panel-head h2{font-size:.82rem;letter-spacing:.04em;font-weight:600;margin:0} +.panel-sub{margin:.25rem 0 0;font-size:11px;line-height:1.4;color:var(--muted)} .count{font-family:var(--mono);font-size:11px;color:var(--faint)} /* ---------- service rows ---------- */ diff --git a/manager/routers/dashboard_api.py b/manager/routers/dashboard_api.py index 67ef13f..21a9b27 100644 --- a/manager/routers/dashboard_api.py +++ b/manager/routers/dashboard_api.py @@ -336,7 +336,18 @@ def get_severity_colors(notifications): children=[ html.Div( className="panel-head", - children=[html.H2("Notifications")], + children=[ + html.Div( + [ + html.H2("Notifications"), + html.P( + "Alerts services raise via the SDK" + " notify() — info, warnings, critical.", + className="panel-sub", + ), + ] + ) + ], ), html.Div( id="notifications-content", diff --git a/tests/manager_test/test_ui_redesign.py b/tests/manager_test/test_ui_redesign.py index be457fc..256d99e 100644 --- a/tests/manager_test/test_ui_redesign.py +++ b/tests/manager_test/test_ui_redesign.py @@ -176,3 +176,12 @@ def test_dashboard_grid_widens_services_and_fills_viewport(): assert "min-height:calc(100vh-4rem)" in css assert "margin-block:auto" in css assert "box-sizing:border-box" in css + + +def test_notifications_panel_has_explanation(): + from manager.routers import dashboard_api + + layout = str(dashboard_api.app.layout) + assert "Alerts services raise" in layout # one-line description of the panel + css = (ASSETS / "dashboard_styles.css").read_text() + assert ".panel-sub" in css From 6f7b9e8bd97a22e14e19b09aa88139fbe82a75b3 Mon Sep 17 00:00:00 2001 From: kavehtoyser Date: Mon, 29 Jun 2026 19:17:56 +0200 Subject: [PATCH 5/6] Dashboard: style LOGS/DOCS as buttons with focus ring Co-Authored-By: Claude Opus 4.8 --- manager/assets/dashboard_styles.css | 10 ++++++---- tests/manager_test/test_ui_redesign.py | 11 +++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/manager/assets/dashboard_styles.css b/manager/assets/dashboard_styles.css index bb89f38..f1722c2 100644 --- a/manager/assets/dashboard_styles.css +++ b/manager/assets/dashboard_styles.css @@ -96,11 +96,13 @@ a{color:inherit;text-decoration:none;} /* ---------- service actions ---------- */ .svc-actions{grid-area:actions;display:flex;gap:.4rem} .lnk{ - font-family:var(--mono);font-size:10.5px;letter-spacing:.08em;text-transform:uppercase; - color:var(--muted);text-decoration:none;border-bottom:1px solid transparent;padding:.1rem 0; - transition:color .15s,border-color .15s; + font-family:var(--mono);font-size:10px;letter-spacing:.1em;text-transform:uppercase; + color:var(--muted);text-decoration:none; + border:1px solid var(--line);border-radius:7px;padding:.34rem .6rem; + transition:color .15s,border-color .15s,background .15s; } -.lnk:hover{color:var(--accent);border-color:var(--accent)} +.lnk:hover{color:var(--accent);border-color:var(--accent-dim);background:rgba(94,230,208,.06)} +.lnk:focus-visible{outline:2px solid var(--accent);outline-offset:2px} /* ---------- notifications ---------- */ .note{ diff --git a/tests/manager_test/test_ui_redesign.py b/tests/manager_test/test_ui_redesign.py index 256d99e..eda4a77 100644 --- a/tests/manager_test/test_ui_redesign.py +++ b/tests/manager_test/test_ui_redesign.py @@ -185,3 +185,14 @@ def test_notifications_panel_has_explanation(): assert "Alerts services raise" in layout # one-line description of the panel css = (ASSETS / "dashboard_styles.css").read_text() assert ".panel-sub" in css + + +def test_service_links_look_like_buttons(): + css = (ASSETS / "dashboard_styles.css").read_text().replace(" ", "") + # .lnk is now a bordered pill, not underline-on-hover text + assert ".lnk{" in css + lnk_block = css.split(".lnk{", 1)[1].split("}", 1)[0] + assert "border:1pxsolid" in lnk_block + assert "border-radius:" in lnk_block + # keyboard focus ring, matching .act + assert ".lnk:focus-visible{" in css From bf7626230252578bd09feeb2084a5f54bf507e2d Mon Sep 17 00:00:00 2001 From: kavehtoyser Date: Mon, 29 Jun 2026 19:21:26 +0200 Subject: [PATCH 6/6] Dashboard: reflow Notifications subtitle to satisfy flake8 (E501) The deeply-nested html.P string exceeded 88 chars; black leaves long string literals alone, so split the literal across one more line. Text unchanged. Co-Authored-By: Claude Opus 4.8 --- manager/routers/dashboard_api.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/manager/routers/dashboard_api.py b/manager/routers/dashboard_api.py index 21a9b27..8897545 100644 --- a/manager/routers/dashboard_api.py +++ b/manager/routers/dashboard_api.py @@ -342,7 +342,8 @@ def get_severity_colors(notifications): html.H2("Notifications"), html.P( "Alerts services raise via the SDK" - " notify() — info, warnings, critical.", + " notify() — info, warnings," + " critical.", className="panel-sub", ), ]