Back to Examples

Stack Navigation Layout

Stack Navigation Layout

The stack navigation pattern implements iOS-style hierarchical push/pop navigation with intelligent responsive behavior. On mobile, it provides focused single-panel navigation with breadcrumb trails. On wider screens, it reveals multiple stack levels simultaneously with flexible panel management, creating an efficient multi-panel workspace that adapts to available screen real estate.

Pattern Overview

Mobile Layout (<768px)

  • Single Panel Focus: Only one panel visible at a time for maximum clarity
  • Push/Pop Navigation: New views push onto stack, back button pops the stack
  • Breadcrumb Trail: Shows navigation path and allows jumping to any level
  • iOS-Style Transitions: Smooth slide animations with proper timing curves
  • Hardware Back Support: Browser back button integrates with navigation stack
  • Deep Linking: URLs reflect current stack state for bookmarking
  • Width Protection: Panels maintain 100% width to prevent layout issues

Responsive Panel Management

  • Tablet (768px-1024px): Intelligent 2-panel layout with priority system
  • Desktop (1024px+): Multi-panel layout with flexible main content area
  • Panel Priority: Profile > Thread > Messages when space is constrained
  • Flexible Sizing: Messages panel expands/contracts based on available space
  • Smooth Transitions: Panels animate in/out with max-width transitions

Key Benefits

  • Familiar iOS Pattern: Users understand push/pop navigation from mobile apps
  • Hierarchical Content: Perfect for deep content structures with clear relationships
  • Intelligent Responsive: Automatically adapts to screen width with priority system
  • Flexible Panel Sizing: Main content area expands/contracts based on available space
  • Breadcrumb Navigation: Clear path visualization and quick level jumping
  • URL Synchronization: Deep linking and browser history integration
  • Efficient Desktop Use: Multiple panels visible simultaneously on wide screens
  • Production Ready: Robust state management with mobile width protection

Responsive Panel Priority System

Panel Hierarchy and Sizing

Panel Width Tablet Behavior Desktop Behavior
Channels 320px (fixed) Always visible Always visible
Messages 320px-768px (flexible) Hidden when thread/profile open Flexible, expands when others close
Thread 384px (fixed, wider) Takes main space when open Fixed width, can coexist
Profile 320px (fixed) Takes main space, hides thread Fixed width, rightmost position

Tablet Priority Logic

Two-Panel Maximum on Tablet

  • Default: Channels + Messages
  • Thread Open: Channels + Thread (Messages hidden)
  • Profile Open: Channels + Profile (Messages and Thread hidden)
  • Priority Order: Profile > Thread > Messages

Implementation Architecture

CSS Variables for Maintainability

:root {
  --channels-panel-width: 20rem;       /* 320px channels panel */
  --thread-panel-width: 24rem;         /* 384px thread panel (wider) */
  --profile-panel-width: 20rem;        /* 320px profile panel */
  --main-content-min-width: 20rem;     /* 320px minimum for main content */
  --main-content-max-width: 48rem;     /* 768px maximum for main content */
  --header-height: 4rem;               /* 64px header height */
  --stack-slide-duration: 350ms;       /* Stack transition timing */
  --stack-slide-easing: cubic-bezier(0.4, 0, 0.2, 1); /* Material motion */
  --panel-transition: max-width 0.3s ease-out, opacity 0.3s ease-out;
}

Flexible Panel Sizing System

/* Channels panel - fixed width */
.stack-panel[data-priority="1"] {
  width: var(--channels-panel-width);
  min-width: var(--channels-panel-width);
  max-width: var(--channels-panel-width);
}

/* Messages panel - flexible width */
.stack-panel[data-priority="2"] {
  flex: 1;
  min-width: var(--main-content-min-width);
  max-width: var(--main-content-max-width);
}

/* Thread panel - wider fixed width */
.stack-panel[data-priority="3"] {
  width: var(--thread-panel-width);
  min-width: var(--thread-panel-width);
  max-width: var(--thread-panel-width);
}

Panel Visibility Management

/* Panel visibility states - using max-width for smooth animations */
.stack-panel.panel-hidden {
  max-width: 0 !important;
  min-width: 0 !important;
  opacity: 0;
  overflow: hidden;
  border-right: none;
}

.stack-panel.panel-visible {
  opacity: 1;
}

Tablet Mode Management

