diff --git a/.changeset/fn-7048-task-detail-mobile-tab-touch-action.md b/.changeset/fn-7048-task-detail-mobile-tab-touch-action.md new file mode 100644 index 0000000000..efab54a76e --- /dev/null +++ b/.changeset/fn-7048-task-detail-mobile-tab-touch-action.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Restore mobile swipe scrolling when touching task-detail tab buttons. +category: fix +dev: Adds detail-tab touch-action pan-x coverage to override the global mobile pan-y lock. diff --git a/packages/dashboard/app/components/TaskDetailModal.css b/packages/dashboard/app/components/TaskDetailModal.css index f0a6b76987..799c189d49 100644 --- a/packages/dashboard/app/components/TaskDetailModal.css +++ b/packages/dashboard/app/components/TaskDetailModal.css @@ -1025,6 +1025,10 @@ FN-6500 fixes a tablet regression from FN-5599: the task-detail overlay offset a touch-action: pan-x pan-y; -webkit-overflow-scrolling: touch; } + + .detail-tab { + touch-action: pan-x pan-y; + } } @media (max-width: 768px) { @@ -1133,6 +1137,10 @@ Embedded in the full-width board-card panel the detail content must be width-bou -webkit-overflow-scrolling: touch; } +.task-detail-content--embedded .detail-tab { + touch-action: pan-x pan-y; +} + /* The gray header row must wrap (task id left, Back-to-board right) instead of overflowing on narrow panels. */ .task-detail-content--embedded .modal-header { flex-wrap: wrap; @@ -1940,6 +1948,9 @@ Remove the divider between the tab selector and the detail area below. Keep the FNXC:TaskDetailTabs 2026-06-25-16:55: Task-detail tabs must remain reachable as a one-row horizontal scroller on mobile across the Board modal and List embedded pane. The tab strip is the scroller, opts out of the global mobile pan-y lock with pan-x pan-y, and keeps tab children non-shrinking so conditional and plugin tabs overflow-scroll instead of clipping or compressing. + +FNXC:TaskDetailTabs 2026-06-26-00:35: +`touch-action` is not inherited, so the tab buttons must also opt into pan-x because they are the real touch targets. A container-only opt-in leaves touches that start on a `.detail-tab` under the global mobile `* { touch-action: pan-y; }` lock and blocks horizontal swipes. */ .detail-tabs { display: flex; @@ -1958,6 +1969,7 @@ Task-detail tabs must remain reachable as a one-row horizontal scroller on mobil .detail-tab { flex-shrink: 0; + touch-action: pan-x pan-y; padding: var(--space-sm) var(--space-lg); background: none; border: none; @@ -2042,6 +2054,7 @@ Task-detail tabs must remain reachable as a one-row horizontal scroller on mobil .detail-tab { min-height: 36px; + touch-action: pan-x pan-y; } .detail-source-grid, @@ -2150,6 +2163,7 @@ Task-detail tabs must remain reachable as a one-row horizontal scroller on mobil } .detail-tab { + touch-action: pan-x pan-y; padding: var(--space-sm) var(--space-md); font-size: 13px; } diff --git a/packages/dashboard/app/components/__tests__/TaskDetailModal.responsive-and-dependencies.test.tsx b/packages/dashboard/app/components/__tests__/TaskDetailModal.responsive-and-dependencies.test.tsx index 6280bee47f..c678eee0fd 100644 --- a/packages/dashboard/app/components/__tests__/TaskDetailModal.responsive-and-dependencies.test.tsx +++ b/packages/dashboard/app/components/__tests__/TaskDetailModal.responsive-and-dependencies.test.tsx @@ -84,6 +84,10 @@ function expectHorizontalTabScroller(ruleBlock: string, surface: string): void { expect(ruleBlock, `${surface} momentum-scroll`).toContain("-webkit-overflow-scrolling: touch;"); } +function expectTabTouchAction(ruleBlock: string, surface: string): void { + expect(ruleBlock, `${surface} touch-action`).toContain("touch-action: pan-x pan-y;"); +} + describe("TaskDetailModal", () => { describe("mobile responsive structure", () => { it("keeps detail metadata as a single wrapping flex row without mobile column fallbacks", () => { @@ -157,12 +161,20 @@ describe("TaskDetailModal", () => { const embeddedTabsBlock = getExactCssRuleBlock(css, ".task-detail-content--embedded .detail-tabs"); const detailContentBlock = getCssRuleBlock(css, ".task-detail-content"); const detailBodyBlock = getCssRuleBlock(css, ".detail-body"); - const detailTabBlock = getCssRuleBlock(css, ".detail-tab"); + const baseDetailTabsSection = css.slice(css.indexOf("/* === Detail Tabs === */")); + const detailTabBlock = getExactCssRuleBlock(baseDetailTabsSection, ".detail-tab"); + const mobileTabBlock = getExactCssRuleBlock(mobileBlock, ".detail-tab"); + const tabletTabBlock = getExactCssRuleBlock(tabletBlock, ".detail-tab"); + const embeddedTabBlock = getExactCssRuleBlock(css, ".task-detail-content--embedded .detail-tab"); expectHorizontalTabScroller(baseTabsBlock, "base .detail-tabs"); expectHorizontalTabScroller(mobileTabsBlock, "mobile .detail-tabs"); expectHorizontalTabScroller(tabletTabsBlock, "tablet .detail-tabs"); expectHorizontalTabScroller(embeddedTabsBlock, "embedded .detail-tabs"); + expectTabTouchAction(detailTabBlock, "base .detail-tab"); + expectTabTouchAction(mobileTabBlock, "mobile .detail-tab"); + expectTabTouchAction(tabletTabBlock, "tablet .detail-tab"); + expectTabTouchAction(embeddedTabBlock, "embedded .detail-tab"); expect(baseTabsBlock).toContain("min-width: 0;"); expect(mobileTabsBlock).toContain("min-width: 0;"); expect(detailTabBlock).toContain("flex-shrink: 0;");