Files
Charon/docs/plans/comprehensive_modal_fix_spec.md
Jeremy dd96493edb fix: resolve modal dropdown z-index conflicts across application
Restructure 7 modal components to use 3-layer architecture preventing
native select dropdown menus from being blocked by modal overlays.

Components fixed:
- ProxyHostForm: ACL selector and Security Headers dropdowns
- User management: Role and permission mode selection
- Uptime monitors: Monitor type selection (HTTP/TCP)
- Remote servers: Provider selection dropdown
- CrowdSec: IP ban duration selection

The fix separates modal background overlay (z-40) from form container
(z-50) and enables pointer events only on form content, allowing
native dropdown menus to render above all modal layers.

Resolves user inability to select security policies, user roles,
monitor types, and other critical configuration options through
the UI interface.
2026-02-04 19:23:35 +00:00

7.4 KiB

Comprehensive Modal Z-Index Fix Plan

Date: 2026-02-04
Issue: Widespread modal overlay z-index pattern breaking dropdown interactions
Scope: 11 modal components across the application
Fix Strategy: Unified 3-layer modal restructuring


Executive Summary

Multiple modal components throughout the application use the same problematic pattern:

<div className="fixed inset-0 bg-black/50 ... z-50">
  {/* Form with dropdowns inside */}
</div>

This pattern creates a z-index stacking context that blocks native HTML <select> dropdown menus from rendering properly, making them unclickable.


Affected Components by Priority

P0 - CRITICAL: Modals with SELECT Dropdowns (Completely Broken)

Component File Line Dropdowns Impact
ProxyHostForm frontend/src/components/ProxyHostForm.tsx 514 ACL selector, Security Headers CRITICAL: Users cannot assign security policies
EditMonitorModal frontend/src/pages/Uptime.tsx 230 Monitor type (HTTP/TCP) HIGH: Users cannot edit monitor configuration
CreateMonitorModal frontend/src/pages/Uptime.tsx 339 Monitor type (HTTP/TCP) HIGH: Users cannot create new monitors
InviteUserModal frontend/src/pages/UsersPage.tsx 171 Role, Permission mode HIGH: Admin cannot invite users with roles
EditPermissionsModal frontend/src/pages/UsersPage.tsx 434 Permission mode, Allowed/Blocked hosts HIGH: Admin cannot modify user permissions
BanIPModal frontend/src/pages/CrowdSecConfig.tsx 1175 Ban duration MEDIUM: Admin cannot set custom ban durations
RemoteServerForm frontend/src/components/RemoteServerForm.tsx 69 Provider (Generic/Docker/K8s) MEDIUM: Users cannot add remote servers

P1 - HIGH: Modals with Other Interactive Elements

Component File Line Elements Impact
PasswordPromptModal frontend/src/pages/Account.tsx 473 Password input, buttons LOW: Simple inputs work
EmailConfirmModal frontend/src/pages/Account.tsx 523 Buttons only NONE: No form inputs

P2 - MEDIUM: Modal Pattern Analysis Required

Component File Line Status Impact
ConfirmDialog frontend/src/pages/WafConfig.tsx 72 Buttons only NONE: No form inputs
SecurityNotificationModal frontend/src/components/SecurityNotificationSettingsModal.tsx 58 TBD - Need analysis UNKNOWN
ImportSitesModal frontend/src/components/ImportSitesModal.tsx 75 TBD - Need analysis UNKNOWN
CertificateCleanupDialog frontend/src/components/dialogs/CertificateCleanupDialog.tsx 27 Buttons only NONE: No form inputs
ImportSuccessModal frontend/src/components/dialogs/ImportSuccessModal.tsx 30 Display only NONE: No form inputs

Unified Fix Strategy

Solution: 3-Layer Modal Architecture

Replace the problematic single-layer pattern:

// ❌ BROKEN: Single layer blocks dropdown menus
<div className="fixed inset-0 bg-black/50 flex items-center justify-center p-4 z-50">
  <form>
    <select> {/* BROKEN: Can't click */} </select>
  </form>