/* Tablet-specific rules: Max 2 panels visible */
@media (min-width: 768px) and (max-width: 1023px) {
  /* Thread mode: hide messages, expand thread */
  .tablet-thread-mode .stack-panel[data-priority="2"] {
    max-width: 0 !important;
    opacity: 0;
  }
  
  .tablet-thread-mode .stack-panel[data-priority="3"] {
    flex: 1;
    width: auto !important;
    max-width: none !important;
  }
  
  /* Profile mode: hide messages and thread, expand profile */
  .tablet-profile-mode .stack-panel[data-priority="2"],
  .tablet-profile-mode .stack-panel[data-priority="3"] {
    max-width: 0 !important;
    opacity: 0;
  }
  
  .tablet-profile-mode .stack-panel[data-priority="4"] {
    flex: 1;
    width: auto !important;
    max-width: none !important;
  }
}

Mobile Width Protection

/* Mobile width protection - prevent 0 width issues */
@media (max-width: 767px) {
  .stack-panel {
    min-width: 100% !important;
    max-width: 100% !important;
    width: 100% !important;
  }
}

// JavaScript mobile protection
function updateMobilePanelVisibility() {
  const currentPanelId = navigationStack[navigationStack.length - 1];
  
  Object.keys(panels).forEach(panelId => {
    const panel = panels[panelId];
    if (panelId === currentPanelId) {
      // Ensure full width on mobile
      panel.style.minWidth = '100%';
      panel.style.maxWidth = '100%';
      panel.style.width = '100%';
    }
  });
}

Advanced State Management

Responsive Panel Logic

function updateTabletModeManagement() {
  const width = window.innerWidth;
  const container = document.querySelector('.stack-container');
  
  if (width >= 768 && width < 1024) {
    // Tablet mode: Only 2 panels max
    const threadVisible = threadPanel.classList.contains('panel-visible');
    const profileVisible = profilePanel.classList.contains('panel-visible');
    
    if (profileVisible) {
      // Profile mode takes priority
      container.classList.add('tablet-profile-mode');
      hidePanel('panel-messages');
      hidePanel('panel-thread');
    } else if (threadVisible) {
      // Thread mode
      container.classList.add('tablet-thread-mode');
      hidePanel('panel-messages');
    } else {
      // Normal mode - show messages
      container.classList.remove('tablet-thread-mode', 'tablet-profile-mode');
      showPanel('panel-messages');
    }
  }
}

Panel Show/Hide Functions

function showPanel(panelId) {
  const panel = panels[panelId];
  if (panel && window.innerWidth >= 768) {
    panel.classList.remove('panel-hidden');
    panel.classList.add('panel-visible');
  }
}

function hidePanel(panelId) {
  const panel = panels[panelId];
  if (panel && window.innerWidth >= 768) {
    panel.classList.remove('panel-visible');
    panel.classList.add('panel-hidden');
  }
}

function closePanel(panelId) {
  if (window.innerWidth < 768) {
    // Mobile: Pop from stack
    popFromStack();
  } else {
    // Desktop/Tablet: Hide the specific panel
    hidePanel(panelId);
    updateTabletModeManagement();
  }
}

Breadcrumb Navigation System

Dynamic Breadcrumb Generation

function updateBreadcrumb() {
  const breadcrumbContent = document.getElementById('breadcrumb-content');
  const breadcrumbNav = document.getElementById('breadcrumb-nav');
  
  if (window.innerWidth >= 768 || navigationStack.length <= 1) {
    breadcrumbNav.classList.add('hidden');
    return;
  }
  
  breadcrumbNav.classList.remove('hidden');
  breadcrumbContent.innerHTML = '';
  
  navigationStack.forEach((panelId, index) => {
    if (index > 0) {
      const separator = document.createElement('span');
      separator.className = 'breadcrumb-separator';
      separator.textContent = '/';
      breadcrumbContent.appendChild(separator);
    }
    
    const item = document.createElement('button');
    if (index === navigationStack.length - 1) {
      item.className = 'breadcrumb-current';
    } else {
      item.className = 'breadcrumb-item';
      item.addEventListener('click', () => {
        // Pop to this level
        while (navigationStack.length > index + 1) {
          navigationStack.pop();
        }
        updatePanelVisibility();
        updateBreadcrumb();
        updateURL();
      });
    }
    
    item.textContent = getPanelTitle(panelId);
    breadcrumbContent.appendChild(item);
  });
}

URL Synchronization and Deep Linking

URL Structure

// URL patterns for different stack states
/                           // Channels only
/channel/general            // Channels → Messages (general)
/channel/general/thread     // Channels → Messages → Thread
/dm/alice                   // Channels → Messages (Alice DM)
/dm/alice/thread           // Channels → Messages → Thread (Alice DM)
/profile                   // Channels → Profile

URL Update Implementation

