Skip to content

fix(css): raise the remaining 44 px controls to the 48 px touch-target minimum (#235) - #239

Merged
dborup merged 6 commits into
masterfrom
codex/issue-235-touch-targets-48
Oct 5, 2026
Merged

dborup merged 6 commits into
masterfrom
codex/issue-235-touch-targets-48

Conversation

@dborup-agent

@dborup-agent dborup-agent commented Oct 5, 2026 •

Copy link
Copy Markdown
Collaborator

Relates to #235

Every control in public/ that was still sized at 44 px now uses the 48 px house minimum from Kpa-clawbot#2052 (as .fav-star did in #229). Tests first: two test files go red on master, then the fix turns them green.

  • Production code: CSS only, plus one line in public/live.js, where an inline style="… min-height:44px …" moves to a live.css rule. No colours added; only sizes, one order and the removal of one max-height.
  • No npm dependencies added. Fork guards unchanged: 9 in deploy.yml, 1 in release-fast-path.yml; .github/ is not in the diff. scripts/check-xss-sinks.sh --diff is clean.

The list from the issue, checked against master aff158c7

Control On master Now
.theme-toggle 44 (46×44 rendered in the desktop nav) 48×48
.modal > .modal-close, .ch-modal-close (≤640 px) 44 48
.ch-back-btn 44, but no markup renders it since Kpa-clawbot#1367 48 (kept consistent; dead rule, see below)
.ch-back, the channel header's real back button 40 48
.ch-avatar.ch-tappable (≤640 px) 44 48
.suggest-claim 44 48
.detail-back-btn, .filter-toggle-btn (pointer: coarse) 44 48

Other 44 px controls found

Control Where Now
The rest of the Kpa-clawbot#630 pointer: coarse block: .filter-bar .btn, .filter-group .btn, .tab-btn, .region-pill, .region-dropdown-trigger, .multi-select-trigger, .node-count-pill, .analytics-time-range button, and the height of .filter-bar input/select style.css 48
.leaflet-control-zoom a style.css, every map 48
.live-leaflet-toggle a live.css 48
live region dropdown ::after tap pad live.css 48
.live-node-filter-hitarea inline style in live.js 48, as a live.css rule (.live-toggles .live-node-filter-hitarea, see round 2)
.ch-proposals-toolbar button, .ch-proposals-actions button channel-proposals.css 48

Left at 44 on purpose: .feed-show-btn and .legend-toggle-btn in live.css's @media (max-width: 640px) block. The same block hides both with display: none !important, so the 44 px rule never renders. Also left alone: the 44 px values that are not controls (.live-node-detail .panel-header min-height, the Kpa-clawbot#1234 live header cap, a 44 px column in analytics).

The channel sidebar header at 390 px

At 390 px the mobile sidebar header holds the title, the region pills and + Add. Master caps it with max-height: 56px (Kpa-clawbot#1224), and that cap was already hiding an overflow: the channel list was drawn over the lower half of the 44 px pills. With 48 px pills, + Add wrapped to a third row under the list and could not be tapped. The new mobile E2E step caught this.

The cap is removed, and the pills get order: 1: title and + Add share the first row and the pills take the second. The header is 115 px (was 56 px clipped). test-issue-1224-channels-mobile-ux-e2e.js used to assert ≤60 px, which only the cap kept true. It now asserts that the header holds all its controls, that the list starts below it, and that it is ≤120 px, at 375 px and, new, at 390×844 with touch.

This is a design trade-off. The alternative was to keep a one-row header by putting the channel region filter into dropdown mode (RegionFilter.init(el, { dropdown: true }), as packets and live already do). That keeps the header at ≤60 px, but it changes the desktop sidebar too and needs three channel E2Es that click the pills (152, 152-decrypt, 154-155) reworked. If you prefer that, say so.

Screenshots (before = master, after = this branch, 390×844 touch unless noted): https://github.com/dborup/CoreScope/tree/codex/issue-235-screenshots, an evidence-only branch that is not meant to be merged.

before after
channel list header

Tests

  • test-touch-targets.js (CI E2E step, no server) lists every control above, 24 more assertions. Two harness fixes were needed to measure them at all:
    • It now has a viewport meta. Without one, the iPhone 13 context laid the page out at 980 px, so no max-width rule ever applied in this test.
    • It now loads every local stylesheet in index.html order, not only style.css. home.css restyles .suggest-claim, and live.css and channel-proposals.css hold controls of their own.
    • It also asserts that the context matches (pointer: coarse) and (max-width: 640px), and checks the ::after tap pad's box.
  • test-issue-2052-touch-target-e2e.js, mobile 390×844 touch, in the real DOM:
    • Every visible tap target in the channel sidebar is now ≥48 (was ≥44; .region-pill was the 44 px exception).
    • New: the open channel's .ch-back is ≥48, unclipped and hit at its centre.
    • New: every sender avatar is ≥48 and the message list does not overflow.
    • New: the add-channel dialog's close button is ≥48 and hit at its centre.
    • New: no horizontal page overflow in any of these states.
  • test-issue-1224-channels-mobile-ux-e2e.js: the header check described above.

On master: the harness fails 24 assertions, the Kpa-clawbot#2052 E2E fails 4 steps, and the Kpa-clawbot#1224 header check fails.

Round 2: review feedback

This round merges origin/master (9989b97f) with a merge commit, then adds 8a8c72ab (tests, red before the fix) and 23778ebd (fix).

  • F1: the region menu stays on-screen. At ≤640 px with more than 4 regions, RegionFilter renders a dropdown, and order: 1 puts its trigger at the right edge of the strip. Its menu was anchored left: 0, so at 390 px it ran from 302 to 522 px. It now opens leftwards from the trigger's right edge: .ch-header-region .region-dropdown-menu { left: auto; right: 0; }, in the same ≤640px block. At 390 px it spans 160–380 px. Desktop is unchanged.
  • F2: the hit-area cursor. The rule moved out of the inline style is now .live-toggles .live-node-filter-hitarea (specificity 0,2,0). It wins over .live-toggles label (0,1,1, cursor: pointer), so the label keeps cursor: text as before fix(css): raise the remaining 44 px controls to the 48 px touch-target minimum #235.
  • F3: centred pill labels. .region-pill gets justify-content: center. Only a pill wider than its label changes, which means the 48 px touch minimum; desktop pills are content-sized.
  • The add-channel dialog's 32 px buttons are tracked in fix(ui): add-channel dialog buttons are 32 px tall on touch (below the 48 px minimum) #249 and are not changed here.

Tests:

  • test-issue-1224-channels-mobile-ux-e2e.js mocks 6 regions through /api/config/regions; the fixture has 4, so it never shows the dropdown. At 320, 360, 390, 430 and 640 px touch, the step checks that the header strip holds its controls, and that the open menu is inside the viewport with all 7 options hit at their centre and no page overflow. Before the fix, 5 steps fail.
  • test-touch-targets.js measures the hit area in its real .live-toggles / .live-node-filter-wrap context and checks cursor: text (F2). It also checks that a 48 px .region-pill centres its label within 1 px (F3).

Perf

There is no hot path: static CSS values only, no JS logic.

🤖 Generated with Claude Code

dborup and others added 3 commits October 5, 2026 06:58
)

test-touch-targets.js now loads every local stylesheet in index.html
order and has a viewport meta, so the iPhone harness really is 390 px
wide and the max-width: 640px rules apply. It lists the controls from
#235 and the other 44 px controls found on master (the coarse-pointer
Kpa-clawbot#630 block, Leaflet zoom and live toggles, the channel-proposals
buttons, the live node-filter hit area and region tap pad). The channel
header's back button is .ch-back (40 px); .ch-back-btn has no markup.

