Network Status
stimeo--network-status
Detects online/offline and toggles a live-region banner on change.
The stimeo--network-status controller reads navigator.onLine on connect and subscribes to the window online / offline events, toggling the matching banner. The offline banner is role="alert" (assertive) because losing connectivity is urgent; the recovery banner is role="status" (polite). To make the announcement reliable across assistive tech, the controller re-writes the banner's text on each transition, guarded so an unchanged state never re-announces, and can auto-hide the recovery banner after onlineAutoHide. navigator.onLine is the browser's guess — it does not guarantee server reachability. It dispatches stimeo--network-status:change, and listeners/timers are removed on disconnect (Turbo included). Behavior only — banner styling is owned by this Playground.
These buttons dispatch the window online / offline events so you can see the behavior without actually disconnecting.
<%# Markup for the network-status (offline-detection banner) demo.
The library checks navigator.onLine initially and subscribes to the window's
online/offline events, toggling the corresponding banner's hidden. Offline uses
role="alert" (assertive); coming back online uses role="status" (polite). Since real
online/offline is environment-dependent, the demo's demo.js fakes the window events
to reproduce the behavior. %>
<div class="network-demo">
<div class="network-demo__controls">
<button class="demo-trigger" type="button" data-network-demo="offline">
<%= t("components.network_status.demo.go_offline") %>
</button>
<button class="demo-trigger" type="button" data-network-demo="online">
<%= t("components.network_status.demo.go_online") %>
</button>
</div>
<div data-controller="stimeo--network-status"
data-stimeo--network-status-online-auto-hide-value="3000">
<div
class="network-banner network-banner--offline"
role="alert"
hidden
data-stimeo--network-status-target="offline">
<%= t("components.network_status.demo.offline_message") %>
</div>
<div
class="network-banner network-banner--online"
role="status"
hidden
data-stimeo--network-status-target="online">
<%= t("components.network_status.demo.online_message") %>
</div>
</div>
<p class="network-demo__note"><%= t("components.network_status.demo.note") %></p>
</div>
/*
* Presentation-only styles for the network-status demo.
* This CSS owns the banner's color and placement; the library only toggles
* hidden and reflects data-state (online / offline).
*/
.network-demo {
display: flex;
flex-direction: column;
gap: 1rem;
max-width: 32rem;
}
.network-demo__controls {
display: flex;
gap: 0.5rem;
}
.network-banner {
padding: 0.6rem 0.9rem;
border: 1px solid var(--border);
border-radius: 0.375rem;
font-size: 0.95rem;
}
.network-banner--offline {
border-color: var(--danger-500);
background: var(--danger-50);
color: var(--color-accent);
}
.network-banner--online {
border-color: var(--leaf-500);
background: var(--leaf-50);
color: var(--leaf-500);
}
.network-demo__note {
margin: 0;
font-size: 0.85rem;
color: var(--color-text-muted);
}
// Consumer-side JS for the network-status demo (demo-only).
// Real online / offline transitions are environment-dependent and hard to reproduce,
// so demo buttons fake the window's online / offline events. The library subscribes to
// those events to switch the banner, so production runs the same code path.
document.querySelectorAll("[data-network-demo='offline']").forEach((button) => {
button.addEventListener("click", () => {
window.dispatchEvent(new Event("offline"));
});
});
document.querySelectorAll("[data-network-demo='online']").forEach((button) => {
button.addEventListener("click", () => {
window.dispatchEvent(new Event("online"));
});
});
// Example of subscribing to the state-change event (usable for analytics, a toast, etc.).
document.addEventListener("stimeo--network-status:change", (event) => {
console.log(`[network-status] online=${event.detail.online}`);
});
These demo styles use shared design tokens (light + dark). Copy the shared styles too, then toggle data-theme on your root element for dark mode.
The data-* attributes you add to your own HTML to wire this component. Put the data-controller below on a root element, then place its targets / values / actions inside that element.
On the root element
data-controller="stimeo--network-status"
Targets
| Name | Description | Attribute |
|---|---|---|
offline
|
The role="alert" banner shown while connectivity is lost. |
data-stimeo--network-status-target="offline" |
online
|
The role="status" recovery banner shown when connectivity returns. |
data-stimeo--network-status-target="online" |
Values
| Name | Description | Attribute |
|---|---|---|
onlineAutoHide
|
Milliseconds after which the recovery banner auto-hides; 0 disables (default 0). | data-stimeo--network-status-online-auto-hide-value |
Events
| Name | Description | Event |
|---|---|---|
change
|
Fires on a connectivity transition with detail.online (boolean). | stimeo--network-status:change |
State hooks
The library only manages these ARIA/data attributes and custom properties. Your CSS reads them to render the look — selectors like [aria-selected], [aria-expanded], or var(--stimeo--…) hook into this state.
| Hook | Target | Meaning |
|---|---|---|
hidden |
Offline / Online banner | Toggled to show the matching banner. |
data-state |
Root element | "online" / "offline". |