Files
Charon/docs/implementation/sidebar-fixed-header-ui-SPEC.md
akanealw eec8c28fb3
Some checks are pending
Go Benchmark / Performance Regression Check (push) Waiting to run
Cerberus Integration / Cerberus Security Stack Integration (push) Waiting to run
Upload Coverage to Codecov / Backend Codecov Upload (push) Waiting to run
Upload Coverage to Codecov / Frontend Codecov Upload (push) Waiting to run
CodeQL - Analyze / CodeQL analysis (go) (push) Waiting to run
CodeQL - Analyze / CodeQL analysis (javascript-typescript) (push) Waiting to run
CrowdSec Integration / CrowdSec Bouncer Integration (push) Waiting to run
Docker Build, Publish & Test / build-and-push (push) Waiting to run
Docker Build, Publish & Test / Security Scan PR Image (push) Blocked by required conditions
Quality Checks / Auth Route Protection Contract (push) Waiting to run
Quality Checks / Codecov Trigger/Comment Parity Guard (push) Waiting to run
Quality Checks / Backend (Go) (push) Waiting to run
Quality Checks / Frontend (React) (push) Waiting to run
Rate Limit integration / Rate Limiting Integration (push) Waiting to run
Security Scan (PR) / Trivy Binary Scan (push) Waiting to run
Supply Chain Verification (PR) / Verify Supply Chain (push) Waiting to run
WAF integration / Coraza WAF Integration (push) Waiting to run
changed perms
2026-04-22 18:19:14 +00:00

19 KiB
Executable File

UI/UX Improvements: Scrollable Sidebar & Fixed Header - Implementation Specification

Status: Planning Complete Created: 2025-12-21 Type: Frontend Enhancement Branch: feature/sidebar-scroll-and-fixed-header


Executive Summary

This specification provides a comprehensive implementation plan for two critical UI/UX improvements to the Charon frontend:

  1. Sidebar Menu Scrollable Area: Make the sidebar navigation area scrollable to prevent the logout section from being pushed off-screen when submenus are expanded
  2. Fixed Header Bar: Make the desktop header bar static/fixed so it remains visible when scrolling the main content area

Current Implementation Analysis

Component Structure

1. Layout Component (/projects/Charon/frontend/src/components/Layout.tsx)

The Layout component is the main container that orchestrates the entire application layout. It contains:

  • Mobile Header (lines 127-143): Fixed header for mobile viewports (lg:hidden)
  • Sidebar (lines 127-322): Navigation sidebar with logo, menu items, and logout section
  • Main Content Area (lines 336-361): Contains the desktop header and page content

2. Sidebar Structure

The sidebar has the following structure:

<aside className="... flex flex-col ...">
  {/* Logo Section */}
  <div className="h-20 flex items-center ...">
    {/* Logo/Banner */}
  </div>

  {/* Menu Container */}
  <div className="flex flex-col flex-1 px-4 mt-16 lg:mt-6">
    {/* Navigation Menu */}
    <nav className="flex-1 space-y-1">
      {/* Menu items */}
    </nav>

    {/* Version & Logout Section */}
    <div className="mt-2 border-t ...">
      {/* Version info and logout button */}
    </div>
  </div>
</aside>

Current Issues:

  • Line 145: flex flex-col flex-1 on the menu container allows it to grow indefinitely
  • Line 146: <nav className="flex-1"> also uses flex-1, causing the navigation to expand and push the logout section down
  • No overflow control or max-height constraints
  • When submenus expand, they push the logout button and version info off the visible area

3. Header Structure

Desktop header (lines 337-361):

<header className="hidden lg:flex items-center justify-between px-8 h-20 bg-white dark:bg-dark-sidebar border-b ...">
  {/* Left section with collapse button */}
  {/* Center section (empty) */}
  {/* Right section with user info, system status, notifications, theme toggle */}
</header>