</div>

With the 3-layer pattern:

// ✅ FIXED: Separate layers for proper z-index stacking
<>
  {/* Layer 1: Background overlay (z-40) */}
  <div className="fixed inset-0 bg-black/50 z-40" onClick={onCancel} />
  
  {/* Layer 2: Form container (z-50, pointer-events-none) */}
  <div className="fixed inset-0 flex items-center justify-center p-4 pointer-events-none z-50">
    
    {/* Layer 3: Form content (pointer-events-auto) */}
    <div className="... pointer-events-auto">
      <form className="pointer-events-auto">
        <select> {/* WORKS: Dropdown renders above all layers */} </select>
      </form>
    </div>
  </div>
</>

Implementation Plan

Phase 1: P0 Critical Components (4-6 hours)

Priority Order (most business-critical first):

  1. ProxyHostForm.tsx (30 min) - Security policy assignment
  2. UsersPage.tsx - InviteUserModal (20 min) - User management
  3. UsersPage.tsx - EditPermissionsModal (30 min) - Permission management
  4. Uptime.tsx - Both modals (45 min) - Monitor management
  5. RemoteServerForm.tsx (20 min) - Infrastructure management
  6. CrowdSecConfig.tsx - BanIPModal (20 min) - Security management

Phase 2: P1 Components (1-2 hours)

Analysis and fix of remaining interactive modals if needed.

Phase 3: Testing & Validation (2-3 hours)

  • Manual testing of all dropdown interactions
  • E2E test updates
  • Cross-browser verification

Total Estimated Time: 7-11 hours


Testing Strategy

Manual Testing Checklist

For each P0 component:

  • Modal opens correctly
  • Background overlay click-to-close works
  • All dropdown menus open and respond to clicks
  • Dropdown options are selectable
  • Form submission works with selected values
  • ESC key closes modal
  • Tab navigation works through form elements

Automated Testing

E2E Tests to Update:

  • tests/integration/proxy-acl-integration.spec.ts - ProxyHostForm dropdowns
  • tests/security/user-management.spec.ts - UsersPage modals
  • tests/uptime/*.spec.ts - Uptime monitor modals
  • Any tests interacting with the affected modals

Unit Tests:

  • Modal rendering tests should continue to pass
  • Form submission tests should continue to pass

Risk Assessment

Risk Level: LOW-MEDIUM

Mitigating Factors: Non-breaking change (only CSS/DOM structure)
Identical fix pattern across all components
Well-understood solution (already documented in ConfigReloadOverlay)
Only affects modal presentation layer

Risk Areas: ⚠️ Multiple files being modified simultaneously
⚠️ Modal close behavior could be affected
⚠️ CSS specificity or responsive behavior could change

Mitigation Strategy:

  • Fix components one at a time
  • Test each component thoroughly before moving to next
  • Keep changes minimal and focused
  • Maintain existing CSS classes and styling

Success Criteria

  • All P0 modal dropdowns are clickable and functional
  • Modal open/close behavior unchanged
  • Background overlay click-to-close still works
  • ESC key behavior unchanged
  • All existing E2E tests pass
  • No new console errors or warnings
  • Cross-browser compatibility maintained (Chrome, Firefox, Safari, Edge)

Implementation Notes

CSS Classes to Add:

  • pointer-events-none on form container layers
  • pointer-events-auto on form content elements

CSS Classes to Modify:

  • Change overlay z-index from z-50 to z-40
  • Keep form container at z-50

Accessibility:

  • Maintain role="dialog" and aria-modal="true" attributes
  • Ensure Tab navigation still works correctly
  • Preserve ESC key handling

Post-Implementation Actions

  1. Documentation Update: Update modal component patterns in design system docs
  2. Code Review Guidelines: Add z-index modal pattern to code review checklist
  3. Linting Rule: Consider ESLint rule to detect problematic modal patterns
  4. Design System: Create reusable Modal component with correct z-index pattern

This comprehensive fix addresses the root cause across the entire application, preventing future occurrences of the same issue.