Working… page: each poll replaces the live region itself, so the finished state is not reliably announced #500
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo#500
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found on 2026-09-29 in a read-only review of postulo at
95348908d, reading the errand templates against the vendored htmx 2.0.10 swap code.Checked again on 2026-10-01 against
mainat0a5cad587: still present.The page a person lands on after sending slow work off (#247) promises, in its own comment, that the change from "working" to "done" or "failed" is announced by a live region. But the live region is the very element htmx replaces on every poll, so the announcement depends on how each screen reader treats a live region that is removed and inserted again with its text already in place. Screen readers track changes inside a live region that already exists; one inserted whole is announced inconsistently, and commonly not at all for
role="status".templates/core/partials/errand_state.html:15-20(thediv#errand-statecarriesrole="status" aria-live="polite" aria-atomic="true"and is also its ownhx-targetwithhx-swap="outerHTML");templates/core/errand.html:18includes it with no wrapper.{{ poll_seconds }}seconds htmx'souterHTMLswap inserts the newdivand removes the old one (a new DOM node, not a text change in the existing one). When the errand finishes, the fragment that says "done" or "That did not work." arrives as a fresh live region. Depending on the browser and reader, nothing is spoken; with some combinations the unchanged "Working…" sentence may instead be re-read on each poll.errand.html, wrap the include in<div role="status" aria-live="polite" aria-atomic="true">, and drop those three attributes fromdiv#errand-statein the partial, which keeps its polling attributes and itsouterHTMLswap. The same shape appears where an out-of-band swap (hx-swap-oob="true", which htmx performs asouterHTML) replaces the live element itself:templates/applications/partials/timeline.html:20and the count intemplates/applications/application_list.html:9-10(likewisejobs/listing_list.htmlandjobs/company_list.html); those deserve the same check.Done when
tests/test_errands.pyrenders the errand page and thecore:errand_statefragment and asserts that the element carryingaria-liveis an ancestor of#errand-statein the page and is absent from the polled fragment; it fails today.