RZREDZ.UI

Busy

REDZ.ui.busy(element, promise) marks a button or a region busy until the promise settles: a button shows a spinner and ignores clicks, a region turns inert under an overlay.

Examples

A region - a card, its body, a dialog's or a drawer's body, any region - keeps its old content under a dimmed overlay while the new comes. The overlay shows only after 400 ms, so a quick answer does not flash; once shown it stays at least 400 ms. A focus the region dropped comes back to the same element, to the element with its id in the new content, or to the region.

Bookings this week

128

bookings, 312,400 THB

<section class="card" id="busy-region-card" aria-labelledby="busy-region-title">
	<div class="card-header"><h2 id="busy-region-title">Bookings this week</h2><button class="btn btn-sm ms-auto" type="button" data-docs-run="busy-region" aria-controls="busy-region-card"><i class="bi bi-arrow-repeat" aria-hidden="true"></i>Reload for 2 seconds</button></div>
	<div class="card-body vstack gap-8">
		<p class="fs-24 fw-600 tabular-nums">128</p>
		<p class="text-muted">bookings, 312,400 THB</p>
	</div>
</section>
await REDZ.ui.busy(card, fetch(url).then((answer) => answer.text()).then((html) => { body.innerHTML = html; }));

A button

The button keeps its focus, ignores clicks until the promise settles and shows a spinner: in place of its leading icon, or over its words when it has none - then it keeps its size too. data-busy on the button gives the words it shows while it waits, said politely once the wait passes 400 ms; set it again before each call when the wait has steps.

<button class="btn btn-primary" type="button" data-docs-run="busy-button">Save for 2 seconds</button>
<button class="btn btn-primary" type="button" data-busy="Signing in…" data-docs-run="busy-button"><i class="bi bi-box-arrow-in-right" aria-hidden="true"></i>Sign in for 2 seconds</button>

A button that starts a cooldown while it waits - "Send the code again" with data-cooldown set as the code goes - stays disabled until the count ends: the cooldown holds aria-disabled while it counts, busy() leaves it alone, and the state from before both comes back when the last of them ends.

States

What busy() sets, written by hand - the overlay appears 400 ms after the page draws it.

elementrestaria-busy
card

128 bookings

128 bookings

btn

Accessibility

  • Screen readers mostly ignore aria-busy, and on a live region it breaks the region in some of them: here it is a styling hook, and busy() refuses a live region in the console.
  • The region is inert while busy: no focus, no clicks, out of the accessibility tree. When the overlay shows, one polite message says so - the page's text busy, “Loading…”.
  • For a first load, where there is nothing to cover yet, use placeholders.

Tokens

From css/components/busy.css, which declares no custom property of its own.

It reads the theme's --redz-bg, --redz-primary.