Excessibility.LiveViewRules.Rules.RevealWithoutAnnouncement (Excessibility v0.18.1)

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:

  1. It is initially hidden — a hidden class, inline display:none, or the hidden attribute.
  2. It is a reveal target, detectable statically as either a data-* attribute on the element whose value is a serialized JS.show/JS.toggle command (the js-exec idiom), or another element's phx-* attribute carrying a show/toggle op whose to targets this element's id.
  3. It exposes no announcement — no role="alert", role="status", role="log", no aria-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>