shadimage
whiskeyjack
libannouncesource: ok · refreshed

announce

Announce a message to assistive technology through a shared live region.

npx shadcn add "https://whiskeyjack.net/r/announce.json"
free

No selection tags yet — empty means no evidence, not lack of support.

Files1 · source cached 10/8/2026, 6:55:28 AM

lib/announce.ts
/**
 * Announce a message to assistive technology via a shared, visually-hidden
 * ARIA live region. Use for status that has no visible text anchor of its own
 * -- route changes, async results, game-state transitions, timers.
 *
 * Two singleton regions are created lazily on `<body>` (one polite, one
 * assertive) and reused. The text is cleared and re-set on the next frame so
 * repeating the same message still re-announces.
 *
 * (Visible status that lives in a component should instead use that component's
 * own live semantics -- e.g. a `Notice`/`Toast` with an aria-live role.)
 */
export type Politeness = "polite" | "assertive";

function getRegion(politeness: Politeness): HTMLElement {
  const id = `wj-live-${politeness}`;
  let region = document.getElementById(id);
  if (!region) {
    region = document.createElement("div");
    region.id = id;
    region.className = "wj-sr-only";
    region.setAttribute("aria-live", politeness);
    region.setAttribute("aria-atomic", "true");
    region.setAttribute("role", politeness === "assertive" ? "alert" : "status");
    document.body.appendChild(region);
  }
  return region;
}

export function announce(message: string, politeness: Politeness = "polite"): void {
  if (typeof document === "undefined") return;
  const region = getRegion(politeness);
  region.textContent = "";
  // Re-set on the next frame so an identical consecutive message re-announces.
  requestAnimationFrame(() => {
    region.textContent = message;
  });
}

More from whiskeyjack

Browse all components in this library →