test-issue-2052-touch-target-e2e.js raises the mobile sidebar check to
48 and measures, at 390x844, the open channel's back button, every
sender avatar and the add-channel dialog's close button, with no
horizontal overflow.

Both fail on master: 24 harness assertions, 4 E2E steps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t minimum (#235)

Every control that was still sized at 44 px now uses the 48 px house
minimum from Kpa-clawbot#2052:

- .theme-toggle; .modal-close / .ch-modal-close and .ch-avatar.ch-tappable
  (max-width: 640px); .suggest-claim; .ch-back-btn (no markup renders it).
- The channel header's back button, .ch-back, which was 40 px.
- The Kpa-clawbot#630 coarse-pointer block: .filter-bar/.filter-group .btn, .tab-btn,
  .region-pill, .region-dropdown-trigger, .multi-select-trigger,
  .node-count-pill, .analytics-time-range button, .detail-back-btn,
  .filter-toggle-btn, and the .filter-bar input/select height.
- Leaflet zoom buttons and the live page's map toggles.
- The live region dropdown's ::after tap pad and the node-filter hit area,
  whose inline style in live.js moves to a live.css rule.
- The channel-proposals toolbar and action buttons.

At 390 px the region pills no longer fit beside the channel title and
+ Add. The mobile sidebar header kept max-height: 56px, which on master
already let the list cover the lower half of the 44 px pills; with 48 px
pills + Add wrapped under the list and could not be tapped. The cap goes,
title and + Add share the first row and the pills take the second
(115 px). test-issue-1224-channels-mobile-ux-e2e.js now checks that the
header holds its controls and the list starts below it (<=120 px)
instead of the <=60 px bound that only the cap kept true.

.feed-show-btn and .legend-toggle-btn keep their 44 px rule: live.css
hides both at <=640px, the only width where it applies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…235)

The header check ran only at 375 px without touch, where the region
pills are 24 px, so dropping the rule that puts the pills on their own
row (header grows to three rows, 139 px) passed. The check is now a
helper and also runs at 390x844 with touch, where the pills and + Add
are 48 px; that mutant now fails there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dborup-agent

Copy link
Copy Markdown
Collaborator Author

Rapport — CS-pve-agent3 PR#239 #235 — head bd56fec

Status: Draft, all acceptance criteria met and tested; CI green on attempt 2 (attempt 1 hit a pre-existing, time-dependent test-issue-1122 failure that reproduces on master). One design choice (the 390 px channel header) is open for review.

Evidence marks: [T] a test run here (command and result below), [A] ad-hoc measurement or screenshot taken here with Playwright, [K] known from elsewhere, not re-run here.

Local setup, as in CI: a Go server built from this branch, on a copy of test-fixtures/e2e-fixture.db that was freshened, given the Kpa-clawbot#1486/Kpa-clawbot#1791 seed, migrated with corescope-migrate, then given the Kpa-clawbot#2073 and #199 seeds. It served plain public/. A second server served master aff158c7's public/ for before/after. Servers were stopped by port.

Acceptance criteria

