Utilities

OfflineBanner

A banner across the top of the page for as long as the browser is offline, gone by itself when the connection is back.

#utility#notification#status#interactive

Demo

Preview

Nothing shows while online. Turn the network off to see the banners.

The second is the default: fixed across the top of the window, with a Try again button here.

Get it

It needs theme.qwe, as every component does; or take the whole kit, quillo-ui.qwe. Each component brings its own styles to the pages that show it.

$ curl -O https://quillolang.org/components/ui/theme.qwe$ curl -O https://quillolang.org/components/ui/offline_banner.qwe
main.qwe
import ui/theme.qwe use theme_cssuse ui/offline_banner.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>

Example

The code of the preview above.

demos/offline_banner.qwe
component OfflineBannerDemoLive() {    state seen = []    <div.demo-stack>        <OfflineBanner sticky={false} title="No connection." text="You can keep writing: your changes are sent when it is back."                       on_change={(on) => { seen = push(seen, on ? "online" : "offline") }}/>        <p.demo-note>{len(seen) == 0 ? "Nothing shows while online. Turn the network off to see the banners." : `Seen: ${join(seen, ", ")}.`}</p>    </div>}view OfflineBannerDemo() =    <div.demo-stack>        <OfflineBannerDemoLive/>        <OfflineBanner reload={true}/>        <p.demo-note>The second is the default: fixed across the top of the window, with a Try again button here.</p>    </div>

Source

ui/offline_banner.qwe
// OfflineBanner: a banner across the top of the page for as long as the browser// is offline, saying what still works; it goes by itself when the connection is// back. Nothing shows while online.////   <OfflineBanner/>//   <OfflineBanner title="No connection" text="You can keep writing: your changes are sent when it is back." sticky={false}/>//// title ("You are offline"), text ("Some things may not work until the// connection is back."): what it says. sticky: fixed across the top of the// window (true); false puts it in the flow of the page where it is written.// dismissable: a close button that hides it for this outage (true); it shows// again the next time the connection drops. reload: a "Try again" button that// reloads the page (false). It is an alert, so screen readers hear it at once.// on_change(online): inside one of your components, called with true or false// as the connection comes and goes. (ConnectionStatus is the smaller cousin: a// pill or a dot, and "Back online" when it returns.)import ui/theme.qwe use ui_clslet offline_banner_css = css`.ui-offline-host { display: contents; }.ui-offline { display: flex; align-items: center; gap: 12px; padding: 10px 16px; background: var(--ui-warning-soft); color: var(--ui-fg);  border-bottom: 1px solid color-mix(in srgb, var(--ui-warning) 35%, transparent); font: 14px/1.45 var(--ui-font); }.ui-offline-sticky { position: fixed; top: 0; left: 0; right: 0; z-index: 60; box-shadow: var(--ui-shadow); animation: ui-offline-in .2s ease-out; }.ui-offline-flow { border: 1px solid color-mix(in srgb, var(--ui-warning) 35%, transparent); border-radius: var(--ui-radius-sm); }.ui-offline-icon { flex: none; width: 20px; height: 20px; color: var(--ui-warning); }.ui-offline-body { flex: 1; min-width: 0; }.ui-offline-title { font-weight: 600; margin-right: 6px; }.ui-offline-text { color: color-mix(in srgb, var(--ui-fg) 80%, transparent); }.ui-offline-btn { flex: none; height: 30px; padding: 0 12px; border: 1px solid color-mix(in srgb, var(--ui-warning) 45%, var(--ui-border));  border-radius: var(--ui-radius-sm); background: var(--ui-surface); color: var(--ui-fg); font: 500 13px/1 var(--ui-font); cursor: pointer; }.ui-offline-btn:hover { background: var(--ui-subtle); }.ui-offline-x { flex: none; display: grid; place-items: center; width: 28px; height: 28px; border: 0; border-radius: var(--ui-radius-sm);  background: transparent; color: inherit; font-size: 20px; line-height: 1; opacity: .7; cursor: pointer; }.ui-offline-x:hover { opacity: 1; background: color-mix(in srgb, var(--ui-warning) 18%, transparent); }.ui-offline-btn:focus-visible, .ui-offline-x:focus-visible { outline: none; box-shadow: var(--ui-focus); }@keyframes ui-offline-in { from { transform: translateY(-100%); } }@media (prefers-reduced-motion: reduce) { .ui-offline-sticky { animation: none; } }`component OfflineBanner(title: str = "You are offline", text: str = "Some things may not work until the connection is back.",                        sticky: bool = true, dismissable: bool = true, reload: bool = false, on_change = null, class: str? = null) {    style global offline_banner_css    state online = true    state closed = false    <div class="ui-offline-host" @mount={(_el) => {        online = js("navigator.onLine") != false        let w = js("window")        let went = (_e = null) => { online = false; closed = false; on_change && on_change(false) }        let came = (_e = null) => { online = true; on_change && on_change(true) }        w.addEventListener("offline", went)        w.addEventListener("online", came)        () => { w.removeEventListener("offline", went); w.removeEventListener("online", came) }    }}>        {!online && !closed && <div class={ui_cls(["ui-offline", sticky ? "ui-offline-sticky" : "ui-offline-flow", class])} role="alert">            <svg.ui-offline-icon viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"                 stroke-linejoin="round" aria-hidden="true"><path d="M5 12.5a10 10 0 0 1 14 0M8.5 16a5 5 0 0 1 7 0M12 19.5h.01M3 3l18 18"/></svg>            <div.ui-offline-body><span.ui-offline-title>{title}</span><span.ui-offline-text>{text}</span></div>            {reload && <button.ui-offline-btn type="button" @click={() => { js("location.reload") }}>Try again</button>}            {dismissable && <button.ui-offline-x type="button" aria-label="Dismiss" @click={() => { closed = true }}>×</button>}        </div>}    </div>}
Thread
55Useful? Like it, or add to its thread below.

Thread 0 posts

On OfflineBanner: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.