fix(web,learner): navigation swaps at once; content-only entrance, loading line, focus (R19)

Drops document-level View Transitions and the lesson hold: they dropped clicks for 120-180 ms, froze the screen
while route code loaded, and let the dock act on a stale lesson. Navigation now swaps immediately; only the
content region enters (opacity and a 6 px rise, 160 ms); a 2 px loading line appears when code or data takes over
150 ms; the dock works from the outline and the URL, so a second Next advances again; focus moves to the new h1
and a polite live region announces it. Route chunks of a feature preload together so the sidebar never drops.
Scroll restore stops on user input; pdf.js never starts a load after cleanup.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lucas WintherandClaude Opus 5.5 committed 2026-10-30 21:49:49 +01:00
1 parent 305c262ff1
commit b5e00af4f7
18 files changed
+231 -275

No files matched your search

+9 -10
View File
@@ -43,21 +43,20 @@ test("a hash scrolls its target into view", () => {
expect(env.y).toBe(0);
});
test("defer() keeps the old entry until settle(), which then applies the route scroll", () => {
test("user input stops a restore that is still waiting for the page to grow", () => {
const env = fakeEnv();
const k = createScrollKeeper(env);
k.routeChanged("a", false);
env.y = 500; k.onScroll();
k.defer();
env.y = 2000; k.onScroll();
k.routeChanged("b", false);
expect(env.y).toBe(500); // the old content is still on screen
env.y = 520; k.onScroll(); // scrolling the old content still belongs to "a"
k.settle();
expect(env.y).toBe(0);
env.max = 300;
k.routeChanged("a", true);
expect(env.y).toBe(520);
k.settle(); // nothing pending: no-op
expect(env.y).toBe(520);
expect(env.y).toBe(300);
k.userInput(); // the user starts scrolling
env.y = 120; k.onScroll();
env.max = 5000; env.flush();
expect(env.y).toBe(120);
k.userInput(); // no restore pending: harmless
});
test("a restore beyond the current page height retries until the page is tall enough", () => {