fix(desktop): don't gate hover affordances on the hover media query - #3657
Merged
Conversation
Tailwind v4 wraps `hover:` in `@media (hover: hover)`. WebView2 on some Windows hosts reports `hover: none` with a mouse attached, leaving every hover-revealed control permanently invisible. Signed-off-by: sumit-m <33051892+sumit-m@users.noreply.github.com>
bradseiler
approved these changes
Jul 30, 2026
tellaho
pushed a commit
that referenced
this pull request
Jul 30, 2026
* origin/main: fix(desktop): allow linux-only media items as dead code off-linux (#3811) fix(desktop): report authenticated relay recovery (#3812) fix(desktop): don't gate hover affordances on the hover media query (#3657) feat(relay): gate kind 30178 team-catalog reads behind the shared tag (#3358) test(desktop): click visible thread collapse guide (#3800) feat(desktop): raise the install ceiling and make installs observable (#3368) fix(db): isolate usage metrics advisory-lock test on scratch DB (#3670) Add Devin as a preset ACP harness (#3225) feat(desktop): improve agent activity header ui (#3321) perf(presence): reduce heartbeat frequency (#3783) Tighten continuation message rows (#3724) Fix video reviews in thread replies (#3719) feat(release): make desktop releases immutable (#3568) Make relay reconnect backoff authoritative (#3774) feat(desktop): add password-protected backups in settings (#3701) fix(desktop): reuse profiles when joining communities (#2155) Signed-off-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz>
brow
pushed a commit
that referenced
this pull request
Jul 31, 2026
* origin/main: (70 commits) fix(catalog): update Amp tagline (#3806) fix(desktop): channel topic and membership metadata cleanup (#3642) fix(desktop): align data deletion labels (#2230) fix(relay): align NIP-11 max_limit with REQ ceiling (#3635) fix(desktop): allow linux-only media items as dead code off-linux (#3811) fix(desktop): report authenticated relay recovery (#3812) fix(desktop): don't gate hover affordances on the hover media query (#3657) feat(relay): gate kind 30178 team-catalog reads behind the shared tag (#3358) test(desktop): click visible thread collapse guide (#3800) feat(desktop): raise the install ceiling and make installs observable (#3368) fix(db): isolate usage metrics advisory-lock test on scratch DB (#3670) Add Devin as a preset ACP harness (#3225) feat(desktop): improve agent activity header ui (#3321) perf(presence): reduce heartbeat frequency (#3783) Tighten continuation message rows (#3724) Fix video reviews in thread replies (#3719) feat(release): make desktop releases immutable (#3568) Make relay reconnect backoff authoritative (#3774) feat(desktop): add password-protected backups in settings (#3701) fix(desktop): reuse profiles when joining communities (#2155) ... Signed-off-by: npub15w828kxsxu2684ynste0uah2jwkgatd99flt7ds4523hzm8ju6cshdr8hh <a38ea3d8d03715a3d49382f2fe76ea93ac8eada52a7ebf3615a2a3716cf2e6b1@buzz.block.builderlab.xyz>
wpfleger96
pushed a commit
that referenced
this pull request
Jul 31, 2026
…chive * origin/main: (25 commits) feat(desktop): import local Pocket voices (#3259) fix(desktop): open profiles from avatars (#3751) refactor(voice): extract reusable Pocket primitives + Pocket voice settings (relands #2467 + #3208) (#3910) docs: add VISION_REMOTE_AGENTS.md (#3924) feat(desktop): auto-enable huddle transcription for agents (#3180) feat(agent): optional reply guard reminds a silent turn to publish (#3763) feat(desktop): upgrade Pocket TTS model (#3266) feat(desktop): delete a message by clearing its edit to empty (#3813) feat(relay): raise hosted community limit to five (#3829) feat(desktop): locally stored NIP-49 encrypted key backup (#2937) fix(catalog): update Amp tagline (#3806) fix(desktop): channel topic and membership metadata cleanup (#3642) fix(desktop): align data deletion labels (#2230) fix(relay): align NIP-11 max_limit with REQ ceiling (#3635) fix(desktop): allow linux-only media items as dead code off-linux (#3811) fix(desktop): report authenticated relay recovery (#3812) fix(desktop): don't gate hover affordances on the hover media query (#3657) feat(relay): gate kind 30178 team-catalog reads behind the shared tag (#3358) test(desktop): click visible thread collapse guide (#3800) feat(desktop): raise the install ceiling and make installs observable (#3368) ... Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com> # Conflicts: # desktop/src/testing/e2eBridge.ts # desktop/tests/helpers/bridge.ts
wpfleger96
pushed a commit
that referenced
this pull request
Jul 31, 2026
…chive * origin/main: (25 commits) feat(desktop): import local Pocket voices (#3259) fix(desktop): open profiles from avatars (#3751) refactor(voice): extract reusable Pocket primitives + Pocket voice settings (relands #2467 + #3208) (#3910) docs: add VISION_REMOTE_AGENTS.md (#3924) feat(desktop): auto-enable huddle transcription for agents (#3180) feat(agent): optional reply guard reminds a silent turn to publish (#3763) feat(desktop): upgrade Pocket TTS model (#3266) feat(desktop): delete a message by clearing its edit to empty (#3813) feat(relay): raise hosted community limit to five (#3829) feat(desktop): locally stored NIP-49 encrypted key backup (#2937) fix(catalog): update Amp tagline (#3806) fix(desktop): channel topic and membership metadata cleanup (#3642) fix(desktop): align data deletion labels (#2230) fix(relay): align NIP-11 max_limit with REQ ceiling (#3635) fix(desktop): allow linux-only media items as dead code off-linux (#3811) fix(desktop): report authenticated relay recovery (#3812) fix(desktop): don't gate hover affordances on the hover media query (#3657) feat(relay): gate kind 30178 team-catalog reads behind the shared tag (#3358) test(desktop): click visible thread collapse guide (#3800) feat(desktop): raise the install ceiling and make installs observable (#3368) ... Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com> # Conflicts: # desktop/src/testing/e2eBridge.ts # desktop/tests/helpers/bridge.ts
joahg
added a commit
to joahg/buzz-dev-mode
that referenced
this pull request
Jul 31, 2026
…-style * origin/main: (22 commits) feat(desktop): import local Pocket voices (block#3259) fix(desktop): open profiles from avatars (block#3751) refactor(voice): extract reusable Pocket primitives + Pocket voice settings (relands block#2467 + block#3208) (block#3910) docs: add VISION_REMOTE_AGENTS.md (block#3924) feat(desktop): auto-enable huddle transcription for agents (block#3180) feat(agent): optional reply guard reminds a silent turn to publish (block#3763) feat(desktop): upgrade Pocket TTS model (block#3266) feat(desktop): delete a message by clearing its edit to empty (block#3813) feat(relay): raise hosted community limit to five (block#3829) feat(desktop): locally stored NIP-49 encrypted key backup (block#2937) fix(catalog): update Amp tagline (block#3806) fix(desktop): channel topic and membership metadata cleanup (block#3642) fix(desktop): align data deletion labels (block#2230) fix(relay): align NIP-11 max_limit with REQ ceiling (block#3635) fix(desktop): allow linux-only media items as dead code off-linux (block#3811) fix(desktop): report authenticated relay recovery (block#3812) fix(desktop): don't gate hover affordances on the hover media query (block#3657) feat(relay): gate kind 30178 team-catalog reads behind the shared tag (block#3358) test(desktop): click visible thread collapse guide (block#3800) feat(desktop): raise the install ceiling and make installs observable (block#3368) ... Amp-Thread-ID: https://ampcode.com/threads/T-019fb8e1-6ece-72a7-8808-9b12e0f7e833 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com> # Conflicts: # desktop/src-tauri/src/linux_media.rs # desktop/src/app/AppShell.tsx
calvadev
pushed a commit
to shopstr-eng/buzz
that referenced
this pull request
Aug 3, 2026
…lock#3657) ## What problem this solves Tailwind v4 compiles every `hover:` variant inside `@media (hover: hover)`. Some Windows hosts answer that query `false` **even with a mouse attached**, and then every hover-revealed control in the app is permanently `visibility: hidden`. Measured in the app's own WebView2 devtools console, on a mouse-driven Windows 11 desktop: ```js matchMedia('(hover: hover)').matches // false matchMedia('(any-hover: hover)').matches // false matchMedia('(pointer: fine)').matches // false matchMedia('(any-pointer: fine)').matches // false navigator.maxTouchPoints // 10 ``` Windows itself, on the same machine at the same moment, reports a mouse present and an integrated digitizer: ``` GetSystemMetrics(SM_DIGITIZER) = 197 // INTEGRATED_TOUCH | INTEGRATED_PEN // | MULTI_INPUT | READY GetSystemMetrics(SM_MAXIMUMTOUCHES) = 10 SystemInformation.MousePresent = True ``` So this is not "the user has no mouse". Windows knows a mouse is attached, and Chromium still reports `any-pointer: fine: false` and `any-hover: false` — the `any-*` queries exist precisely to describe *any* available input device, and they are wrong here. The presence of an integrated touch digitizer collapses the reported capability to touch-only. The compiled rule that never applies: ```css .group-hover\/member\:visible { &:is(:where(.group\/member):hover *) { @media (hover: hover) { visibility: visible; } } } ``` The row genuinely matches `:hover` (verified: `row.matches(':hover') === true`), the button is in the DOM, the utility class is generated — and the declaration still never lands. ## Why this is more than one control Not a single menu. Confirmed newly-ungated in the production bundle after the change: | utility | media-gated before | after | |---|---|---| | `group-hover/member:visible` | yes | no | | `group-hover/inbox-item:opacity-100` | yes | no | | `group-hover/channel-row:opacity-100` | yes | no | | `group-hover/attachment:opacity-100` | yes | no | | `hover:bg-muted` | yes | no | On an affected host the channel-member action menu (remove member, change role, start/stop agent) has **no reachable affordance at all**: `visibility: hidden` also removes the button from tab order, so there is no keyboard path either. ## The fix One line, at the root, next to the existing variant override: ```css @custom-variant hover (&:hover); ``` This trusts the actual hover event rather than the capability query. Chromium only fires `:hover` when a real pointer is present, so behaviour on hosts that report the capability correctly is unchanged. Verified against a production `vite build`, not just the dev server — the override cascades to the *named* group variants (`group-hover/member`, etc.), which is the part that matters here. ## Prior art in this repo block#2849 overrides Tailwind v4's `dark:` variant default at the *exact same insertion point* in this file, for the same class of reason (a v4 default that does not match how this app actually works). This change follows that precedent. **Note for whoever merges second: block#2849 and this PR will conflict textually** — both append a `@custom-variant` immediately after `@config`. The resolution is to keep both lines; they are independent. ## Scope Desktop only. `web/src/shared/styles/globals.css` has the same Tailwind v4 default, but `web/src` contains **zero** `group-hover` usages, so there are no hover-revealed affordances to strand there. Adding the override to web would be speculative. One `hover` capability query is deliberately left in place — `.buzz-wave-hover-trigger` in `animations.css` gates a decorative wave-hand animation on `(hover: hover) and (pointer: fine)`. That is a cosmetic flourish rather than an affordance, so it stays inert on affected hosts instead of widening this diff. ## Reproducing The trigger is **an integrated touch digitizer anywhere on the machine**, not the display you are actually working on. This was found on a touch-capable laptop docked to an ordinary non-touch external monitor, driven entirely by a mouse — so "I'm on a desktop monitor" does not rule you out. Check with: ```js matchMedia('(hover: hover)').matches // false ⇒ affected ``` Not reproducible on macOS, or on a Windows machine with no digitizer at all — `hover: hover` is true there and every affordance works normally. If you are on such a host, emulate it in devtools by forcing `hover: none` / `pointer: coarse`, then open a channel's member list and hover a row: no action menu appears. ## Tradeoff worth naming On a genuine touch-only device, a bare `&:hover` can latch after a tap and stay applied until the next interaction, where the media-query default would have suppressed it. That is the real cost of this change. The judgement here is that a stuck hover style is a cosmetic annoyance, while an unreachable "remove member" button is a functional dead end — and that the affected hosts are overwhelmingly mouse-driven machines that merely *happen* to ship a digitizer, as the `MousePresent = True` reading above shows. If you would rather scope this to `@media not (hover: hover)` as an additive fallback instead of overriding the variant, I am happy to rework it. Signed-off-by: sumit-m <33051892+sumit-m@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What problem this solves
Tailwind v4 compiles every
hover:variant inside@media (hover: hover). SomeWindows hosts answer that query
falseeven with a mouse attached, and thenevery hover-revealed control in the app is permanently
visibility: hidden.Measured in the app's own WebView2 devtools console, on a mouse-driven Windows 11
desktop:
Windows itself, on the same machine at the same moment, reports a mouse present
and an integrated digitizer:
So this is not "the user has no mouse". Windows knows a mouse is attached, and
Chromium still reports
any-pointer: fine: falseandany-hover: false— theany-*queries exist precisely to describe any available input device, andthey are wrong here. The presence of an integrated touch digitizer collapses the
reported capability to touch-only.
The compiled rule that never applies:
The row genuinely matches
:hover(verified:row.matches(':hover') === true),the button is in the DOM, the utility class is generated — and the declaration
still never lands.
Why this is more than one control
Not a single menu. Confirmed newly-ungated in the production bundle after the
change:
group-hover/member:visiblegroup-hover/inbox-item:opacity-100group-hover/channel-row:opacity-100group-hover/attachment:opacity-100hover:bg-mutedOn an affected host the channel-member action menu (remove member, change role,
start/stop agent) has no reachable affordance at all:
visibility: hiddenalso removes the button from tab order, so there is no keyboard path either.
The fix
One line, at the root, next to the existing variant override:
This trusts the actual hover event rather than the capability query. Chromium
only fires
:hoverwhen a real pointer is present, so behaviour on hosts thatreport the capability correctly is unchanged.
Verified against a production
vite build, not just the dev server — theoverride cascades to the named group variants (
group-hover/member, etc.),which is the part that matters here.
Prior art in this repo
#2849 overrides Tailwind v4's
dark:variant default at the exact sameinsertion point in this file, for the same class of reason (a v4 default that
does not match how this app actually works). This change follows that precedent.
Note for whoever merges second: #2849 and this PR will conflict textually —
both append a
@custom-variantimmediately after@config. The resolution isto keep both lines; they are independent.
Scope
Desktop only.
web/src/shared/styles/globals.csshas the same Tailwind v4default, but
web/srccontains zerogroup-hoverusages, so there are nohover-revealed affordances to strand there. Adding the override to web would be
speculative.
One
hovercapability query is deliberately left in place —.buzz-wave-hover-triggerinanimations.cssgates a decorative wave-handanimation on
(hover: hover) and (pointer: fine). That is a cosmetic flourishrather than an affordance, so it stays inert on affected hosts instead of
widening this diff.
Reproducing
The trigger is an integrated touch digitizer anywhere on the machine, not the
display you are actually working on. This was found on a touch-capable laptop
docked to an ordinary non-touch external monitor, driven entirely by a mouse — so
"I'm on a desktop monitor" does not rule you out. Check with:
Not reproducible on macOS, or on a Windows machine with no digitizer at all —
hover: hoveris true there and every affordance works normally. If you are onsuch a host, emulate it in devtools by forcing
hover: none/pointer: coarse,then open a channel's member list and hover a row: no action menu appears.
Tradeoff worth naming
On a genuine touch-only device, a bare
&:hovercan latch after a tap and stayapplied until the next interaction, where the media-query default would have
suppressed it. That is the real cost of this change.
The judgement here is that a stuck hover style is a cosmetic annoyance, while an
unreachable "remove member" button is a functional dead end — and that the
affected hosts are overwhelmingly mouse-driven machines that merely happen to
ship a digitizer, as the
MousePresent = Truereading above shows. If you wouldrather scope this to
@media not (hover: hover)as an additive fallback insteadof overriding the variant, I am happy to rework it.