You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Dogfood QA on a real iPhone (iOS 18.7 / Safari 26.5), AFTER the #730 marker-crash fix landed, found three Tier-2 components still broken on touch. All the rest now work. Fix all three in a SINGLE PR.
hover-card: opens only on mouseenter / focusin. Touch has no hover, so a tap falls through to the inner <a href> and the client router navigates (looks like a page "refresh"; it is NOT prefetch). Tapping the trigger should open the card and not navigate the link.
dropdown-menu submenu: the sub-trigger opens the submenu on pointer-enter and closes on pointer-leave, so on touch the submenu is only open while the finger is held down. A tap should toggle the submenu open and keep it open.
sonner: the demo "Show toast" button calls toast.success() via a dynamic import. The import path (/components/ui/sonner.ts, HTTP 200) and the toast.success export both verified, but a Chromium + desktop-WebKit triage shows the toast does NOT render even on desktop (clicked=true, <ui-sonner> mounted, but no toast node appears). So sonner is broader than iOS, likely the <ui-sonner> viewport signal re-render or where the toast node is placed.
Design / approach
hover-card + dropdown share a root cause: hover/pointer-only interaction with no touch path. Add a pointer-type-aware tap path (e.g. @click / pointerdown with pointerType === 'touch', or a coarse-pointer check) that opens (hover-card) / toggles (submenu) without breaking mouse hover or keyboard nav. For hover-card the tap handler must preventDefault() the inner link navigation on the first tap (open), letting a second tap follow the link, or open without consuming the link per the agreed UX.
sonner is a separate, desktop-reproducible bug: root-cause why toast() does not produce a visible toast (the items signal re-render in <ui-sonner>, the toaster singleton routing in connectedCallback, or the toast node placement), then fix.
Implementation notes (for the implementing agent)
Where to edit (registry is the source; scripts/copy-registry.js mirrors it into the ui-website):
packages/ui/packages/registry/components/hover-card.ts (UiHoverCardTrigger; open handlers _showTimer/_hideTimer, the control lookup a[href], button, ... ~L90).
sonner (3) IS reproducible on desktop Chromium/WebKit, so fix + verify it locally first.
Do NOT regress mouse-hover or keyboard behaviour. Hover-card's inner <a> must still navigate with JS off (progressive enhancement).
<ui-sonner>'s connectedCallback overwrites the singleton toaster so the last-mounted instance wins (noted in examples.ts ~L916); multiple <ui-sonner> on one page is a known gotcha.
Tests + docs: browser tests (packages/core or a ui-package test layer) for the interaction + the sonner render (Chromium-runnable for sonner); update component docs/examples if the interaction contract changes.
Acceptance criteria
hover-card opens on tap (touch) without navigating the inner link; mouse hover still opens it; JS-off the link still navigates.
dropdown submenu opens and STAYS open on tap (touch); mouse hover still works; keyboard arrow/Escape nav unaffected.
sonner toast renders when triggered (fix the desktop-reproducible failure first; then confirm on iOS).
Tests cover each change at the layer it touches; the sonner render has a Chromium browser test; a counterfactual proves it fires.
All three fixed in a SINGLE PR; any on-device diagnostic scaffolding removed before merge.
Problem
Dogfood QA on a real iPhone (iOS 18.7 / Safari 26.5), AFTER the #730 marker-crash fix landed, found three Tier-2 components still broken on touch. All the rest now work. Fix all three in a SINGLE PR.
mouseenter/focusin. Touch has no hover, so a tap falls through to the inner<a href>and the client router navigates (looks like a page "refresh"; it is NOT prefetch). Tapping the trigger should open the card and not navigate the link.toast.success()via a dynamic import. The import path (/components/ui/sonner.ts, HTTP 200) and thetoast.successexport both verified, but a Chromium + desktop-WebKit triage shows the toast does NOT render even on desktop (clicked=true,<ui-sonner>mounted, but no toast node appears). So sonner is broader than iOS, likely the<ui-sonner>viewport signal re-render or where the toast node is placed.Design / approach
@click/pointerdownwithpointerType === 'touch', or a coarse-pointer check) that opens (hover-card) / toggles (submenu) without breaking mouse hover or keyboard nav. For hover-card the tap handler mustpreventDefault()the inner link navigation on the first tap (open), letting a second tap follow the link, or open without consuming the link per the agreed UX.toast()does not produce a visible toast (theitemssignal re-render in<ui-sonner>, thetoastersingleton routing inconnectedCallback, or the toast node placement), then fix.Implementation notes (for the implementing agent)
scripts/copy-registry.jsmirrors it into the ui-website):packages/ui/packages/registry/components/hover-card.ts(UiHoverCardTrigger; open handlers_showTimer/_hideTimer, the control lookupa[href], button, ...~L90).packages/ui/packages/registry/components/dropdown-menu.ts(UiDropdownMenuSub/UiDropdownMenuSubTrigger; submenu open/close +dropdownMenuSubTriggerClass).packages/ui/packages/registry/components/sonner.ts(toastersingleton ~L64,toast()export ~L?,<ui-sonner>connectedCallbacksetstoaster.add = this._add~L152,items = signal<ToastItem[]>([])~L140,_add/addToast~L165,render()withrepeat).packages/ui/packages/website/app/docs/components/[name]/examples.ts(sonner demo at L529onclick="import('/components/ui/sonner.ts').then(m => m.toast.success(...))").<a>must still navigate with JS off (progressive enhancement).<ui-sonner>'sconnectedCallbackoverwrites the singletontoasterso the last-mounted instance wins (noted in examples.ts ~L916); multiple<ui-sonner>on one page is a known gotcha.signal.get()inrender()); no class-field reactive props; tag-prefixed CSS in light DOM. Build on dogfood: all tier-2 ui components dead on iOS (slot hydration); tap does nothing #730 (MARKER='wjm-').packages/coreor a ui-package test layer) for the interaction + the sonner render (Chromium-runnable for sonner); update component docs/examples if the interaction contract changes.Acceptance criteria