Skip to content
Alpha

Hololink UI is under active development. Components, APIs and these docs may change without notice.

Loading states

Tell people something is happening, and keep the page from jumping when it finishes.

Component Use it when Example
holo-skeleton You know the shape of the content that is loading A card, list row or avatar
holo-spinner You do not know the shape, and the wait is short A button action, a small refresh
holo-progress The wait is long, or you can measure it An upload, a multi-step import

Rules of thumb:

  • Under about a second, show nothing, or only disable the control. A flash of loading UI is worse than a short wait.
  • Prefer a skeleton for page and panel content. It previews the layout and keeps it stable.
  • Use a spinner for a wait of unknown length that has no content shape to mimic.
  • Use progress when you can say how far along you are.

holo-spinner has role="status" and a visually hidden label (default “Loading…”) that assistive technology announces. Set a label that says what is loading.

Saving changes
<holo-spinner label="Saving changes"></holo-spinner>

holo-skeleton is a placeholder block, hidden from assistive technology (aria-hidden="true"). Its variant is text, circle or rect, and width and height take any CSS length. Match the size of the real content.

<div aria-busy="true">
<holo-skeleton variant="circle" width="3rem" height="3rem"></holo-skeleton>
<holo-skeleton variant="text" width="60%"></holo-skeleton>
<holo-skeleton variant="text" width="90%"></holo-skeleton>
</div>

holo-progress has role="progressbar" and uses label as its accessible name.

  • Determinate: set value (and max, default 100). Use it when you can measure progress.
  • Indeterminate: set indeterminate. Use it for work of unknown length. It omits aria-valuenow, so screen readers report it as busy without a number.
<holo-progress label="Uploading report.pdf" value="40"></holo-progress> <holo-progress label="Preparing export" indeterminate></holo-progress>

Update value as the work advances. Switch to determinate as soon as you have a real figure.

Set aria-busy="true" on the container that is being updated, and remove it when the content is ready. Assistive technology then waits for the finished content instead of announcing each partial change.

panel.setAttribute('aria-busy', 'true');
panel.replaceChildren(...skeletons);
const data = await load();
panel.replaceChildren(render(data));
panel.removeAttribute('aria-busy');

aria-busy does not announce anything by itself. Pair it with a holo-spinner or a status message when the user triggered the wait, so they hear that work started.

  • Give skeletons the same dimensions as the content that replaces them, so nothing moves on load.
  • Reserve space for content that arrives later, with min-height or aspect-ratio, instead of letting the page grow.
  • Swap content in place. Do not insert a loading banner above existing content.
  • Keep a button the same width while it loads: show the spinner beside the label or inside a fixed-width button, rather than replacing the label.
  • Respect prefers-reduced-motion. The Hololink UI loaders do, so do not add your own looping animations.