Skip to content

Commit ac42fa3

Browse files
os-zhuangclaude
andcommitted
feat(nav): iOS-native polish for the mobile sidebar drawer
Keeps the deliberate drawer-only mobile nav (no bottom tab bar, per the 2026-05-20 decision) but makes the drawer itself feel native on phones: - Tap targets: nav rows go from 32–36px to >=44px (iOS HIG minimum), with 15px text, 20px icons, and rounded-xl rows. All gated behind useIsMobile() in NavigationRenderer, so the desktop rail (32px, compact) is byte-for-byte unchanged. - Snappier open: the mobile sidebar Sheet opens in 300ms ease-out instead of the shared 500ms default. Scoped to the drawer's SheetContent only — other Sheets/dialogs keep their timing. - The mobile-only "Home" row in UnifiedSidebar matches: 44px, 15px, 20px icon, rounded-xl. Verified on showcase (390px): rows measure 44px, no console errors; desktop (1280px) class lists confirm no min-h-[44px] / rounded-xl leak. type-check passes for components, layout, and app-shell. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 4cc3fd1 commit ac42fa3

3 files changed

Lines changed: 16 additions & 8 deletions

File tree

packages/app-shell/src/layout/UnifiedSidebar.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -355,9 +355,9 @@ export function UnifiedSidebar({ activeAppName }: UnifiedSidebarProps) {
355355
<SidebarHeader className="border-b p-1.5">
356356
<SidebarMenu>
357357
<SidebarMenuItem>
358-
<SidebarMenuButton asChild className="h-9 text-sm font-medium">
358+
<SidebarMenuButton asChild className="min-h-[44px] text-[15px] font-medium rounded-xl">
359359
<Link to="/home" onClick={() => setOpenMobile(false)} data-testid="mobile-sidebar-home">
360-
<Home className="h-4 w-4" />
360+
<Home className="h-5 w-5" />
361361
<span>{t('home.nav', { defaultValue: 'Home' })}</span>
362362
</Link>
363363
</SidebarMenuButton>

packages/components/src/ui/sidebar.tsx

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/layout/src/NavigationRenderer.tsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ import {
6262
CollapsibleContent,
6363
Badge,
6464
Separator,
65+
cn,
66+
useIsMobile,
6567
} from '@object-ui/components';
6668
import type { NavigationItem } from '@object-ui/types';
6769

@@ -712,6 +714,11 @@ function NavigationItemRenderer({
712714
templateContext?: NavTemplateContext;
713715
}) {
714716
const location = useLocation();
717+
// iOS-native mobile drawer polish: >=44px tap targets, larger text and
718+
// icons, rounder rows. Desktop (>=768px) keeps the compact rail untouched.
719+
const isMobile = useIsMobile();
720+
const mobileBtnClass = isMobile ? 'min-h-[44px] text-[15px] gap-3 rounded-xl' : undefined;
721+
const navIconClass = cn('shrink-0', isMobile ? 'h-5 w-5' : 'h-4 w-4');
715722
// Resolve the initial open state with platform-aware defaults:
716723
//
717724
// 1. `expanded` is the spec field name; `defaultOpen` is the legacy
@@ -787,10 +794,10 @@ function NavigationItemRenderer({
787794
<Collapsible open={isOpen} onOpenChange={setIsOpen}>
788795
<SidebarGroup>
789796
<SidebarGroupLabel asChild>
790-
<CollapsibleTrigger className="flex w-full items-center justify-between">
797+
<CollapsibleTrigger className={cn('flex w-full items-center justify-between', isMobile && 'min-h-[44px] text-[15px] rounded-xl')}>
791798
{groupLabel}
792799
<ChevronRight
793-
className={`ml-auto h-4 w-4 transition-transform ${isOpen ? 'rotate-90' : ''}`}
800+
className={cn('ml-auto transition-transform', isMobile ? 'h-5 w-5' : 'h-4 w-4', isOpen && 'rotate-90')}
794801
/>
795802
</CollapsibleTrigger>
796803
</SidebarGroupLabel>
@@ -843,8 +850,9 @@ function NavigationItemRenderer({
843850
<SidebarMenuButton
844851
tooltip={actionLabel}
845852
onClick={() => onAction?.(item)}
853+
className={mobileBtnClass}
846854
>
847-
<Icon className="h-4 w-4" />
855+
<Icon className={navIconClass} />
848856
<span>{actionLabel}</span>
849857
{item.badge != null && (
850858
<Badge variant={item.badgeVariant ?? 'default'} className="ml-auto text-[10px] px-1.5 py-0">
@@ -884,7 +892,7 @@ function NavigationItemRenderer({
884892

885893
const content = (
886894
<>
887-
<Icon className="h-4 w-4" />
895+
<Icon className={navIconClass} />
888896
<span>{itemLabel}</span>
889897
{item.badge != null && (
890898
<Badge variant={item.badgeVariant ?? 'default'} className="ml-auto text-[10px] px-1.5 py-0">
@@ -905,7 +913,7 @@ function NavigationItemRenderer({
905913
<GripVertical className="h-3.5 w-3.5" />
906914
</span>
907915
)}
908-
<SidebarMenuButton asChild isActive={isActive} tooltip={itemLabel}>
916+
<SidebarMenuButton asChild isActive={isActive} tooltip={itemLabel} className={mobileBtnClass}>
909917
{external ? (
910918
<a href={href} target="_blank" rel="noopener noreferrer">
911919
{content}

0 commit comments

Comments
 (0)