Current Issues:

  • The header is part of the main content's flex column
  • No position: fixed or sticky positioning
  • Scrolls away with the content area
  • Line 336: <main> has overflow-auto, allowing the entire main section to scroll, including the header

Styling Approach

The application uses:

  • Tailwind CSS for utility-first styling (/projects/Charon/frontend/tailwind.config.js)
  • CSS Custom Properties in /projects/Charon/frontend/src/index.css for design tokens
  • Inline Tailwind classes for component styling (no separate CSS modules)

Implementation Plan

Improvement 1: Scrollable Sidebar Menu

Goal

Create a scrollable middle section in the sidebar between the logo and logout areas, ensuring the logout button remains visible even when submenus are expanded.

Technical Approach

File to Modify: /projects/Charon/frontend/src/components/Layout.tsx

Changes Required:

  1. Logo Section (lines 138-144): Keep fixed at top

    • Already has fixed height (h-20)
    • No changes needed
  2. Menu Container (line 145): Restructure to enable proper flex layout

    • Current: <div className="flex flex-col flex-1 px-4 mt-16 lg:mt-6">
    • New: <div className="flex flex-col flex-1 min-h-0 px-4 mt-16 lg:mt-6">
    • Reasoning: Adding min-h-0 prevents the flex item from exceeding its container
  3. Navigation Section (line 146): Add scrollable overflow

    • Current: <nav className="flex-1 space-y-1">
    • New: <nav className="flex-1 overflow-y-auto space-y-1">
    • Reasoning: overflow-y-auto enables vertical scrolling when content exceeds available space
  4. Version/Logout Section (lines 280-322): Keep fixed at bottom

    • Current: <div className="mt-2 border-t border-gray-200 dark:border-gray-800 pt-4 ..."> (line 280)
    • New: <div className="flex-shrink-0 mt-2 border-t border-gray-200 dark:border-gray-800 pt-4 ..."> (line 280)
    • Reasoning: flex-shrink-0 prevents this section from being compressed when space is tight
  5. Collapsed Logout Section (lines 307-322): Also add shrink prevention

    • Current: <div className="mt-2 border-t border-gray-200 dark:border-gray-800 pt-4 pb-4"> (line 308)
    • New: <div className="flex-shrink-0 mt-2 border-t border-gray-200 dark:border-gray-800 pt-4 pb-4"> (line 308)

CSS Properties Breakdown

Property Purpose Impact
min-h-0 Allows flex item to shrink below content size Enables proper scrolling in flexbox
overflow-y-auto Shows vertical scrollbar when needed Makes navigation scrollable
flex-shrink-0 Prevents element from shrinking Keeps logout section at fixed size

Responsive Considerations

  • Mobile (< 1024px): The sidebar is already in a slide-out panel, but the same scroll behavior will apply
  • Desktop (≥ 1024px): Scrolling will be more noticeable when sidebar is expanded and multiple submenus are open
  • Collapsed Sidebar: When collapsed (isCollapsed === true), only icons are shown, reducing the need for scrolling

Testing Scenarios

  1. Expanded Sidebar with All Submenus Open:

    • Expand Settings submenu (5 items)
    • Expand Tasks submenu (4 items including nested Import submenu)
    • Expand Security submenu (6 items)
    • Verify logout button remains visible and accessible
  2. Collapsed Sidebar:

    • Toggle sidebar to collapsed state
    • Verify collapsed logout button remains visible at bottom
  3. Mobile View:

    • Open mobile sidebar
    • Expand multiple submenus
    • Verify scrolling works and logout is accessible

Improvement 2: Fixed Header Bar

Goal

Make the desktop header bar remain visible at the top of the viewport when scrolling the main content area.

Technical Approach

File to Modify: /projects/Charon/frontend/src/components/Layout.tsx

