From 485221b739097806c2a80d97030128e038d05f01 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Sun, 4 Oct 2026 10:55:25 +0100 Subject: [PATCH] fix(app): enlarge compact sidebar footer tap targets (#6044) * fix(app): enlarge compact sidebar footer tap targets The compact drawer reused 28px desktop buttons, making footer actions hard to tap. Use 44px targets with larger glyphs in compact layouts while preserving desktop geometry. * test(app): wait for compact footer tap stability --- packages/app/e2e/browser/sidebar-help.spec.ts | 32 +++++++++++++++++++ packages/app/src/components/left-sidebar.tsx | 24 ++++++++------ .../components/sidebar/sidebar-help-menu.tsx | 15 +++++---- packages/app/src/styles/theme.ts | 1 + 4 files changed, 56 insertions(+), 16 deletions(-) diff --git a/packages/app/e2e/browser/sidebar-help.spec.ts b/packages/app/e2e/browser/sidebar-help.spec.ts index 11c7cc52b7..6d2ed6e96f 100644 --- a/packages/app/e2e/browser/sidebar-help.spec.ts +++ b/packages/app/e2e/browser/sidebar-help.spec.ts @@ -155,6 +155,18 @@ test("keeps diagnostics available from Settings after globalizing the sheet", as test.describe("compact sidebar help", () => { test.use({ viewport: { width: 390, height: 844 }, isMobile: true, hasTouch: true }); + test("footer buttons provide finger-sized targets and accept taps beyond the glyph", async ({ + page, + }) => { + await gotoAppShell(page); + await openCompactSidebar(page); + await expectFingerSizedFooterTargets(page); + await tapHelpNearTargetEdge(page); + await expect(page.getByTestId("sidebar-help-menu")).toBeVisible(); + await page.getByTestId("sidebar-help-diagnostics").click(); + await expectDiagnosticReport(page); + }); + test("offers diagnostics without advertising disabled keyboard shortcuts", async ({ page }) => { await gotoAppShell(page); await page.getByRole("button", { name: "Open menu", exact: true }).click(); @@ -165,3 +177,23 @@ test.describe("compact sidebar help", () => { await expectDiagnosticReport(page); }); }); + +async function openCompactSidebar(page: Page): Promise { + await page.getByRole("button", { name: "Open menu", exact: true }).click(); + await expect(page.getByTestId("sidebar-footer-bottom-line")).toBeVisible(); +} + +async function expectFingerSizedFooterTargets(page: Page): Promise { + const buttons = page.getByTestId("sidebar-footer-bottom-line").getByRole("button"); + await expect(buttons).toHaveCount(5); + for (const button of await buttons.all()) { + const bounds = await button.boundingBox(); + expect(bounds?.width).toBeGreaterThanOrEqual(44); + expect(bounds?.height).toBeGreaterThanOrEqual(44); + } +} + +async function tapHelpNearTargetEdge(page: Page): Promise { + const button = page.getByRole("button", { name: "Help and support", exact: true }); + await button.tap({ position: { x: 40, y: 40 } }); +} diff --git a/packages/app/src/components/left-sidebar.tsx b/packages/app/src/components/left-sidebar.tsx index 0737634a1b..a6def126b1 100644 --- a/packages/app/src/components/left-sidebar.tsx +++ b/packages/app/src/components/left-sidebar.tsx @@ -26,6 +26,7 @@ import { SidebarSeparator } from "@/components/sidebar/sidebar-separator"; import { SidebarNavRows } from "@/components/sidebar/sidebar-nav-rows"; import { SidebarHelpMenu } from "@/components/sidebar/sidebar-help-menu"; import { SidebarResizeHandle } from "@/components/sidebar-resize-handle"; +import { buttonControlHeight } from "@/components/ui/control-geometry"; import { Shortcut } from "@/components/ui/shortcut"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { HEADER_INNER_HEIGHT, useIsCompactFormFactor } from "@/constants/layout"; @@ -275,7 +276,7 @@ function FooterIconButton({ testID, label, icon: Icon, - iconSize, + iconSizeAdjustment = 0, shortcutKeys, theme, }: { @@ -284,17 +285,20 @@ function FooterIconButton({ label: string; icon: typeof FolderPlus; /** Only for a glyph that reads larger than the others at the same size. */ - iconSize?: number; + iconSizeAdjustment?: number; shortcutKeys?: ReturnType; theme: SidebarTheme; buttonRef?: RefObject; }) { + const isCompact = useIsCompactFormFactor(); + const iconSize = isCompact ? theme.iconSize.xl : theme.iconSize.md; + return ( {({ hovered }) => ( )} @@ -367,7 +371,7 @@ function SidebarHostPicker({ label={label} icon={Server} // Server's two boxes fill more of the square than the other glyphs. - iconSize={theme.iconSize.md - 1} + iconSizeAdjustment={-1} theme={theme} /> @@ -923,7 +927,7 @@ const styles = StyleSheet.create((theme) => ({ borderTopWidth: 1, borderTopColor: theme.colors.border, }, - // The icons' 28pt buttons sit edge to edge; their own inset spaces the glyphs. + // Buttons sit edge to edge; their own inset spaces the glyphs. sidebarFooter: { flexDirection: "row", alignItems: "center", @@ -940,14 +944,14 @@ const styles = StyleSheet.create((theme) => ({ paddingVertical: theme.spacing[1.5], gap: 2, }, - footerIconButton: { - width: 28, - height: 28, + footerIconButton: (isCompact: boolean) => ({ + width: isCompact ? buttonControlHeight.md : buttonControlHeight.xs, + height: isCompact ? buttonControlHeight.md : buttonControlHeight.xs, alignItems: "center", justifyContent: "center", paddingVertical: theme.spacing[1], paddingHorizontal: theme.spacing[1], - }, + }), tooltipRow: { flexDirection: "row", alignItems: "center", diff --git a/packages/app/src/components/sidebar/sidebar-help-menu.tsx b/packages/app/src/components/sidebar/sidebar-help-menu.tsx index d709b3d9ea..85acc8cf30 100644 --- a/packages/app/src/components/sidebar/sidebar-help-menu.tsx +++ b/packages/app/src/components/sidebar/sidebar-help-menu.tsx @@ -14,6 +14,8 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; +import { buttonControlHeight } from "@/components/ui/control-geometry"; +import { useIsCompactFormFactor } from "@/constants/layout"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { useAppDiagnosticStore } from "@/diagnostics/store"; import { useKeyboardShortcutsAvailable } from "@/keyboard/availability"; @@ -77,6 +79,7 @@ function HostVersionHint({ host }: { host: HostProfile }) { } export function SidebarHelpMenu() { + const isCompact = useIsCompactFormFactor(); const { t } = useTranslation(); const shortcutsAvailable = useKeyboardShortcutsAvailable(); const openAppDiagnostic = useAppDiagnosticStore((state) => state.open); @@ -103,14 +106,14 @@ export function SidebarHelpMenu() { {({ hovered }) => ( )} @@ -181,14 +184,14 @@ export function SidebarHelpMenu() { } const styles = StyleSheet.create((theme) => ({ - trigger: { - width: 28, - height: 28, + trigger: (isCompact: boolean) => ({ + width: isCompact ? buttonControlHeight.md : buttonControlHeight.xs, + height: isCompact ? buttonControlHeight.md : buttonControlHeight.xs, alignItems: "center", justifyContent: "center", paddingVertical: theme.spacing[1], paddingHorizontal: theme.spacing[1], - }, + }), tooltipText: { fontSize: theme.fontSize.base, color: theme.colors.popoverForeground, diff --git a/packages/app/src/styles/theme.ts b/packages/app/src/styles/theme.ts index 04b1e8f6ae..d5c1678749 100644 --- a/packages/app/src/styles/theme.ts +++ b/packages/app/src/styles/theme.ts @@ -595,6 +595,7 @@ export const ICON_SIZE = { sm: 14, md: 16, lg: 20, + xl: 24, } as const; export const ICON_STROKE_WIDTH = 1.5;