feat(nav): dj-navigate keeps aria-current="page" in sync across SPA nav (#1756)#1757
Conversation
…av (#1756) A persistent nav usually lives OUTSIDE [dj-root], so dj-navigate's dj-root-only swap never updated a server-rendered active-link highlight — it stayed on the page you navigated from. The client now re-derives the active link from the URL after each navigation (on dj-navigate click, on the WS mount via bindNavigationDirectives/reinitAfterDOMUpdate, and on popstate), setting aria-current="page" on the matching [dj-navigate] link and removing it from the others. Cross-origin targets are never current; an app-authored aria-current of a different value is left untouched. Apps style a[dj-navigate][aria-current="page"]. Adds tests/js/dj_navigate_aria_current_1756.test.js (4 cases, gate-off proven). document.title + [dj-root] dj-view-attr sync across SPA nav remain in #1756. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Security Review RequiredThis PR modifies security-sensitive files. Please ensure the following areas receive additional review:
Review Checklist
See SECURITY_GUIDELINES.md for full security review criteria. |
Stage-7 Adversarial Review — PR #1757 (feat #1756)Verdict: APPROVE 🟢 dj-navigate auto-syncs Mandatory checks
Logic correctness (
|
Retrospective — PR #1757 (#1756 part 1)Task: make Quality: 5/5Generalizes a real downstream fix into an accessible, zero-per-app-JS framework feature; gate-off-proven; all nav paths traced. What went well
What could be improved
Verified
|
Addresses #1756 (part 1 — the primary, titled ask).
document.titleand the[dj-root]dj-view-attr sync remain tracked there.Problem
A persistent nav usually lives outside
[dj-root].dj-navigateswaps only the[dj-root]subtree, so a server-rendered active-link highlight ({% if slug == active_page %}…) never updates on SPA navigation — it stays on the page you came from. (Reported on djust.org: Examples → another page kept "Examples" highlighted; worked around there with a per-app script.)Fix (framework, generic, accessible)
dj-navigatenow managesaria-current="page"itself. After each navigation it setsaria-current="page"on the[dj-navigate]link whose path matches the current URL and removes it from the others. Three call sites in18-navigation.js:handleLiveRedirect(right afterpushState) — instant feedback on click;bindNavigationDirectives(runs viareinitAfterDOMUpdate) — covers the WS mount + patches + initial load;popstatehandler — back/forward.Apps just style
a[dj-navigate][aria-current="page"]— no per-app JS.Guarantees:
dj-navigatetargets (sister-site links whose pathname is/) are never marked current.aria-currentof a different value (step,true, …) is left untouched — we only manage the"page"value we set.page); section/ancestor highlighting is left to the app.Verification
tests/js/dj_navigate_aria_current_1756.test.js— 4 cases (match-moves-on-nav, cross-origin-never-current, app-value-not-clobbered, function exposed). Gate-off proven: neuteringupdateAriaCurrentfails 3/4.make build-js);client.js+ min/br/gz/map committed.Once released, djust.org can drop its
nav-active.jsworkaround and style[aria-current="page"]instead.🤖 Generated with Claude Code