Changes Required:

  1. Main Content Container (line 336): Remove scrolling from main element

    • Current: <main className={flex-1 min-w-0 overflow-auto pt-16 lg:pt-0 flex flex-col transition-all duration-200 ${isCollapsed ? 'lg:ml-20' : 'lg:ml-64'}}>
    • New: <main className={flex-1 min-w-0 pt-16 lg:pt-0 flex flex-col transition-all duration-200 ${isCollapsed ? 'lg:ml-20' : 'lg:ml-64'}}>
    • Reasoning: Remove overflow-auto to prevent the entire main section from scrolling
  2. Desktop Header (line 337): Make header sticky

    • Current: <header className="hidden lg:flex items-center justify-between px-8 h-20 bg-white dark:bg-dark-sidebar border-b border-gray-200 dark:border-gray-800 relative">
    • New: <header className="hidden lg:flex items-center justify-between px-8 h-20 bg-white dark:bg-dark-sidebar border-b border-gray-200 dark:border-gray-800 sticky top-0 z-10">
    • Reasoning:
      • sticky top-0 makes the header stick to the top of its container
      • z-10 ensures it stays above content when scrolling
      • Remove relative as sticky is the new positioning context
  3. Content Wrapper (line 360): Add scrolling to content area only

    • Current: <div className="p-4 lg:p-8 max-w-7xl mx-auto w-full">
    • New: <div className="flex-1 overflow-y-auto"><div className="p-4 lg:p-8 max-w-7xl mx-auto w-full">
    • Reasoning: Wrap content in a scrollable container that excludes the header
    • Note: Add closing </div> before the closing </main> tag (after line 362)

CSS Properties Breakdown

Property Purpose Impact
position: sticky Keeps element in place within scroll container Header stays visible when scrolling
top-0 Sticks to top edge of viewport Header aligns with top of screen
z-index: 10 Layering order Ensures header appears above content
overflow-y-auto Vertical scrollbar when needed Content scrolls independently

Alternative Approach: Fixed Positioning

If sticky positioning causes issues (rare in modern browsers), use fixed positioning instead:

<header className="hidden lg:fixed lg:left-0 lg:right-0 lg:top-0 lg:flex items-center justify-between px-8 h-20 bg-white dark:bg-dark-sidebar border-b border-gray-200 dark:border-gray-800 z-10" style={{ paddingLeft: isCollapsed ? '5rem' : '16rem' }}>

Trade-offs:

  • fixed removes the element from document flow, requiring manual left padding
  • sticky is simpler and requires no layout adjustments
  • Recommend sticky as the primary solution

Layout Conflicts & Z-Index Considerations

Current Z-Index Values in Layout:

  • Mobile overlay: z-20 (line 330)
  • Notification dropdown: z-20 (line in NotificationCenter.tsx)
  • Sidebar: z-30 (line 132)
  • Mobile header: z-40 (line 127)

