Skip to content

[web] Navigating shows the previous view's figures under the new view's nav highlight — the loading signal is assistive-only #754

Description

@eaitbrahim

Found while testing v0.14.0.

What happens

Navigating between views, the nav highlight moves immediately and the content does not. Until the read returns, the page shows the previous view's numbers underneath the new view's nav state.

show() (keel/web/static/js/main.js:604) does, in order:

  1. sets document.title to the new route
  2. sets aria-current="page" on the new nav link
  3. sets aria-busy="true" on #content
  4. calls paint(route, true)

paint awaits the fetch and only then calls rebuildInto, which replaceChildrens the view. So between steps 2 and the fetch resolving, every sighted signal says "you are on Balances" while the DOM still holds Positions' rows.

This is not merely a missing spinner. For that window the page makes a false claim: a stale figure under a fresh label. That is the class of thing the payload contract exists to prevent everywhere else — _session_banner refuses to render at all rather than assert a mode it cannot verify, and the balances tiles carry an as-of stamp precisely so a held figure cannot read as a current one.

The accessible half is already correct

aria-busy is toggled properly — "true" in show() (main.js:617), "false" at the end of paint() (main.js:521), and index.html:301 ships the initial aria-busy="true". A screen reader is already told the region is busy.

Nothing styles it. There is no [aria-busy="true"] rule anywhere in keel/web/static/css/. So the work is mostly the visual half of a signal that is already wired, correctly, in one place.

Why it is worse on the slow reads, which are the honest ones

The views most likely to lag are the ones that read the most: Activity (the whole audit_events chain verifies on every read — chain_state's docstring measures ~135 ms for 20,000 events and explains why it refuses to cache the verdict), and the Research Hub views. Those are exactly the pages where a stale-figure-under-fresh-label window is least acceptable.

Suggested shape

  • A CSS rule on #content[aria-busy="true"] — dim, or a determinate-free progress affordance. No new attribute; the hook exists.
  • Consider whether the content should be cleared or visibly staled on route change rather than left asserting the old view's numbers. Dimming is the cheap fix; not showing the previous route's data under the new route's label is the correct one.
  • Respect prefers-reduced-motion for anything animated.
  • The poll path must not flash it. show()'s interval calls paint(route, !live) every POLL_MS and the EventSource path repaints on revision changes — a busy indicator firing four times a minute on a page nobody navigated would be worse than none. Only a route change should show it, which means the indicator cannot simply key off aria-busy as currently toggled.

That last point is the design question, and it is the reason this is not a one-line CSS change: aria-busy today marks any read, and the indicator wants navigation reads only.

Acceptance

  • Changing route gives a sighted user an immediate, visible signal that data is loading
  • The previous route's figures are never shown under the new route's nav highlight
  • A background poll or an SSE-driven repaint produces no visible flash
  • prefers-reduced-motion honoured
  • aria-busy semantics unchanged for assistive tech
  • tests/commands/test_console_thinness.py still passes — this is rendering, so it stays in keel/web/, and no derivation enters render.js

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions