# AJAX Navigation Plan

## Goal

Move from full-page navigation to AJAX-driven main-content navigation in controlled phases, while keeping stability, accessibility, and upgrade safety.

## Approach Summary

- Start with a minimal pilot on a small route subset.
- Keep existing server-rendered pages as source of truth during rollout.
- Add a thin client navigation layer that swaps only the main content container.
- Build explicit lifecycle hooks so page-specific JS can initialize and dispose safely.
- Expand route coverage only after passing test gates.

## Why Slow Rollout

AJAX navigation can improve UX, but common failures are subtle and expensive:

- Event listeners duplicate after repeated swaps.
- Browser back/forward can desync URL and content.
- Focus and screen-reader behavior can regress.
- Failed network requests can leave partial UI state.
- Legacy page scripts may assume full reload semantics.

A staged rollout avoids broad regressions and creates reusable patterns.

## Target Scope

Inventory reference:

- Full page-by-page scope is documented in AJAX-PAGE-INVENTORY.md.
- Current grouped counts: Wave 1 = 4 pages, Wave 2 = 36 pages, Wave 3 = 34 pages (74 total HTML pages).

Phase 1 target:

- Intercept internal nav clicks for a small route set (example: dashboard <-> mailbox pages).
- Fetch destination page HTML.
- Extract and replace only main content node.
- Update URL via history API.
- Re-run a controlled page init lifecycle.

Out of scope in Phase 1:

- Global conversion of all pages.
- Aggressive prefetching.
- Complex transitions/animations.
- Full script hot-swapping.

## Architecture Draft

### 1) Stable Shell Contract

Keep shell static:

- Header
- Sidebar
- Footer
- Shared scripts

Swap region only:

- Main content container (single canonical selector)

### 2) Navigation Runtime Contract

Define events/hooks:

- ajax:beforeNavigate
- ajax:contentReplaced
- ajax:pageInit
- ajax:pageDispose
- ajax:navigateError

Each feature must register init/dispose through one registry.

### 3) Script Lifecycle Registry

Use one registry file to avoid duplicated listeners:

- registerPageHook(routePattern, { init, dispose })
- dispose runs before content replacement
- init runs after content replacement

### 4) History and URL Management

- pushState on successful internal navigation
- popstate handler re-renders content for browser back/forward
- preserve query/hash
- ignore external links and modified-click behavior (cmd/ctrl, new tab)

### 5) Accessibility Contract

After each content swap:

- Focus main heading or main region
- Announce page change in aria-live region
- Preserve skip-link targets
- Maintain document title consistency

## Risk Register and Test Coverage

### Risk 1: Page-specific JS not re-initialized or double-bound

Tests:

- Navigate A -> B -> A 5 times and verify no duplicate click behavior.
- Confirm dropdowns/treeview/card actions still work after each swap.
- Check listener count for critical handlers in dev diagnostics.

### Risk 2: Broken back/forward behavior

Tests:

- Deep-link load -> navigate -> back -> forward sequence.
- Verify URL, title, active menu state, and main content remain in sync.
- Verify scroll restoration policy is consistent.

### Risk 3: Accessibility regression

Tests:

- Keyboard-only navigation through route changes.
- Verify focus lands on expected heading/main region after swap.
- Verify screen-reader announcement text in live region.

### Risk 4: Network failures and race conditions

Tests:

- Simulate slow network and multiple rapid clicks.
- Ensure stale response does not overwrite newer navigation.
- Verify fallback behavior on HTTP 4xx/5xx/timeouts.

### Risk 5: Drift from static page behavior

Tests:

- Compare rendered main content from full reload vs AJAX path.
- Validate key widgets/components parity on pilot routes.

## Minimal Implementation Checklist (Pilot)

1. Choose one canonical main content selector and enforce it on pilot pages.
2. Add click interception for same-origin internal links only.
3. Fetch destination HTML and parse document.
4. Replace main content node only.
5. Update title and history state.
6. Emit lifecycle events in fixed order.
7. Add init/dispose hooks for existing page scripts used by pilot pages.
8. Add basic loading and error UI states.
9. Add race protection (AbortController + request token check).
10. Add accessibility focus/announcement handling.

## Definition of Done (Pilot)

- No console errors in repeated navigation loops.
- Sidebar/menu/dropdown behavior remains stable after swaps.
- Back/forward works reliably.
- Focus behavior passes keyboard smoke tests.
- Failed fetch gracefully falls back (error notice or hard navigation).

## Gradual Expansion Plan

- Wave 1: dashboard + mailbox pages.
- Wave 2: forms + tables pages.
- Wave 3: remaining sections.

Gate to move between waves:

- Pilot checklist fully passes.
- No high-severity regressions in manual smoke tests.
- Lifecycle hooks exist for every page-specific behavior in wave.

## Full Implementation End State

- Most internal links use AJAX navigation layer.
- Shared shell is fully modular and centrally managed.
- All page scripts are lifecycle-aware (init/dispose).
- Accessibility behavior remains equivalent or better than full reload.
- Hard navigation fallback remains available for safe rollback.

## Rollback Strategy

- Feature flag for AJAX navigation runtime.
- If disabled, links revert to standard full-page behavior.
- Keep static page rendering untouched during migration.

## Open Decisions Before Coding

1. Canonical selector for main content container across all target pages.
2. Initial pilot route set (recommend: dashboard + mailbox).
3. Error fallback policy:
   - Inline error and stay on current page
   - Automatic hard navigation retry
4. Scroll policy after content swap:
   - Top of page
   - Preserve previous scroll for back/forward only

## Suggested Next Step

Create a tiny technical spec for the pilot runtime API (function signatures, event payloads, and route hook registration), then implement behind a feature flag.