Recommended Z-Index Strategy:

  • Desktop header: z-10 (new, ensures it's below sidebar and modals)
  • Sidebar: z-30 (existing, stays above header)
  • Mobile header: z-40 (existing, stays above sidebar on mobile)
  • Dropdowns/Modals: z-50 (standard for dialogs, already used in some components)

No conflicts expected as desktop header (z-10) will be lower than sidebar (z-30) and mobile header (z-40).

Responsive Considerations

  • Mobile (< 1024px):

    • Mobile header is already fixed (fixed top-0)
    • No changes needed for mobile behavior
    • Desktop header is hidden (hidden lg:flex)
  • Desktop (≥ 1024px):

    • New sticky header behavior applies
    • Content scrolls independently
    • Header width automatically adjusts based on sidebar state (isCollapsed)

Testing Scenarios

  1. Desktop Scroll Behavior:

    • Navigate to a page with long content (e.g., Proxy Hosts with many entries)
    • Scroll down the page
    • Verify header remains visible at top
    • Verify sidebar toggle button, notifications, and theme toggle remain accessible
  2. Sidebar Interaction:

    • Toggle sidebar collapse/expand
    • Verify header adjusts smoothly without layout shift
    • Ensure header content remains properly aligned
  3. Content Overflow:

    • Test on various screen heights (small laptop, large monitor)
    • Verify scrollbar appears on content area, not entire viewport
  4. Dropdown Interactions:

    • Open notification center dropdown
    • Verify it appears above header (correct z-index)
    • Scroll content and ensure dropdown stays anchored to header

Implementation Steps

Phase 1: Sidebar Scrollable Area

  1. Backup current state: Create a git branch for this feature

    git checkout -b feature/sidebar-scroll-and-fixed-header
    
  2. Modify Layout.tsx: Apply changes to sidebar structure

    • Line 145: Add min-h-0 to menu container
    • Line 146: Add overflow-y-auto to navigation
    • Line 280: Add flex-shrink-0 to version/logout section
    • Line 308: Add flex-shrink-0 to collapsed logout section
  3. Test in development:

    cd frontend
    npm run dev
    
    • Test all scenarios listed in "Testing Scenarios" section
    • Verify no visual regressions
  4. Browser compatibility check:

    • Chrome/Edge (Chromium)
    • Firefox
    • Safari

Phase 2: Fixed Header Bar

  1. Modify Layout.tsx: Apply changes to header and main content

    • Line 336: Remove overflow-auto from main element
    • Line 337: Add sticky top-0 z-10 to header, remove relative
    • Line 360: Wrap content in scrollable container
  2. Test in development: Verify all scenarios in "Testing Scenarios" section

  3. Cross-browser testing: Ensure sticky positioning works consistently

Phase 3: Integration Testing

  1. Combined behavior:

    • Test both improvements together
    • Verify no layout conflicts
    • Check z-index stacking works correctly
  2. Accessibility testing:

    • Keyboard navigation with scrollable sidebar
    • Screen reader compatibility
    • Focus management when scrolling
  3. Performance check:

    • Monitor for layout thrashing
    • Check for smooth 60fps scrolling
    • Verify no memory leaks with scroll event handlers (none should be needed)

Phase 4: Production Deployment

  1. Create pull request with screenshots/video demonstrating the improvements

  2. Code review checklist:

    • All Tailwind classes are correctly applied
    • No visual regressions on mobile
    • No visual regressions on desktop
    • Z-index stacking is correct
    • Scrolling performance is smooth
    • Accessibility is maintained
  3. Merge and deploy after approval


Potential Issues & Mitigation

Issue 1: Safari Sticky Positioning

Problem: Older Safari versions have inconsistent position: sticky support

Mitigation:

  • Test on Safari 13+ (current support is excellent)
  • If issues arise, fall back to position: fixed approach
  • Use CSS feature detection if needed

Issue 2: Scrollbar Styling

Problem: Default scrollbars may look inconsistent with dark theme

Solution: Add custom scrollbar styles to /projects/Charon/frontend/src/index.css:

/* Custom Scrollbar Styles */
.overflow-y-auto::-webkit-scrollbar {
  width: 8px;
}

.overflow-y-auto::-webkit-scrollbar-track {
  background: transparent;
}

.overflow-y-auto::-webkit-scrollbar-thumb {
  background-color: rgba(148, 163, 184, 0.3);
  border-radius: 4px;
}

.dark .overflow-y-auto::-webkit-scrollbar-thumb {
  background-color: rgba(148, 163, 184, 0.5);
}

.overflow-y-auto::-webkit-scrollbar-thumb:hover {
  background-color: rgba(148, 163, 184, 0.6);
}

Issue 3: Layout Shift on Sidebar Toggle

Problem: Collapsing/expanding sidebar might cause visible layout shift with fixed header

Mitigation:

  • Already handled by Tailwind transitions: transition-all duration-200
  • Existing CSS transitions on line 132 and 336 will smooth the animation
  • No additional work needed

Issue 4: Mobile Header Conflict

Problem: Mobile header is already fixed, might conflict with new desktop header behavior

Mitigation:

  • Mobile header uses lg:hidden (line 127)
  • Desktop header uses hidden lg:flex (line 337)
  • No overlap between the two states
  • Already properly separated by breakpoints

Configuration File Review

.gitignore

Review: No changes needed for CSS/layout updates

  • Already ignores common frontend build artifacts
  • No new files or directories will be created

codecov.yml

Status: File does not exist in repository

  • No changes needed

.dockerignore

Review: No changes needed

  • Layout changes are code modifications, not new files
  • All frontend source files are already properly handled

Dockerfile

Review: No changes needed

  • Layout changes are CSS/JSX modifications
  • Frontend build process remains unchanged
  • Build steps (lines 35-52) compile the app correctly regardless of layout changes

Success Criteria

Sidebar Scrollable Area

  • Logout button always visible at bottom of sidebar
  • Smooth scrolling when menu items overflow
  • No layout jumps or visual glitches
  • Works in collapsed and expanded sidebar states
  • Mobile sidebar behaves correctly

Fixed Header Bar

  • Header remains visible when scrolling content
  • No layout shift or jank during scroll
  • All header buttons remain functional
  • Z-index layering is correct (dropdowns above header)
  • Sidebar toggle properly adjusts header width

Overall

  • No performance degradation
  • Maintains accessibility standards
  • Works across all supported browsers
  • Responsive behavior intact
  • Dark mode styling consistent

File Change Summary

Files to Modify

File Line Numbers Changes
/projects/Charon/frontend/src/components/Layout.tsx 145 Add min-h-0 to menu container
/projects/Charon/frontend/src/components/Layout.tsx 146 Add overflow-y-auto to navigation
/projects/Charon/frontend/src/components/Layout.tsx 280 Add flex-shrink-0 to version/logout section
/projects/Charon/frontend/src/components/Layout.tsx 308 Add flex-shrink-0 to collapsed logout section
/projects/Charon/frontend/src/components/Layout.tsx 336 Remove overflow-auto from main element
/projects/Charon/frontend/src/components/Layout.tsx 337 Add sticky top-0 z-10, remove relative
/projects/Charon/frontend/src/components/Layout.tsx 360-362 Wrap content in scrollable container
/projects/Charon/frontend/src/index.css EOF Optional: Add custom scrollbar styles

Files to Create

None - All changes are modifications to existing files


Timeline Estimate

  • Phase 1 (Sidebar): 2-3 hours (implementation + testing)
  • Phase 2 (Header): 2-3 hours (implementation + testing)
  • Phase 3 (Integration): 2 hours (combined testing + refinements)
  • Phase 4 (Deployment): 1 hour (PR, review, merge)

Total: 7-9 hours


Additional Notes

Design System Considerations

The application already uses a comprehensive design token system (see /projects/Charon/frontend/src/index.css):

  • Spacing tokens (--space-*)
  • Color tokens (--color-*)
  • Transition tokens (--transition-*)

All proposed changes use existing Tailwind utilities that map to these tokens, ensuring consistency.

Future Enhancements

After implementing these improvements, consider:

  1. Sidebar Width Persistence: Store user's preferred sidebar width (collapsed/expanded) in localStorage (already implemented on line 29-33)

  2. Smooth Scroll to Active Item: When a page loads, scroll the sidebar to show the active menu item:

    useEffect(() => {
      const activeElement = document.querySelector('nav a[aria-current="page"]');
      activeElement?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
    }, [location.pathname]);
    
  3. Header Scroll Shadow: Add a subtle shadow when content scrolls beneath header:

    const [isScrolled, setIsScrolled] = useState(false);
    
    useEffect(() => {
      const handleScroll = (e) => {
        setIsScrolled(e.target.scrollTop > 0);
      };
      // Attach to content scroll container
    }, []);
    
    <header className={`... ${isScrolled ? 'shadow-md' : ''}`}>
    

References


Document Version: 1.0 Created: 2025-12-21 Author: GitHub Copilot Status: Ready for Implementation