diff --git a/Microsoft/Windows/PowerShell/ModuleAnalysisCache b/Microsoft/Windows/PowerShell/ModuleAnalysisCache
new file mode 100644
index 00000000..fa9dde86
Binary files /dev/null and b/Microsoft/Windows/PowerShell/ModuleAnalysisCache differ
diff --git a/docs/MOBILE_NAVIGATION_RESPONSIVE_DESIGN.md b/docs/MOBILE_NAVIGATION_RESPONSIVE_DESIGN.md
new file mode 100644
index 00000000..59c796cd
--- /dev/null
+++ b/docs/MOBILE_NAVIGATION_RESPONSIVE_DESIGN.md
@@ -0,0 +1,7 @@
+# Mobile Navigation Responsive Design
+
+`MobileNavigation` renders as a bottom tab bar on compact and portrait screens. It switches to a left-side rail only when the viewport is at least `640px` wide and in landscape orientation.
+
+This keeps the navigation reachable on phones while avoiding an unexpected side rail on narrow portrait layouts. The component also preserves safe-area padding with `env(safe-area-inset-*)`, remains hidden at the `lg` breakpoint, and uses WAI-ARIA tab semantics with roving keyboard focus.
+
+Responsive behavior is covered by `src/components/mobile/__tests__/MobileNavigation.test.tsx`.
diff --git a/src/components/mobile/MobileNavigation.tsx b/src/components/mobile/MobileNavigation.tsx
index 112f4643..495435ad 100644
--- a/src/components/mobile/MobileNavigation.tsx
+++ b/src/components/mobile/MobileNavigation.tsx
@@ -1,18 +1,52 @@
-import React, { useState } from 'react';
+'use client';
+
+import React, { useState, useEffect, useRef } from 'react';
import { Home, Search, BookOpen, User } from 'lucide-react';
interface NavItem {
id: string;
label: string;
icon: React.ReactNode;
- onClick?: () => void;
}
+const railNavClasses = [
+ '[@media_(min-width:640px)_and_(orientation:landscape)]:top-0',
+ '[@media_(min-width:640px)_and_(orientation:landscape)]:bottom-0',
+ '[@media_(min-width:640px)_and_(orientation:landscape)]:left-0',
+ '[@media_(min-width:640px)_and_(orientation:landscape)]:right-auto',
+ '[@media_(min-width:640px)_and_(orientation:landscape)]:h-dvh',
+ '[@media_(min-width:640px)_and_(orientation:landscape)]:w-20',
+ '[@media_(min-width:640px)_and_(orientation:landscape)]:border-t-0',
+ '[@media_(min-width:640px)_and_(orientation:landscape)]:border-r',
+].join(' ');
+const railListClasses = [
+ '[@media_(min-width:640px)_and_(orientation:landscape)]:h-full',
+ '[@media_(min-width:640px)_and_(orientation:landscape)]:flex-col',
+ '[@media_(min-width:640px)_and_(orientation:landscape)]:justify-start',
+ '[@media_(min-width:640px)_and_(orientation:landscape)]:space-y-6',
+ '[@media_(min-width:640px)_and_(orientation:landscape)]:px-0',
+ '[@media_(min-width:640px)_and_(orientation:landscape)]:pt-8',
+].join(' ');
+const railListItemClasses = [
+ '[@media_(min-width:640px)_and_(orientation:landscape)]:flex-none',
+ '[@media_(min-width:640px)_and_(orientation:landscape)]:px-2',
+].join(' ');
+const railButtonClasses = [
+ '[@media_(min-width:640px)_and_(orientation:landscape)]:mx-auto',
+ '[@media_(min-width:640px)_and_(orientation:landscape)]:h-14',
+ '[@media_(min-width:640px)_and_(orientation:landscape)]:w-14',
+ '[@media_(min-width:640px)_and_(orientation:landscape)]:max-w-none',
+ '[@media_(min-width:640px)_and_(orientation:landscape)]:py-0',
+].join(' ');
+const railHiddenClass = '[@media_(min-width:640px)_and_(orientation:landscape)]:hidden';
+const railBlockClass = '[@media_(min-width:640px)_and_(orientation:landscape)]:block';
+
export const MobileNavigation: React.FC<{
initialActive?: string;
onNavChange?: (id: string) => void;
}> = ({ initialActive = 'home', onNavChange }) => {
const [activeTab, setActiveTab] = useState(initialActive);
+ const buttonRefs = useRef<(HTMLButtonElement | null)[]>([]);
const navItems: NavItem[] = [
{ id: 'home', label: 'Home', icon: },
@@ -21,40 +55,107 @@ export const MobileNavigation: React.FC<{
{ id: 'profile', label: 'Profile', icon: },
];
+ // Sync state with prop
+ useEffect(() => {
+ setActiveTab(initialActive);
+ }, [initialActive]);
+
const handleTabClick = (id: string) => {
setActiveTab(id);
if (onNavChange) onNavChange(id);
};
+ const handleKeyDown = (e: React.KeyboardEvent, index: number) => {
+ let nextIndex = -1;
+ const length = navItems.length;
+
+ if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
+ nextIndex = (index + 1) % length;
+ } else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
+ nextIndex = (index - 1 + length) % length;
+ } else if (e.key === 'Home') {
+ nextIndex = 0;
+ } else if (e.key === 'End') {
+ nextIndex = length - 1;
+ }
+
+ if (nextIndex !== -1) {
+ e.preventDefault();
+ buttonRefs.current[nextIndex]?.focus();
+ }
+ };
+
return (