function updateURL() {
  const path = navigationStack.map(panelId => {
    switch (panelId) {
      case 'panel-channels': return '';
      case 'panel-messages': 
        return currentUser ? `dm/${currentUser}` : `channel/${currentChannel}`;
      case 'panel-thread': return 'thread';
      case 'panel-profile': return 'profile';
      default: return '';
    }
  }).filter(Boolean).join('/');
  
  const newPath = path ? `/${path}` : '/';
  history.pushState({ stack: [...navigationStack] }, '', newPath);
}

// Handle browser back/forward
window.addEventListener('popstate', (e) => {
  if (e.state && e.state.stack) {
    navigationStack.length = 0;
    navigationStack.push(...e.state.stack);
    updatePanelVisibility();
    updateBreadcrumb();
  }
});

Performance Optimization

Animation Performance

  • Hardware Acceleration: Use transform and opacity for all animations
  • Max-Width Transitions: Smooth panel show/hide with max-width instead of width
  • Material Motion: Cubic-bezier timing curves for natural feel
  • Layer Optimization: Proper z-index management for smooth transitions
  • Frame Rate: 60fps animations with optimized timing

Memory Management

  • Panel Lifecycle: Efficient creation and destruction of panel content
  • Event Cleanup: Remove event listeners when panels are destroyed
  • State Preservation: Maintain scroll position and form state across navigation
  • Resize Handling: Efficient window resize event handling with debouncing

Accessibility Implementation

  • Keyboard Navigation: Tab order follows stack hierarchy
  • Focus Management: Focus moves to appropriate elements on navigation
  • Screen Reader Support: Announce navigation changes and stack updates
  • ARIA Landmarks: Proper region labeling for each panel
  • Reduced Motion: Respect prefers-reduced-motion setting
  • Touch Targets: Minimum 44px touch targets for all interactive elements
  • Color Independence: Don't rely solely on color for navigation state
  • Escape Key: Close rightmost panel or pop stack on Escape

Customization Guide

Adjusting Panel Widths

Simply update the CSS variables:

:root {
  --channels-panel-width: 24rem;  /* 384px instead of 320px */
  --thread-panel-width: 28rem;    /* 448px instead of 384px */
}

This automatically updates all panel widths across all breakpoints.

Modifying Transition Timing

Adjust animation timing and easing:

:root {
  --stack-slide-duration: 300ms;  /* Faster transitions */
  --stack-slide-easing: ease-out; /* Different easing curve */
  --panel-transition: max-width 0.2s ease-in-out, opacity 0.2s ease-in-out;
}

Adding Custom Panels

Extend the stack system:

<!-- Add new panel with priority 5 -->
<div class="stack-panel panel-hidden" id="panel-settings" data-priority="5">
  <!-- Panel content -->
</div>

/* CSS for new panel */
.stack-panel[data-priority="5"] {
  width: var(--channels-panel-width);
  min-width: var(--channels-panel-width);
  max-width: var(--channels-panel-width);
}

Browser Support and Fallbacks

Feature Modern Browsers Fallback
CSS Transforms Hardware-accelerated sliding Opacity transitions
Max-Width Transitions Smooth panel animations Display none/block
History API Full URL synchronization Hash-based navigation
CSS Variables Dynamic theming Fixed values in CSS
Flexbox Flexible panel layout Float-based layout

Stack Navigation Highlights

  • Production Ready: Robust implementation with mobile width protection
  • Flexible Panel Sizing: Main content expands/contracts based on available space
  • Intelligent Priority System: Profile > Thread > Messages on constrained screens
  • Smooth Animations: Max-width transitions for seamless panel show/hide
  • iOS-Style Navigation: Familiar push/pop pattern with smooth transitions
  • Breadcrumb System: Visual navigation path with quick level jumping
  • URL Synchronization: Deep linking and browser history integration
  • Multi-Panel Desktop: Efficient use of wide screens with automatic panel revelation

When to Choose Stack Navigation

Ideal Use Cases

  • • Deep hierarchical content structures
  • • iOS-style mobile applications
  • • Clear parent-child content relationships
  • • Applications requiring breadcrumb navigation
  • • Multi-level drill-down interfaces
  • • Wide screen optimization needed
  • • Flexible panel sizing requirements

Consider Alternatives When

  • • Flat information architecture
  • • Equal-priority content sections
  • • Simple two-level navigation
  • • Desktop-first application design
  • • Limited screen real estate
  • • Complex multi-dimensional navigation

The stack navigation pattern excels in applications with deep hierarchical content where users need to drill down through multiple levels while maintaining context. Its intelligent responsive behavior automatically adapts to screen width, revealing more stack levels on wider screens for efficient multi-panel workflows. The flexible panel sizing system ensures optimal use of available space, while the robust state management and URL synchronization make it ideal for complex applications requiring bookmarkable deep links and browser history integration.