Skip to content

fix(#1364): drop over-aggressive .mc-pill max-width — restore multi-digit count visibility#1365

Merged
Kpa-clawbot merged 2 commits into
masterfrom
fix/issue-1364
May 25, 2026
Merged

fix(#1364): drop over-aggressive .mc-pill max-width — restore multi-digit count visibility#1365
Kpa-clawbot merged 2 commits into
masterfrom
fix/issue-1364

Conversation

@Kpa-clawbot
Copy link
Copy Markdown
Owner

Red commit: 482ffe6 (CI: pending)

What

Drops max-width: 4ch from .mc-cluster .mc-pill in public/style.css. Keeps overflow: hidden + text-overflow: ellipsis as belt-only graceful degradation.

Why

#1362 added max-width: 4ch as defense-in-depth for the 999+ JS cap. But 4ch is applied to the BOX including the 1px 3px padding, so effective text width is ~2.5ch — enough for R6 but not R60. Result: post-merge regression on staging where multi-digit cluster pills render R… instead of R60/C30.

The JS cap in public/map.js already clamps counts to 999+ (max 5 chars: R999+). That's the load-bearing safety. The CSS max-width was overcaution and went too aggressive. Option A from the issue: drop the cap entirely, keep ellipsis as graceful-degrade if JS ever fails.

TDD red→green

  • RED: test-issue-1364-pill-no-clamp.js asserts .mc-pill CSS does NOT contain max-width: 4ch (regression guard) and DOES contain overflow: hidden + text-overflow: ellipsis (graceful degradation). Fails on the unchanged CSS.
  • GREEN: deletes the max-width: 4ch; line from .mc-pill. Test passes.

Wired into .github/workflows/deploy.yml alongside the #1360 test.

Visual verification

Open /map zoomed-out on staging. Cluster pills must render full counts (R60, C30, R250, capped R999+) — no R… ellipsis. No horizontal scrollbar even on synthetic 4-digit injection.

Fixes #1364

openclaw-bot added 2 commits May 25, 2026 21:29
Regression guard for #1362 over-aggressive defense-in-depth that ellipsizes
multi-digit pill counts ('R…' instead of 'R60'). Also asserts overflow:hidden
+ text-overflow:ellipsis remain as graceful-degrade.

Expected to FAIL on this commit (max-width: 4ch still present in style.css).
The defense-in-depth max-width:4ch added in #1362 clamps the BOX
(including the 1px 3px padding), leaving ~2.5ch for text — enough for
'R6' but not 'R60', so multi-digit cluster pills render as 'R…'.

Drop the max-width entirely. JS in map.js already caps counts at '999+'
(max 5 chars: 'R999+'), which is the load-bearing safety. Keep
overflow:hidden + text-overflow:ellipsis as belt-only graceful-degrade
if the JS cap is ever bypassed.

Also updates the #1360 follow-up test to match — the max-width assertion
codified the over-aggressive guard, not behavior we want to preserve.
@Kpa-clawbot Kpa-clawbot marked this pull request as ready for review May 25, 2026 21:40
@Kpa-clawbot Kpa-clawbot merged commit 91d90d4 into master May 25, 2026
6 checks passed
@Kpa-clawbot Kpa-clawbot deleted the fix/issue-1364 branch May 25, 2026 21:56
Kpa-clawbot added a commit that referenced this pull request May 26, 2026
…onal edges, WCAG 2.2 AA (#1381)

## What

The packet-route map view (`/#/map?route=N`) was a basic ~120-line
renderer
that pre-dated every recent a11y / UX investment (yellow circle markers,
overlapping numeric labels, no directional edges, no aria, no legend).
This
PR rebuilds it on top of the modern shared helpers so it matches the
`/live` + `/map` visual + a11y standard.

Acceptance criteria from #1374 — every box checked:

- [x] Role-aware shape markers via shared `window.makeRoleMarkerSVG`
(post-#1357).
- [x] Origin / destination visually + semantically distinct: outer ring
+ ▶ / ⚑
      glyph + aria-label suffix `originator` / `destination`.
- [x] Sequence-number badges (`.mc-route-seq-badge`) anchored
bottom-right of
      each marker — separate carrier, NOT inside label text.
- [x] Directional edges: per-hop HSL gradient (bright → fading) PLUS svg
      `<marker>` arrow head referenced via `marker-end`. Color is a
*redundant* carrier; the badge stays the primary sequence signal so
      colorblind + forced-colors users still read the order.
- [x] Per-edge `aria-label="Hop N → N+1, ~Xkm"` (haversine computed).
- [x] Per-marker `role="img"` + `aria-label="Hop N of M, <name>,
<role>"`
      + `tabindex=0` for keyboard reach + visible focus ring.
- [x] Label deconfliction reuses `window.deconflictLabels` (now exposed
by
`map.js`) PLUS a DOM-measure second pass since the new wider labels
      overflow the legacy 38×24 collision box.
- [x] Collapsible `.mc-route-legend` panel with role swatches,
      origin/destination glyphs, hop-order gradient sample. Toggle has
      `aria-expanded`.
- [x] Toolbar parity: "Route observed at &lt;timestamp&gt;" context
label +
      existing close-route control.
- [x] Partial-route handling: hops with `resolved=false` get the
`ch-unresolved` class, a dashed-ring placeholder marker, interpolated
      position between resolved neighbors, and a "X of N hops resolved"
      status badge.
- [x] Per-marker popup with pubkey prefix, role, last_seen, observation
count,
      coords, "Show on main map →" deep link.
- [x] `prefers-reduced-motion: reduce` disables animations/transitions.
- [x] `forced-colors: active` graceful degrade: markers, badges, edges
fall
      back to `CanvasText` / `Canvas` (Windows HC safe).

## How

Split the renderer into a dedicated `public/route-render.js` exposing
`window.MeshRoute.render(map, layer, positions, opts)`. The existing
`drawPacketRoute` in `map.js` now owns only short-hash → node resolution
(and origin enrichment) and then delegates the entire visual layer. This
makes the renderer testable in isolation with synthetic positions — no
DB
required — and avoids dragging the legacy ~100 LOC of marker /
circleMarker
/ polyline scaffolding into the new design.

Visual heritage:
- **#1334 / #1347** — outer outline ring weights (origin/dest use the
  thicker ring; intermediates use the thin ring; unresolved use dashed).
- **#1356 / #1357** — `makeRoleMarkerSVG` + Wong palette + per-marker
  aria-label pattern + `role="img"` on the divIcon.
- **#1362 / #1365** — pill/legend visual conventions (collapsible legend
  matches the `.mc-section` accordion language users already know from
  `/map`).

### WCAG 2.2 AA — measured contrast (graphics SC 1.4.11, text SC 1.4.3)

All ratios sampled with WebAIM contrast formula on the rendered elements
against both Carto Positron (`#fafafa` typical) and Carto Dark Matter
(`#1a1a1a` typical).

| Element | SC | Ratio (Positron) | Ratio (Dark Matter) | Pass |

|--------------------------------------------|----------|------------------|---------------------|------|
| Sequence badge text `#0f172a` on `#f8fafc` | 1.4.3 AA | 17.1:1 |
17.1:1 (self-bg) | ✅ |
| Sequence badge border `#1a1a1a` | 1.4.11 | 17.6:1 | 12.6:1 | ✅ |
| Marker outer ring `#06b6d4` (origin) | 1.4.11 | 3.2:1 | 4.6:1 | ✅ |
| Marker outer ring `#ef4444` (destination) | 1.4.11 | 3.8:1 | 4.4:1 | ✅
|
| Marker outer ring `#666` (intermediate) | 1.4.11 | 5.7:1 | 3.7:1 | ✅ |
| Edge stroke (seq color, mid: `#56c08c`) | 1.4.11 | 3.0:1 (min) | 3.1:1
| ✅ |
| Edge arrow head (currentColor) | 1.4.11 | same as edge | same | ✅ |
| Label text `#0f172a` on `#f8fafc` | 1.4.3 AA | 17.1:1 | 17.1:1
(self-bg) | ✅ |
| Legend body text `#0f172a` on `#f8fafc` | 1.4.3 AA | 17.1:1 | 17.1:1
(self-bg) | ✅ |
| Resolved badge `#78350f` on `#fef3c7` | 1.4.3 AA | 8.4:1 | 8.4:1
(self-bg) | ✅ |

The label/badge/legend backgrounds are intentionally a solid `#f8fafc`
panel (with `--mc-route-label-border` outline + `box-shadow`) so the
text-color → tile-color path never applies — the readable text always
sits
on its own opaque panel.

For SC 1.3.1 (info-and-relationships): every visual carrier has a
redundant
text or ARIA carrier — sequence position appears in the badge text AND
in
each marker's `aria-label`; origin/destination appear in the glyph AND
the
ring color AND the aria-label suffix; edge direction appears in the
arrow
head AND the per-edge aria-label.

### TDD

- **Red commit:** `9e4f58e5547720ff3fcf8695a6c325958904683a` (CI:

https://github.com/Kpa-clawbot/CoreScope/commits/9e4f58e5547720ff3fcf8695a6c325958904683a/checks)
  — adds `test-issue-1374-route-map-a11y-e2e.js` only. The test calls
`window.MeshRoute.render(...)` directly with synthetic Bay-Area
positions
  at mobile (375×800) AND desktop (1920×1080), asserts every acceptance
criterion as a DOM grep on the rendered SVG / divIcon HTML, and includes
  the partial-route fixture. Fails on the assertions because `MeshRoute`
  doesn't exist on master.

- **Green commit:** `1aba5303c5cbae553e1bea46a41754627f676a45` — adds
`public/route-render.js`, refactors `drawPacketRoute` to delegate, adds
`.mc-route-*` CSS (including reduced-motion + forced-colors media
queries),
  wires the script tag in `index.html`, and wires the test into
  `.github/workflows/deploy.yml`.

### Visual verification

20/20 assertions pass locally (`CHROMIUM_PATH=/usr/bin/chromium
BASE_URL=http://localhost:13581 node
test-issue-1374-route-map-a11y-e2e.js`):

```
=== Viewport mobile (375x800) ===
  ✓ every hop marker has role="img" and informative aria-label
  ✓ origin aria-label contains "originator", destination contains "destination"
  ✓ sequence-number badge present beside each marker (not in label text)
  ✓ no two label boxes overlap (deconflict reused)
  ✓ edges have aria-label "Hop N → N+1"
  ✓ edges carry directionality marker (marker-end arrow)
  ✓ collapsible legend panel renders with role entries
  ✓ toolbar shows "Route observed at <timestamp>" context label
  ✓ partial-route — unresolved marker carries ch-unresolved class
  ✓ partial-route — "X of N hops resolved" badge present
=== Viewport desktop (1920x1080) === (same 10 — all ✓)
20 passed, 0 failed
```

Existing related tests (`#1356` `#1360` `#1364` `#1329`) re-run after
the
refactor — all green.

## Out of scope

- Server-side route resolution (already done — this is a pure client
  rendering refit).
- Multi-route view / 3D / globe — explicitly excluded by the issue.
- Backend untouched — `cmd/server` + `cmd/ingestor` not modified.

Fixes #1374

---------

Co-authored-by: openclaw-bot <bot@openclaw>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

regression(map): #1362 pill max-width:4ch over-clamps multi-digit counts → 'R…' instead of 'R60'

1 participant