# Criterion Evidence Test Mutant (set back, test must fail)
1 Every listed control measures ≥48×48 at mobile width [T] test-touch-targets.js (iPhone 13, 390 px, coarse pointer): OK, 59 checks, 24 of them new; master: 24 fail. [T] test-issue-2052-touch-target-e2e.js at 390×844 touch in the real DOM: 14/14; master: 10/14 (sidebar pills 44, .ch-back 40, avatars 44, dialog close 44) both files are in the CI E2E step [T] 11 control mutants, all killed (harness; the four channel ones also by the Kpa-clawbot#2052 E2E): .theme-toggle, .modal-close/.ch-modal-close, .ch-back → 40, .ch-avatar.ch-tappable, .suggest-claim, the Kpa-clawbot#630 coarse block, Leaflet zoom, live toggles, live ::after pad, live node-filter hit area, channel-proposals buttons
2 No horizontal overflow at 390 px and on desktop where the controls are [T] the Kpa-clawbot#2052 E2E asserts scrollWidth - clientWidth ≤ 0 for the channel list, the open channel with messages, and the add-channel dialog at 390, and at 1440 on desktop. [A] probe at 390 touch, 768 touch and 1440: home + nav, suggest dialog, channel list/header/messages, add-channel dialog, node detail, nodes list, packets, analytics, map, live. Overflow is 0 everywhere, on branch and on master Kpa-clawbot#2052 E2E, Kpa-clawbot#1224 E2E see row 3
3 Channel sidebar header at 390 px holds its controls (found during this work) [T] test-issue-1224-channels-mobile-ux-e2e.js 6/6; master: 4/6 ("+ Add" sticks out of the 56 px header at 375 px; pills and "+ Add" at 390 touch) Kpa-clawbot#1224 E2E, now also at 390×844 touch [T] max-height: 56px restored: fails at both widths. [T] order: 1 removed: header 139 px, fails at 390 touch
4 Screenshots of the tight spots [A] 13 before/after pairs on the evidence-only branch codex/issue-235-screenshots (28e5aba5) — —
5 Fork guards unchanged [T] grep -c "== 'Kpa-clawbot/CoreScope'": 9 in deploy.yml, 1 in release-fast-path.yml; .github/ not in the diff — —
6 XSS preflight [T] bash scripts/check-xss-sinks.sh --diff: exit 0, no findings (it scans public/live.js) — —
7 sh test-all.sh [T] 217/217, exit 0, on bd56fecb — —
8 CI E2E files locally [T] all 133 node commands of the "Run Playwright E2E tests" step, one by one: exit 0. Five (test-observer-iata-1188, test-gestures-1062, test-touch-gestures-coverage, test-issue-1692-packets-init-parallel, test-issue-1122-details-row-clamp) first failed on a fixture that had aged during the long local run: the first four by falling outside the packets page's 15-minute window, Kpa-clawbot#1122 by the time-dependent geometry described under CI. All five passed once the fixture was freshened, as CI does right before the step — —

One mutant survived, as expected: height: 44px on .filter-bar input/select in the coarse block is equivalent. The same block sets min-height: 48px on them, so they still render 48 px.

.feed-show-btn / .legend-toggle-btn were not mutated. Their 44 px rule sits in a ≤640px block that also hides both (display: none !important), so it never renders. They are left unchanged and documented in the PR.

The order: 1 mutant first survived, because the Kpa-clawbot#1224 header check ran only at 375 px without touch. Commit bd56fecb added the 390×844 touch run, which kills it.

Selected measurements [A] (master → branch):

Spot 390 touch 1440 desktop
nav .theme-toggle in the bottom-nav "More" sheet (mirror) 46×44 → 48×48; nav stays 52 px, no overflow
channel sidebar header 56 px (content spilling under the list) → 115 px, two rows 144 px → 144 px (unchanged)
.ch-back / avatars / dialog close 40 / 44 / 44 → 48 / 48 / 48 not shown / 36 / 32×29 (desktop sizes unchanged)
suggest dialog .suggest-claim 59.7×44 → 59.7×48; rows +4 px 64.7×44 → 64.7×48
node detail .detail-back-btn 44 → 48 42×28 (unchanged, no coarse pointer)
Leaflet zoom, live toggles 44 → 48 44 → 48

CI (run 37276734145, head bd56fecb)

Job Attempt 1 Attempt 2 (failed jobs re-run)
Go Build & Test success (not re-run) success
Playwright E2E Tests failure: test-issue-1122-details-row-clamp-e2e.js, [desktop-1200] and [tablet-900] "advert link in Details is not hit-testable" (R5-D4 300D Rak) success. In the log: test-touch-targets.js: OK, test-issue-2052-touch-target-e2e.js: 14 passed, 0 failed, Kpa-clawbot#1224 6/6 including the new 390×844 touch step, test-bottom-nav-1061-e2e.js: 31 passed, Kpa-clawbot#1122 passed 18 failed 0
Build & Publish Docker Image skipped (E2E failed) success
Release Artifacts skipped skipped
Deploy Staging / Publish Badges & Summary skipped (fork guard) skipped (fork guard)

The attempt-1 failure is not from this PR [T]:

  • It reproduces locally on master's public/ as well as on this branch, once the freshened fixture is about 27 minutes old: 2/2 runs fail on each, with the same message.
  • On a fresh fixture both pass 3/3.
  • The measured geometry shows the link's box growing to about 52 px tall (it wraps), so the probe point at r.top + r.height / 2 lands on the td or the clip, not the link.
  • The unrelated codex/issue-96-hide-control-packets run failed on the same assertion at 07:46 UTC [A].
  • This PR changes nothing on the packets table at desktop width. Its desktop-visible changes are the nav theme toggle, map zoom and live toggles.

Worth its own issue.

Residuals

  • Design choice to review: the 390 px channel header grows from 56 px (clipped) to 115 px. The alternative is the region dropdown in the channel header, which keeps one row but changes desktop and needs three channel E2Es reworked. Details are in the PR description.
  • .ch-back-btn has no markup since regression(channels mobile): #1227 picker rows too cramped — restore prod's roomier layout (keep header wins) Kpa-clawbot/CoreScope#1367. It is raised to 48 for consistency, but it could be deleted in a cleanup PR, together with the dead .feed-show-btn/.legend-toggle-btn 44 px rule in live.css.
  • Not in scope, all pre-existing and identical on master [A]:
  • The generic .modal > .modal-close is checked in the harness only. No observer in the E2E fixture has a neighbour sparkline, so the real dialog cannot be opened there.
  • test-issue-1122-details-row-clamp-e2e.js is time-dependent (see CI above); it needs its own issue.
  • Workflow note: the harness also runs live.css, home.css and the other local sheets now. A future sheet that changes a listed control will show up there, as intended.

@adminopenclaw8-sketch

Copy link
Copy Markdown
Collaborator

Review — CS-Macmini PR#239 touch-targets-48 — head bd56fec

Dom: APPROVE med nits

F1 is a one-line CSS fix and should go in before merge.

Independent, read-only review. Evidence marks: [T] a test run here, [A] an ad-hoc measurement or screenshot taken here with Playwright, [K] known from elsewhere, not re-run here.

Setup [T]: git archive of head bd56fecb and of the merged tree git merge-tree --write-tree origin/master bd56fecb… (master 3878d7ea, tree 8ca8adca). The server, migrate tool and ingestor were built from the merged tree. A copy of e2e-fixture.db was prepared as CI does it: freshen, the Kpa-clawbot#1486/Kpa-clawbot#1791 seed, corescope-migrate, then the Kpa-clawbot#2073 and #199 seeds. One server served the merged public/ and a second one served master 3878d7ea's public/ on the same DB, for before/after. Both were stopped by port. The head was bd56fecb on git ls-remote before and after the review. Master has since moved to 2a7877ac (#231, #238: channel-proposal JS only, no layout), and the merge with that is clean too; see Tests.

Findings

# Severity Finding Evidence Suggested fix
F1 Medium At ≤640 px with more than 4 regions, RegionFilter renders a dropdown, not pills. order: 1 moves its trigger to the right edge of the header, after + Add. The menu is anchored left: 0 and is at least 220 px wide, so it now runs about 129 px past the right edge at 390 px. On master the overshoot is about 63 px. The option labels are cut ("MRY - MF…"), and the centre of every option lies off-screen. [A] 6 regions mocked through /api/config/regions, 390×844 touch: menu items hit at their centre 0/7 on branch, 7/7 on master; same at 320, 360, 430 and 640. Checkboxes and codes stay visible, and a Playwright click on an item still works. Add .ch-header-region .region-dropdown-menu { left: auto; right: 0; } in the same ≤640px block. [A] With it: menu fully on-screen, 7/7 hit at 320–640 px. It also fixes master's existing partial clip. Optionally add a Kpa-clawbot#1224 step with 6 mocked regions, because the E2E fixture (4 regions) never renders the dropdown.
F2 Low Moving the inline style of .live-node-filter-hitarea into live.css changes more than size. .live-toggles label (specificity 0,1,1, cursor: pointer) beats the new .live-node-filter-hitarea rule (0,1,0), so the label's padding area now shows a pointer cursor instead of a text cursor. The rule's own cursor: text and display: inline-flex never apply. Display renders the same anyway, because the flex parent blockifies it. [A] computed cursor: master text, branch pointer, at 390 touch and 1440. Height is 48 as intended. Use .live-toggles .live-node-filter-hitarea { … }. Mutant M9 below also shows that restoring the old inline min-height:44px in live.js is not caught, because the harness only sees CSS. A real-DOM height check in test-1110-live-filter.js would close that gap (optional).
F3 Nit .region-pill is inline-flex without justify-content: center. In the new 48 px pills the label hugs the left edge ("All", "MRY"). This already happened at 44 px, but the second row makes it more visible. [A] screenshot at 390 touch. Optional: justify-content: center on the coarse-pointer pills.
F4 Follow-up The primary buttons in the add-channel dialog are 32 px tall on touch: #chGenerateBtn 181×32, #chPskAddBtn 67×32, #chHashtagBtn 91×32. They were never 44 px, so they are outside #235. [A] 390×844 touch, same on master. A separate issue.

Everything else I checked holds up; see below.

1. Every #235 control measures ≥48×48

Real DOM, master → branch [A]:

Control 360 / 390 touch 768 touch 1440 desktop
.theme-toggle (desktop nav) hidden; the mobile mirror .bottom-nav-sheet-darkbtn is 175×48 on both hidden 46×44 → 48×48, nav stays 52 px
.ch-modal-close (#chModalClose) 44×44 → 48×48, hit at centre 32×29 (unchanged, ≤640px rule only) 32×29 (unchanged)
.ch-back (the real back button) 40×40 → 48×48 not rendered not rendered
.ch-back-btn no markup since Kpa-clawbot#1367 (grep confirms); the rule is dead and is raised for consistency — —
.ch-avatar.ch-tappable 44 → 48×48 (35 of 35 in #test) 36 (unchanged) 36 (unchanged)
.suggest-claim 61.5×44 → 61.5×48; rows 65 → 69 px 62.8×44 → 62.8×48 66.1×44 → 66.1×48
.detail-back-btn (#nodeBackBtn) 44 → 48×48 44 → 48 42×28 (unchanged, fine pointer)
.filter-toggle-btn the visible control is the navbar mirror, 65.7×48 on both 76.7×44 → 76.7×48 76.7×34 (unchanged)
.region-pill (channel header) 44 → 48×48 44 → 48 24 (unchanged)
region dropdown trigger (6 regions) 44 → 48 tall — —
.leaflet-control-zoom a (map, live) 44 → 48×48 44 → 48 44 → 48
.live-leaflet-toggle a 44 → 48×48 44 → 48 44 → 48
.live-node-filter-hitarea 140×44 → 140×48 — 142×44 → 142×48

My own grep [A] of public/*.css and public/*.js for 4[0-7]px on sizing properties leaves only what the PR already explains:

2. Layout at 390 px and on desktop

  • [A] Horizontal page overflow is 0 on master and branch, at 320, 360 and 390 touch, 768 touch and 1440, on:
    • home and suggest;
    • the channel list, the open channel with messages and the add-channel dialog;
    • node detail, packets, map and live.
  • [A] I found no clipped text in #homeSuggest, #chHeader or #chMessages (I checked leaf elements with hidden overflow and scrollWidth > clientWidth).
  • [A] The open-channel header grows from 69 to 77 px at 390. The back button is hit at its centre (the style.css declares .nav-btn and .ch-icon-btn at both 48px and 44px Kpa-clawbot/CoreScope#2052 E2E also asserts this).
  • [A] Desktop: the channel sidebar header is 144 px on both, unchanged. The nav theme toggle fits; the nav stays 52 px.
  • Screenshots were taken locally at 390×844 for every state above, master and branch. They are not uploaded, because this review is read-only (no pushes). The author's evidence branch 28e5aba5 shows the same before/after for the channel header.

Channel header, my assessment: I agree with the design choice (pills mode, 2–4 regions), with F1 fixed for the dropdown mode.

Regions → 1 2 4 (fixture) 6 (dropdown)
master, 390 touch 56 px 56 px 56 px, 6 controls stick out, the list covers the pills 56 px, trigger 44 tall
branch, 390 touch 61 px, 1 row 61 px, 1 row 115 px, 2 rows, nothing outside, list starts below 61 px, 1 row (see F1)
master, 320/360 touch 56 56, + Add outside 56, + Add on a 3rd row under the list: a Playwright click on it timed out at 360 56
branch, 320/360 touch 61 115 115 61

[A] from route-mocked /api/config/regions.

Why I agree:

  • Two rows are unavoidable in pills mode. At 48 px, title + Region label + 5 pills + + Add need about 470 px; the strip has 370 at 390.
  • The row split is the conventional mobile one. Row 1 holds the title and the primary action, row 2 the filter.
  • The cost is small and only paid when needed. order: 1 only costs a row when the pills do not fit: with 1 or 2 regions at ≥390 px, or in dropdown mode, the header stays one row (61 px). 115 px is 14 % of an 844 px viewport, and the list still shows 5 full rows.
  • Master is broken here, not compact: the 56 px cap hid the overflow, and at ≤360 px + Add could not be reached.

The alternative, the region dropdown on mobile only, would save about 54 px in the 2–4-region case. But it needs breakpoint-dependent JS (init with { dropdown } plus a re-render on resize), and it hides the current selection behind a tap. It would also inherit F1. I would not do it.

Simpler suggestion: keep this PR's CSS, add the one-line menu anchor from F1, and optionally F3.

3. Tests and mutants

[T] Each listed control is asserted in test-touch-targets.js (59 checks, OK). The channel controls are also asserted in the real DOM by test-issue-2052-touch-target-e2e.js (14/14) and test-issue-1224-channels-mobile-ux-e2e.js (6/6).

Mutants, each a copy of the merged tree served by its own server [T]:

# Mutant Killed by
M1 .theme-toggle back to 44 harness ✗
M2 .ch-avatar.ch-tappable back to 44 harness ✗, Kpa-clawbot#2052 ✗ ("35 of 35 avatars below 48x48")
M3 ≤640px .modal-close / .ch-modal-close back to 44 harness ✗ (both entries), Kpa-clawbot#2052 ✗
M4 .ch-back back to 40 harness ✗, Kpa-clawbot#2052 ✗ (2 steps)
M5 .suggest-claim back to 44 harness ✗
M6 #630 coarse block min-height/width back to 44 harness ✗ (.detail-back-btn, .filter-toggle-btn)
M7 order: 1 removed Kpa-clawbot#1224 ✗ (header 139 px at 390 touch); Kpa-clawbot#2052 passes
M8 max-height: 56px restored Kpa-clawbot#1224 ✗ (both widths); Kpa-clawbot#2052 passes
M10 Leaflet zoom back to 44 harness ✗
M11 .live-node-filter-hitarea CSS back to 44 harness ✗
M9 old inline style="…min-height:44px…" restored in live.js survives (see F2; the harness cannot see markup)

10 of 11 killed.

4. Colours, tokens, behaviour, XSS

  • [T] There are no colours in the added lines: the only #… hits are issue numbers in comments. The diff changes sizes, one order, and removes one max-height; no new tokens are needed.
  • [A] Behaviour beyond size changed in one place only: the cursor in F2.
  • [T] scripts/check-xss-sinks.sh --diff exits 0 against both 3878d7ea and aff158c7.
  • [T] There are no config values, so there is nothing for the customizer.

5. Rules

  • [T] Fork guards == 'Kpa-clawbot/CoreScope': 9 in deploy.yml and 1 in release-fast-path.yml. .github/ is not in the diff.
  • [T] No closing keywords: closingIssuesReferences is empty, the body says "Relates to fix(css): raise the remaining 44 px controls to the 48 px touch-target minimum #235", and the commit messages have none.
  • [T] All three commits (8ab854fb, cd1a70e7, bd56fecb) have author and committer dborup <kontakt@meshview.dk>.
  • [A] CI on run 37276734145: Go Build & Test and Playwright E2E pass. Deploy and badges are skipped by the fork guard.

Tests run

  • [T] sh test-all.sh: 217/217, exit 0, and node test-frontend-helpers.js: 707/0, both on the merged tree with master 3878d7ea (8ca8adca) and again with master 2a7877ac (da89ac56). The harness is OK on both.

  • [T] Against the merged-tree server, all exit 0:

    Group Results
    Touch targets test-touch-targets.js OK; test-issue-2052-touch-target-e2e.js 14/0; test-issue-1224-channels-mobile-ux-e2e.js 6/6
    Nav test-bottom-nav-1061-e2e.js 31/0; nav 1055 20/20, 1102 5/5, 1413 6/0, 1400 4/0
    Channels 1367 9/9; test-channel-fluid-e2e.js 15/0; list-render 8/0, selection-flow 8/0, add-modal 8/0; 1087 3/0; 1111 2/0; 152 6/0; 154-155 8/0; test-channel-modal-ux.js 33/0
    Own server and ingestor test-channel-proposals-e2e.js 23/0; 152-decrypt 18/0
    Map test-map-modal-fluid-e2e.js 10/0
  • [T] The full "Run Playwright E2E tests" list from deploy.yml, all 133 commands, against plain public/ (not instrumented): 128 exit 0. The other 5 fail the same way on master's public/:

    • test-issue-1281-location-row-e2e.js, test-analytics-fluid-charts.js and test-e2e-1267-mobile-vcr.js hardcode /usr/bin/chromium. With CHROMIUM_PATH all three pass on master and on branch.
    • test-e2e-playwright.js fails "Version info lives on Perf dashboard" (the #navStats wait times out). Without fail-fast it is 131 pass / 1 fail on both master and branch public/. It looks environment- or order-dependent; I did not dig further.
    • test-a11y-axe-1668.js fails on mobile light /observers, colour-contrast on the connected state label. It fails identically on master public/, and this PR does not touch colours.

Not verified

  • Real devices: iOS Safari and Android Chrome. I only used Chromium emulation (isMobile, hasTouch).
  • Dark-mode screenshots. No colour tokens changed.
  • The generic .modal > .modal-close in the real DOM (the observer sparkline dialog cannot be opened with the fixture). It is checked in the harness only, as the author says.
  • The channel-proposals dialog buttons in the real DOM. Their E2E passes, but I did not measure them myself.
  • Widths between 641 and 767 with touch, apart from the 768 sample. The pill clipping there is the same as on master.
  • A re-run of the E2E list on master 2a7877ac: only the unit suites and the harness were re-run there.

dborup and others added 3 commits October 5, 2026 09:42
…and the pill label (#239)

Review findings on #235:

- F1: test-issue-1224-channels-mobile-ux-e2e.js mocks 6 regions, so the
  channel header renders the region dropdown, and checks at 320, 360,
  390, 430 and 640 px touch that its open menu stays inside the viewport
  with every option hit at its centre. With order: 1 the trigger sits at
  the right edge and the left-anchored menu runs off-screen.
- F2: test-touch-targets.js measures .live-node-filter-hitarea in its
  real .live-toggles context and checks that it keeps cursor: text, which
  .live-toggles label (cursor: pointer) overrides.
- F3: test-touch-targets.js checks that a 48px .region-pill centres its
  label.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… and centred pills (#239)

- F1: at <=640px the channel header's region dropdown menu opens from the
  trigger's right edge (left: auto; right: 0), since order: 1 puts the
  trigger at the right edge of the strip.
- F2: the live node filter's hit-area rule is scoped to .live-toggles, so
  it wins over .live-toggles label and keeps the text cursor it had as
  an inline style before #235.
- F3: .region-pill centres its label (justify-content: center). Only a
  pill wider than its content, the 48px touch minimum, changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dborup-agent

Copy link
Copy Markdown
Collaborator Author

Rapport — CS-pve-agent1 PR#239 runde 2 — head 23778eb

Status: F1–F3 fixed with red-then-green tests, each mutant killed. Master 9989b97f merged in with a merge commit. CI is green on all jobs. Still a draft.

Review feedback addressed (commit 23778ebd; tests in 8a8c72ab; merge commit 34d6b5b6)

Evidence: [T] test run here · [A] measurement or screenshot here · [K] from the review or issue, not re-run.

Findings

# Change Test Mutant
F1 .ch-header-region .region-dropdown-menu { left: auto; right: 0; } in the ≤640px block. At 390 px the menu spans 302–522 → 160–380 px. Desktop is unchanged. test-issue-1224-channels-mobile-ux-e2e.js: 6 regions mocked via /api/config/regions, at 320/360/390/430/640 px touch. The header holds its controls; the open menu is inside the viewport; all 7 options are hit at their centre; there is no page overflow. Before the fix, 5 of 16 steps fail ("menu 302-522px is outside the 390px viewport"). After, 16/16. [T] Rule removed: the same 5 steps fail. [T]
F2 .live-node-filter-hitarea → .live-toggles .live-node-filter-hitarea (0,2,0 beats .live-toggles label 0,1,1). test-touch-targets.js measures the hit area in its real .live-toggles / .live-node-filter-wrap context and asserts cursor: text. Red before the fix (pointer). [T] On the real live page the computed cursor is text at 390 touch and at 1440. [A] Selector unscoped: the harness fails. [T]
F3 justify-content: center on .region-pill. Only a pill wider than its label changes (the 48 px touch minimum). The harness asserts that the label is centred within 1 px in a 48 px pill. Red before the fix (offset −8 px). [T] Real DOM at 390 touch: label offsets −4.9 to −10.9 px → 0.0. [A] Removed: the harness fails. [T]
F4 No change; tracked in #249. — —

In the harness, display is not asserted for F2: a flex parent blockifies inline-flex to flex, both before and after #235. [A]

Tests run locally

Screenshots (local, 390×844 touch, 6 mocked regions, channel header with the region menu open) [A]

  • Before (light): the menu starts under "All Regions" at x=302 and is cut at the right edge. The labels read "SJC - Sa…" and "MRY - Mo…", and the option centres are off-screen.
  • After (light and dark): the menu opens leftwards from the trigger's right edge (x=160–380) and sits fully on-screen below "+ Add" / "All Regions". All labels are complete ("SJC - San Jose" … "LAX - Los Angeles").
  • Pills mode (fixture, 4 regions), after: the labels in the 48 px pills are centred.

The screenshots were not uploaded; they are described here only.

CI (run 37292852773, head 23778ebd, attempt 1) [A]

Job Result
✅ Go Build & Test pass
🎭 Playwright E2E Tests pass
🏗️ Build & Publish Docker Image pass (local build; push guarded)
📦 Release Artifacts / 🚀 Deploy Staging / 📝 Publish Badges skipped

No rerun was needed; neither #244 nor #250 occurred.

Residuals

  • On the live page the hit area measured 0×0 in my check, because the controls panel is collapsed on load. Its 48 px height is covered by the harness only (as before), and Macmini's M9 (inline style restored in live.js) is still not caught. That was optional and is not done here.
  • I did not test on real devices; Chromium emulation only.
  • Everything else is unchanged from round 1 (see the author's report and the review).

@adminopenclaw8-sketch

Copy link
Copy Markdown
Collaborator

Review — CS-Macmini PR#239 runde 2 — head 23778eb

Dom: APPROVE med nits

This is a read-only re-review of round 2. F1–F3 from my round-1 review are fixed, each with a test that is red before and green after, and F4 is unchanged and tracked in #249. Nothing blocks the merge. The two new nits below already exist on master and belong in follow-ups, not in this PR.

Evidence: [T] test run here · [A] measurement or screenshot here · [K] from the author's report or CI, not re-run.

Setup:

1. My round-1 findings

F1: the channel-header region menu, ≤640 px. Fixed. [T][A]

.ch-header-region .region-dropdown-menu { left: auto; right: 0; } sits in the ≤640 block. Measured with 6 mocked regions, menu open (all 7 options hit at their centre unless noted):

width round 1 (8a8c72ab) round 2 (head)
320 touch menu 230–450, 0/7 hit menu 90–310, 7/7
390 touch 300–520, 0/7 160–380, 7/7 (light and dark)
640 touch 550–770, 0/7 410–630, 7/7
641 / 700 / 768 / 1024 / 1440 desktop 461–681 / 520–740 / 12–232 / 113–333 / 136–356 identical to round 1
  • Desktop is unchanged by construction: the rule is inside @media (max-width: 640px), and the measurements are identical.
  • The E2E covers 320, 360, 390, 430 and 640, but not the desktop position. The existing 1024 desktop guard only checks the layout direction. That is acceptable for a media-scoped rule.

F2: the live node-filter hit area. Fixed. [T][A]

  • .live-toggles .live-node-filter-hitarea (0,2,0) now beats .live-toggles label (0,1,1).
  • Computed cursor on the real live page: pointer in round 1, text in round 2, at both 390 touch and 1440 desktop. min-height is 48px.

F3: region-pill labels. Fixed. [T][A]

  • justify-content: center on .region-pill.
  • Real DOM at 390 touch (fixture, 4 pills, 48×48): label offsets are −10.9, −4.9, −5.4, −5.7 and −6.6 px in round 1, and 0.0 px for every pill in round 2.

F4. Not changed, and tracked in #249, as agreed. [A]

2. Red before, green after, and mutants

  • test-issue-1224-channels-mobile-ux-e2e.js: red 11/16 against 8a8c72ab, where the 5 failing steps are "menu … is outside the …px viewport" at 320, 360, 390, 430 and 640. Green 16/16 against the merged tree. [T]
  • test-touch-targets.js: red against the 8a8c72ab CSS on 2 assertions (cursor pointer; pill label offset −8 px). Green with 61 assertions on the merged tree. [T]

Mutants, each applied alone to the merged tree:

# mutant result
MA F1 rule removed (from the author's report) killed, 1224 at 11/16 [T]
MB F1 as left: auto only killed, 11/16 [T]
MC F1 as right: 0 only killed, 11/16 [T]
MD F2 selector unscoped (from the author's report) killed, cursor pointer [T]
ME F2 cursor: text dropped killed [T]
MF F3 justify-content removed (from the author's report) killed, offset −8 px [T]
MG F2 hit area at 44px killed, height 44 [T]
MH round-1 inline style="…min-height:44px; cursor:text;" restored in live.js survives [T]. This is the known residual from round 1; the author's report lists it as optional and not done.

3. Merge commit 34d6b5b6

  • Its tree is byte-identical to git merge-tree --write-tree bd56fecb 9989b97f (da8d19f8), so there are no manual edits in the merge. [T]
  • git diff 9989b97f 23778ebd touches only the PR's own 7 files, the same set as round 1: channel-proposals.css, live.css, live.js, style.css and the three tests. [T]
  • The merged tree against current origin/master c12aa60b merges without conflicts and also differs only in those 7 files. .github is unchanged. [T]

4. No regression in the round-1 48×48 controls

  • test-touch-targets.js OK, with 61 assertions including every round-1 selector and tap pad at ≥48. [T]
  • test-issue-2052-touch-target-e2e.js 14/0. [T]
  • The channel-proposal buttons and the live hit area still measure 48. [T]

5. Rules

  • Fork guards: github.repository == 'Kpa-clawbot/CoreScope' appears 9 times in deploy.yml and once in release-fast-path.yml, the same as master. [T]
  • No closing keywords in the PR body or in the commits 34d6b5b6, 8a8c72ab and 23778ebd. [T]
  • Author and committer of all three commits: dborup <kontakt@meshview.dk>. [T]
  • The PR is still a draft against master. [T]

Findings

# Severity Finding Since Action
F1 — The region menu was off-screen ≤640 px round 1 Fixed [T][A]
F2 — The hit-area cursor was pointer round 1 Fixed [T][A]
F3 — Pill labels were off-centre round 1 Fixed [T][A]
F4 Low Add-channel dialog buttons at 32 px round 1 Tracked in #249 [A]
N1 Low Between 641 and 767 px, the channel region menu (6 regions) runs about 40 px past the right edge: 461–681 at 641, 520–740 at 700, 587–807 at 767. Options are still hit at their centre, and there is no page overflow. Identical on master. pre-existing Follow-up issue; not this PR [A]
N2 Low The region dropdown items are 28 px tall at every width, touch included. They are below the 48 px minimum, but they were never 44 px controls, so they are outside #235's 44→48 scope. Identical on master. pre-existing Follow-up issue (touch targets) [A]
N3 Info MH survives: nothing catches the inline 44px style coming back in live.js round 1 Optional, as before [T]

Tests run (merged tree vs. origin/master c12aa60b)

  • sh test-all.sh: 218 passed, 0 failed (218 files). node test-frontend-helpers.js: 707 passed, 0 failed. [T]
  • 41 E2E files against the local server, all exit 0 [T]:
    • every channels, live and touch-target E2E listed in deploy.yml, including test-issue-1224 16/16, test-issue-2052 14/0 and test-touch-targets OK;
    • the self-starting test-channel-proposals-e2e.js 24/0 and test-channels-client-state-152-decrypt-e2e.js 18/0.
  • CI run 37292852773 on 23778ebd: Go Build & Test, Playwright E2E and Docker build passed; release, deploy and badges were skipped. [K] I confirmed this via gh run view.

Screenshots, 390×844 touch, 6 mocked regions, menu open [A]

  • Before (round 1, light): "+ Add" and "All Regions" sit at the right of the header. The menu opens under the trigger at x=300 and is cut at the right edge. Only "All", "SJC - Sa…", "SFO - Sa…", "OAK - Oa…", "MRY - Mo…", "SMF - Sa…" and "LAX - Los…" show, and the option centres are off-screen.
  • After (round 2, light and dark): the menu opens leftwards from the trigger's right edge (x=160–380) and sits fully on-screen over the channel list. All labels are complete: "All", "SJC - San Jose", "SFO - San Francisco", "OAK - Oakland", "MRY - Monterey", "SMF - Sacramento", "LAX - Los Angeles". Dark mode uses the themed card background and border.
  • Pills mode (fixture, 4 regions), after: "Region:" followed by All, MRY, OAK, SFO and SJC as 48×48 pills on the second header row, with labels centred.

Not verified

  • Real devices: Chromium emulation only.
  • Firefox and WebKit.
  • The live hit area on a real page with the controls panel expanded by a user: measured through computed style and the harness only.
  • The Docker image.
  • Staging and production: not accessed.

@adminopenclaw8-sketch

Copy link
Copy Markdown
Collaborator

Review — CS-Macmini PR#239 runde 2 — head 23778eb

Dom: APPROVE

Read-only re-review of round 2 (8a8c72ab tests, 23778ebd fix, merge 34d6b5b6). All three round-1 findings are fixed. Each fix has a test that is red on round 1 (bd56fecb) and green on head, and nothing regressed. Evidence marks: [T] a test run here, [A] an ad-hoc measurement or screenshot taken here with Playwright, [K] known from the report, an earlier review or the issue, not re-run here.

Setup [T]:

Findings

# Severity Finding Evidence
R1 Nit (pre-existing, unchanged) Between 641 and 767 px with touch, the ≤640px rule does not apply. The dropdown trigger stays where it is, and its menu box still overshoots the right edge by 40 px. All 7 option labels stay on-screen and every option is hit at its centre, so it is only the menu's right padding and border. Identical on master and on round 1, so this is out of scope. Mention it in #249 or a follow-up only if someone cares. [A] 641/660/700/720/740/767 touch, 6 mocked regions: menu right edge = vw + 40, hits 7/7, labels past edge 0, on both head and master
R2 Nit (carried over, acknowledged) Round-1 M9 still survives: restoring the old inline style="…min-height:44px…" on the live.js hit-area label is not caught by any test. The harness only sees CSS, and the live page keeps the controls panel collapsed (the hit area measures 0×0 on load). The author lists it as a residual, and it was optional. [T] mutant M6 below: harness OK, Kpa-clawbot#1224 16/16, Kpa-clawbot#2052 14/14

No new issues introduced by round 2.

1. Round-1 findings

F1 — fixed. ≤640px: .ch-header-region .region-dropdown-menu { left: auto; right: 0; }. With 6 regions mocked through /api/config/regions, touch, menu opened [A]:

Width Round 1 menu (x) Head menu (x) Options hit at centre, round 1 → head
320 230–450 90–310 0/7 → 7/7
360 270–490 130–350 0/7 → 7/7
390 300–520 160–380 0/7 → 7/7
430 340–560 200–420 0/7 → 7/7
640 550–770 410–630 0/7 → 7/7
  • The menu's right edge now aligns with the trigger's right edge, and page overflow is 0 in all cases.
  • Desktop and tablet are unchanged: at 700/768 touch and 1024/1440 the menu box, the computed left: 0 and the trigger position are identical on round 1 and head (the rule is inside the ≤640px block).
  • The selector is scoped to .ch-header-region, so other RegionFilter dropdowns are untouched [A].

F2 — fixed.

  • Selector: .live-toggles .live-node-filter-hitarea (0,2,0) now beats .live-toggles label (0,1,1).

  • Real live page, computed style [A]:

    Viewport Round 1 Head
    390 touch cursor: pointer cursor: text
    1440 cursor: pointer cursor: text

    min-height: 48px on both, and the label sits inside .live-toggles in the real DOM.

  • The harness entry now renders the label in its real .live-toggles > .live-node-filter-wrap context and asserts cursor: text [T].

F3 — fixed. justify-content: center on .region-pill. Fixture (4 regions), 390 touch, label centre offset in each 48×48 pill [A]:

Pill All MRY OAK SFO SJC
Round 1 (px) −10.9 −4.9 −5.4 −5.7 −6.6
Head (px) 0.0 0.0 0.0 0.0 0.0
  • The header height is unchanged (115 px).
  • The rule is global for .region-pill, but it only moves a label when the pill is wider than its content, i.e. under the 48 px coarse-pointer minimum. Desktop pills are content-sized [A].

F4 — unchanged, tracked in #249 (open: "add-channel dialog buttons are 32 px tall on touch") [T].

2. Tests red before, green after

Test Round 1 bd56fecb Head / merged
test-issue-1224-channels-mobile-ux-e2e.js 11/16: the 5 "region menu opens on-screen" steps fail ("menu 300-520px is outside the 390px viewport", etc.) 16/16
test-touch-targets.js (new harness) FAIL, 2 assertions: hit-area cursor pointer; pill label offset −8.0 px OK

[T]. The new Kpa-clawbot#1224 steps tap the real trigger, wait for the menu, and check the viewport bounds, the hit at each option's centre and page overflow at five widths. That is the test I suggested in round 1.

Mutants: each a copy of merged public/, served by its own server, with the harness run against the same copy [T]:

# Mutant Harness Kpa-clawbot#1224 Kpa-clawbot#2052
M1 F1 rule removed (report mutant) OK 11/16 ✗ 14/0
M2 F2 selector unscoped again (report mutant) FAIL ✗ 16/16 14/0
M3 F3 justify-content: center removed (report mutant) FAIL ✗ 16/16 14/0
M4 F1 with right: 0 only (left: 0 still wins) OK 11/16 ✗ 14/0
M5 F2 scoped, but cursor: pointer FAIL ✗ 16/16 14/0
M6 old inline min-height:44px restored in live.js OK 16/16 14/0 — survives (R2)
M7 round-1 regression: .ch-avatar.ch-tappable back to 44 FAIL ✗ 16/16 13/1 ✗

6 of 7 are killed. The survivor is the known round-1 residual.

3. Merge commit 34d6b5b6

  • git merge-tree --write-tree bd56fecb 9989b97f gives tree da8d19f8, which is exactly 34d6b5b6^{tree}. The merge is the automatic one, with no hand edits [T].
  • git diff 9989b97f 23778ebd --stat lists only the PR's 7 files (channel-proposals.css, live.css, live.js, style.css, and the three tests). Nothing from master leaks in or is reverted [T].
  • Round 2 itself (34d6b5b6..23778ebd) is 4 files, +70/−4, matching the report [T].

4. No regression in the round-1 controls

5. Rules

Screenshots (local, 390×844 touch, light; not uploaded) [A]

  • Region menu, 6 regions, before (round 1): the menu opens under "All Regions" at x=300. Its right half is cut off at the viewport edge: labels read "SJC - Sa…", "MRY - Mo…" and the option centres are off-screen.
  • Region menu, after: the menu opens leftwards under "+ Add" / "All Regions" (x=160–380), fully on-screen, with all labels complete ("SJC - San Jose" … "LAX - Los Angeles"). The channel list stays visible to its left.
  • Pills, 4 regions, before: "All", "MRY" and the others hug the left edge of their 48 px pills.
  • Pills, after: the labels are centred. The header layout is otherwise identical (title + "+ Add" on row 1, "Region:" + 5 pills on row 2).

Tests run (merged tree, master c12aa60b)

Not verified

  • Real devices (iOS Safari, Android Chrome); Chromium emulation only.
  • Dark mode: I took no dark screenshots, but no colours changed.
  • The hit area's real-DOM height with the live controls panel expanded (R2); it is covered by the harness only.
  • I did not re-run the full 133-command deploy.yml E2E list this round, only the channels, live, touch and nav subset above. CI ran the full list green.

@dborup
dborup marked this pull request as ready for review October 5, 2026 12:50
@dborup
dborup merged commit d05b0db into master Oct 5, 2026
6 checks passed
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.

3 participants