Offline state

Connectivity chrome from resolveStatus({ kind: "offline" }) in @sometic/dom/status, plus bindOfflineRecovery for the moment the browser reports it is back. Polite live region, role="status", data-status="offline", and the default title You are offline. Same resolver as Empty state, Error state, and Conflict state, with a different kind.

PreviewStatus

Usage

js
import { bindOfflineRecovery, resolveStatus, resolveStatusAction } from "@sometic/dom/status";

const panel = document.querySelector("#offline");
const view = resolveStatus({ kind: "offline", hasAction: true });

panel.className = view.className;
for (const [key, value] of Object.entries(view.attributes)) {
    panel.setAttribute(key, value);
}

const action = document.createElement("button");
for (const [key, value] of Object.entries(resolveStatusAction().attributes)) {
    action.setAttribute(key, value);
}
action.textContent = "Retry when online";
panel.append(action);

const stopRecovery = bindOfflineRecovery({
    onOnline: () => {
        panel.replaceChildren(document.createTextNode("Back online"));
    },
});
ts
import { bindOfflineRecovery, resolveStatus, type StatusViewModel } from "@sometic/dom/status";

export function mountOffline(panel: HTMLElement, refetch: () => void): () => void {
    const view: StatusViewModel = resolveStatus({ kind: "offline", hasAction: true });
    panel.className = view.className;
    for (const [key, value] of Object.entries(view.attributes)) {
        panel.setAttribute(key, value);
    }
    return bindOfflineRecovery({ onOnline: refetch });
}
html
<section id="offline"></section>

<script type="module">
    import { bindOfflineRecovery, resolveStatus } from "@sometic/dom/status";

    const panel = document.querySelector("#offline");
    const view = resolveStatus({ kind: "offline", hasAction: true });

    panel.className = view.className;
    for (const [key, value] of Object.entries(view.attributes)) {
        panel.setAttribute(key, value);
    }

    const title = document.createElement("h3");
    title.dataset.slot = "title";
    title.textContent = view.title;

    const description = document.createElement("p");
    description.dataset.slot = "description";
    description.textContent = "Changes will be sent when the connection returns.";

    panel.replaceChildren(title, description);

    const stopRecovery = bindOfflineRecovery({
        onOnline: () => {
            description.textContent = "Back online, retrying";
        },
    });

    window.addEventListener("pagehide", stopRecovery, { once: true });
</script>

Notes

  • Offline is polite, because it is a condition rather than a failed action.
  • bindOfflineRecovery returns a dispose function and never touches window at import time. Call it on unmount, or pass a signal tied to your component lifecycle.
  • The online event means the interface came back, not that your API is reachable. Treat recovery as "retry now", then handle a real failure as Error state.
  • Recovery is a callback on purpose: only your app knows whether to revalidate a query, resend a mutation, or just re-enable a button.
  • In tests, pass addEventListener to bindOfflineRecovery instead of faking globals.

Full API, accessibility, styling, edge cases, and FAQ: Status surfaces.