Overlays

AlertDialog

A modal that only informs, with one OK button, on the browser's own <dialog> and no JavaScript.

#dialog#overlay#feedback#no-js

Demo

Preview

Export done

Your file is on its way to you by email. It can take a few minutes.

About

Quillo UI 1.0. Every component in one file, and each on its own.

Payment failed

Your card was declined. Nothing was charged.

Session ended

You were signed out after an hour without activity.

Not closed yet.

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/alert_dialog.qwe
main.qwe
import ui/theme.qwe use theme_cssuse ui/alert_dialog.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>

Example

The code of the preview above.

demos/alert_dialog.qwe
component AlertDialogDemoLive() {    state closed = 0    <div.demo-row>        <AlertDialog id="alert-demo-live" title="Session ended" tone="warning" ok="Sign in again" trigger="Session ended"                     on_close={() => { closed += 1 }}>            <p>You were signed out after an hour without activity.</p>        </AlertDialog>        <span.demo-note>{closed == 0 ? "Not closed yet." : `Closed ${closed} ${closed == 1 ? "time" : "times"}.`}</span>    </div>}view AlertDialogDemo() =    <div.demo-stack>        <div.demo-row>            <AlertDialog id="alert-demo-done" title="Export done" tone="success" trigger="Export done">                <p>Your file is on its way to you by email. It can take a few minutes.</p>            </AlertDialog>            <AlertDialog id="alert-demo-info" title="About" trigger="About">                <p>Quillo UI 1.0. Every component in one file, and each on its own.</p>            </AlertDialog>            <AlertDialog id="alert-demo-failed" title="Payment failed" tone="danger" ok="Try again" trigger="Payment failed">                <p>Your card was declined. Nothing was charged.</p>            </AlertDialog>        </div>        <AlertDialogDemoLive/>    </div>

Source

ui/alert_dialog.qwe
// AlertDialog: a modal that only tells ("Your session has ended", "Export done")// and has one OK button. It is the browser's own <dialog>, opened by its trigger// button with the `command` attribute and closed by a `method="dialog"` form, so it// needs no JavaScript (Chrome 135, Firefox 144, Safari 26 and later; where the// button cannot open it, open it with Modal instead). Escape closes it too; focus// stays in it, and it is announced as an alert dialog.////   <AlertDialog id="done" title="Export done" trigger="Export">//       <p>Your file is on its way to you by email.</p>//   </AlertDialog>//   <AlertDialog id="out" title="Session ended" tone="warning" ok="Sign in again" trigger="Show">...</AlertDialog>//// id (required): the dialog's id, unique on the page: the trigger opens it by it.// title: its heading, and its name for screen readers. children: the message.// trigger: the text of a button that opens it (styled as a Button when button_css is// on the page); null for none, to open it yourself (`document.getElementById(id).showModal()`).// ok ("OK"): the button's text. tone: "info" (default), "success", "warning" or// "danger", an icon and a colour. on_close(): inside one of your components,// called when it closes.import ui/theme.qwe use ui_clslet alert_dialog_css = css`.ui-alert-dialog-host { display: contents; }.ui-alert-dialog {  width: min(400px, calc(100vw - 32px)); padding: 0; border: 1px solid var(--ui-border); border-radius: var(--ui-radius);  background: var(--ui-surface); color: var(--ui-fg); box-shadow: var(--ui-shadow-lg); font: 15px/1.5 var(--ui-font);  --t: var(--ui-info); --ts: var(--ui-info-soft);}.ui-alert-dialog-success { --t: var(--ui-success); --ts: var(--ui-success-soft); }.ui-alert-dialog-warning { --t: var(--ui-warning); --ts: var(--ui-warning-soft); }.ui-alert-dialog-danger { --t: var(--ui-danger); --ts: var(--ui-danger-soft); }.ui-alert-dialog::backdrop { background: var(--ui-overlay); }.ui-alert-dialog[open] { animation: ui-alert-dialog-in .16s ease-out; }@keyframes ui-alert-dialog-in { from { opacity: 0; transform: translateY(8px) scale(.98); } }.ui-alert-dialog-box { display: grid; gap: 8px; justify-items: center; padding: 24px 22px 20px; text-align: center; }.ui-alert-dialog-icon { display: grid; place-items: center; width: 44px; height: 44px; margin-bottom: 4px; border-radius: 50%; background: var(--ts); color: var(--t); }.ui-alert-dialog-icon svg { width: 24px; height: 24px; }.ui-alert-dialog-box h2 { margin: 0; font-size: 17px; font-weight: 600; line-height: 1.3; }.ui-alert-dialog-body { color: var(--ui-muted); }.ui-alert-dialog-body > :first-child { margin-top: 0; }.ui-alert-dialog-body > :last-child { margin-bottom: 0; }.ui-alert-dialog-box form { width: 100%; margin: 12px 0 0; }.ui-alert-dialog-ok {  width: 100%; height: 38px; border: 0; border-radius: var(--ui-radius-sm); background: var(--ui-accent); color: var(--ui-accent-fg);  font: 500 14px/1 var(--ui-font); cursor: pointer;}.ui-alert-dialog-ok:hover { background: color-mix(in srgb, var(--ui-accent) 88%, var(--ui-fg)); }.ui-alert-dialog-ok:focus-visible { outline: none; box-shadow: var(--ui-focus); }@media (prefers-reduced-motion: reduce) { .ui-alert-dialog[open] { animation: none; } }`fn alert_dialog_icon(tone: str) =    match tone {        "success" => "M20 6 9 17l-5-5"        "warning" => "M12 9v4M12 17h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z"        "danger" => "M12 8v4M12 16h.01M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20Z"        _ => "M12 16v-4M12 8h.01M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20Z"    }view AlertDialog(id: str, title: str, trigger: str? = null, ok: str = "OK", tone: str = "info", on_close = null,                 class: str? = null, children) {    style global alert_dialog_css    <div.ui-alert-dialog-host>        {trigger != null && <button.ui-btn.ui-btn-outline type="button" commandfor={id} command="show-modal"                                    aria-haspopup="dialog">{trigger}</button>}        <dialog class={ui_cls(["ui-alert-dialog", tone != "info" && `ui-alert-dialog-${tone}`, class])} id={id}                role="alertdialog" aria-labelledby={`${id}-title`} aria-describedby={`${id}-body`}                @close={() => { on_close && on_close() }}>            <div.ui-alert-dialog-box>                <div.ui-alert-dialog-icon aria-hidden="true">                    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"                         stroke-linejoin="round"><path d={alert_dialog_icon(tone)}/></svg>                </div>                <h2 id={`${id}-title`}>{title}</h2>                <div.ui-alert-dialog-body id={`${id}-body`}>{children}</div>                <form method="dialog"><button.ui-alert-dialog-ok type="submit" autofocus>{ok}</button></form>            </div>        </dialog>    </div>}
Thread
67Useful? Like it, or add to its thread below.

Thread 0 posts

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

No posts yet. The first one opens the thread.