Excessibility. LiveViewRules. Rules. RevealWithoutAnnouncement
(Excessibility v0.15.3)
Copy Markdown
View Source
Flags initially hidden containers that are revealed from the server without exposing that reveal to assistive technology.
A common LiveView pattern un-hides a container without any page
navigation or focus change — e.g. push_event(socket, "js-exec", %{to: "#cap-reached-banner", attr: "data-show"}) executing a JS.show
command stored on the element, or a sibling phx-click={JS.show(to: "#banner")}. Visually the banner appears; for a screen-reader user
nothing happens, because at rest the element is hidden and carries no
role="alert" / role="status" / aria-live. axe-core cannot catch
this — the element is hidden when it scans.
What's flagged
An element for which all of the following hold:
- It is initially hidden — a
hiddenclass, inlinedisplay:none, or thehiddenattribute. - It is a reveal target, detectable statically as either a
data-*attribute on the element whose value is a serializedJS.show/JS.togglecommand (thejs-execidiom), or another element'sphx-*attribute carrying ashow/toggleop whosetotargets this element's id. - It exposes no announcement — no
role="alert",role="status",role="log", noaria-live, and no ancestor providing one.
Reveal targets that are dialogs (role="dialog" / aria-modal) are
skipped: dialogs have their own correct focus-management pattern.
Fix
<!-- Bad -->
<div id="banner" class="hidden" data-show={JS.show(display: "flex")}>
You've reached your cap.
</div>
<!-- Good: informational transition -->
<div id="banner" class="hidden" role="status" data-show={JS.show(display: "flex")}>
You've reached your cap.
</div>