Problem
webjs scopes a swap to a frame only when the trigger is physically nested inside it (closest-only activeFrameId, router-client.js:437-452): no external nav/sidebar link or filter form can drive a content frame by id, and there is no _top token to break a frame-internal link out to a full page. Separately, issues its fetch (router-client.js:1192) with zero busy signal (webjs-frame.js:53-59 is a bare anchor): no aria-busy, no CSS hook, no event, so a slow link leaves the frame stale with nothing assistive tech can announce.
Design / approach
Resolve a native data-webjs-frame attribute (incl. a reserved _top) via document.getElementById so external triggers drive a frame, and toggle the native aria-busy attribute on the frame around the fetch. Standard data-* attribute and ARIA, matching the existing data-prefetch convention.
Web-standards fit: Uses a standard data-* attribute, getElementById, and the native aria-busy attribute; no frame-targeting abstraction is added.
Prior art: Turbo data-turbo-frame (incl. _top) via getElementById; Turbo markAsBusy/clearBusyState and requestStarted/requestFinished aria-busy.
Acceptance criteria
Filed from the production-readiness audit (webjs vs Next.js / Remix / Rails / Turbo / Lit). Theme: frames. Priority: P2. Kept to webjs identity: no-build, progressive enhancement, web-components-first, AI-first, batteries-included, close to web standards.
Problem
webjs scopes a swap to a frame only when the trigger is physically nested inside it (closest-only activeFrameId, router-client.js:437-452): no external nav/sidebar link or filter form can drive a content frame by id, and there is no _top token to break a frame-internal link out to a full page. Separately, issues its fetch (router-client.js:1192) with zero busy signal (webjs-frame.js:53-59 is a bare anchor): no aria-busy, no CSS hook, no event, so a slow link leaves the frame stale with nothing assistive tech can announce.
Design / approach
Resolve a native data-webjs-frame attribute (incl. a reserved _top) via document.getElementById so external triggers drive a frame, and toggle the native aria-busy attribute on the frame around the fetch. Standard data-* attribute and ARIA, matching the existing data-prefetch convention.
Web-standards fit: Uses a standard data-* attribute, getElementById, and the native aria-busy attribute; no frame-targeting abstraction is added.
Prior art: Turbo data-turbo-frame (incl. _top) via getElementById; Turbo markAsBusy/clearBusyState and requestStarted/requestFinished aria-busy.
Acceptance criteria
Filed from the production-readiness audit (webjs vs Next.js / Remix / Rails / Turbo / Lit). Theme: frames. Priority: P2. Kept to webjs identity: no-build, progressive enhancement, web-components-first, AI-first, batteries-included, close to web standards.