Quick facts
Package overview
- Package
a11y-async-button- Type
- Vanilla TypeScript, browser runtime
- Dependencies
- None at runtime
- Best for
- Save, send, submit, and retry actions with async feedback
Live demo
Try loading, success, and error states
The buttons below are real <button> elements.
The first simulates a successful save. The second simulates an error
and then resets.
Success state updates the button text, removes busy semantics, announces the result, and returns to idle after the reset delay.
Error state uses the same semantic button, announces the failure, and keeps repeat activation blocked while work is running.
Example library
Choose the pattern you need
Start with the core patterns, then explore focused addons for common product flows and development workflows.
-
Core · Start here
Basic states
Learn the smallest setup for loading, success, error, and reset.
-
Core · Visual feedback
Icon and text
Keep decorative icons synchronized with accessible state labels.
-
Integration · Form workflow
Validated form submit
Combine A11y Form Validator summaries and inline errors with async submission, server-error recovery, and visible status feedback.
-
User flow addon
Retry
Support recoverable failures with clear attempt limits and outcomes.
-
Feedback addon
Visible status
Synchronize button state with persistent, readable status content.
-
Configuration addon
Copy presets
Apply consistent action language across save, send, and delete flows.
-
Developer tool
Markup diagnostics
Scan integrations and surface actionable setup findings.
Installation
Install from npm
Import the JavaScript API and the optional baseline stylesheet from the package.
npm install a11y-async-button
pnpm add a11y-async-button
yarn add a11y-async-button
Basic usage
Enhance an existing button
Markup
<button
class="a11y-async-button"
type="button"
data-a11y-async-button
data-loading-text="Saving..."
data-success-text="Saved"
data-error-text="Could not save"
data-reset-delay="2000"
>
<span class="a11y-async-button__text" data-a11y-async-button-text>
Save changes
</span>
</button>
JavaScript
import { createAsyncButton } from "a11y-async-button";
import "a11y-async-button/styles.css";
const button = document.querySelector("[data-a11y-async-button]");
if (button instanceof HTMLButtonElement) {
const asyncButton = createAsyncButton(button);
button.addEventListener("click", async () => {
if (asyncButton.getState() !== "idle") return;
asyncButton.loading();
try {
await saveChanges();
asyncButton.success();
} catch {
asyncButton.error();
}
});
}
API
Options, methods, and events
Options can be passed to createAsyncButton() or read
from matching data attributes.
| API | Description |
|---|---|
createAsyncButton(element, options) |
Initializes one button and returns an instance. |
initAsyncButtons(options, root) |
Initializes all [data-a11y-async-button] buttons
in the given root.
|
loading(), success(), error() |
Move the button through async action states. |
reset(), lock(), unlock() |
Restore idle state or control activation manually. |
getState(), setState(), destroy() |
Inspect state, set a state directly, or remove plugin behavior. |
createAsyncButtonForm(form, options) |
Optional form addon for validation handoff, async submit state, and visible status feedback. |
initAsyncButtonForms(options, root) |
Initializes all [data-a11y-async-form] forms in the given root. |
createAsyncButtonRetry(button, options) |
Optional retry addon for recoverable async action failures. |
initAsyncButtonRetries(options, root) |
Initializes all [data-a11y-async-retry] buttons in the given root. |
createAsyncButtonStatus(button, options) |
Optional status addon for visible feedback synced from async button state. |
initAsyncButtonStatuses(options, root) |
Initializes all [data-a11y-async-status-button] buttons in the given root. |
createAsyncButtonPreset(button, options) |
Optional presets addon for named or custom async button copy defaults. |
initAsyncButtonPresets(options, root) |
Initializes all [data-a11y-async-preset] buttons in the given root. |
getAsyncButtonPresetOptions(preset, options) |
Returns core async button options for a named or custom preset. |
createAsyncButtonDebugReport(root, options) |
Optional debug addon that scans markup and returns structured findings. |
logAsyncButtonDebugReport(report, options) |
Logs debug findings with severity-specific console methods. |
| Data attribute | Purpose |
|---|---|
data-loading-text, data-success-text, data-error-text |
Text used while work is loading, complete, or failed. |
data-idle-text, data-reset-delay |
Idle text and milliseconds before success or error resets. |
data-prevent-double-click |
Set to false to allow repeat activation while loading. |
data-preserve-width, data-use-native-disabled |
Preserve initial width or use native disabled while locked. |
data-announce, data-live-region |
Control managed or external live-region announcements. |
data-a11y-async-form, data-a11y-async-form-status |
Opt in to the separate form addon and connect visible submit status output. |
data-a11y-async-retry, data-max-attempts |
Opt in to the separate retry addon and configure recoverable attempt limits. |
data-a11y-async-status-button, data-status-target |
Opt in to the separate status addon and connect an existing visible status element. |
data-a11y-async-preset |
Opt in to the separate presets addon and choose a named action preset. |
Lifecycle events bubble from the button, including
a11y-async-button:init,
a11y-async-button:loading,
a11y-async-button:success,
a11y-async-button:error,
a11y-async-button:reset,
a11y-async-button:state-change,
a11y-async-button:lock,
a11y-async-button:unlock, and
a11y-async-button:destroy.
Accessibility behavior
Semantics and announcements
Keyboard interaction
- Uses native button activation for Enter and Space.
- Blocks repeated activation while loading or locked.
- Does not trap focus or move focus after state changes.
Screen reader behavior
- Sets
aria-busyduring async work. - Uses
aria-disabledby default so focus does not disappear. - Announces loading, success, and error text through a managed or external live region.
Styling hooks
CSS classes and custom properties
The default stylesheet uses the a11y-async-button BEM
block with state classes and public custom properties.
.a11y-async-button- root button class..a11y-async-button__text- optional text target..is-loading,.is-success,.is-error, and.is-disabled- state classes.--a11y-async-button-idle-background,--a11y-async-button-success-background, and--a11y-async-button-focus-ring- public styling hooks.
Limitations
What the plugin expects
- It requires a real
<button>element. - It does not create form validation, network requests, or application state.
- Form submit helpers live in the separate
./addons/formentry. - Retry helpers live in the separate
./addons/retryentry. - Visible status helpers live in the separate
./addons/statusentry. - Preset helpers live in the separate
./addons/presetsentry. - Markup diagnostics live in the separate
./addons/debugentry. - Screen reader output depends on clear loading, success, and error text.
- Default CSS is optional, but visible focus and state styling still need to be provided by the page.