Modal
A dialog over the page, on the browser's own <dialog>: focus, Escape and screen readers handled.
Demo
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/modal.qwemain.qwe
import ui/theme.qwe use theme_cssuse ui/modal.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>Example
The code of the preview above.
demos/modal.qwe
component ModalDemoLive() { state editing = false <div.demo-row> <Button variant="soft" on_click={() => { editing = true }}>Rename (from state)</Button> <Modal title="Rename project" open={editing} size="sm" on_close={() => { editing = false }}> <p>Opened by the component's own state; closing it sets the state back.</p> </Modal> </div>}view ModalDemo() = <div.demo-row> <Modal title="Invite people" trigger="Invite"> <p>They get an email with a link that signs them in. Press Escape or click outside to close.</p> </Modal> <ModalDemoLive/> </div>Source
ui/modal.qwe
// Modal: a dialog over the page, on the browser's own <dialog>, so focus stays in// it, Escape closes it and screen readers announce it.//// <Modal title="Invite people" trigger="Invite">// <p>They get an email with a link.</p>// </Modal>//// trigger: the text of a button that opens it (styled as a Button when// button_css is on the page). Inside one of your components, open it from your// own state instead: <Modal title="..." open={editing} on_close={() => { editing = false }}>.// size: "sm", "md" (default) or "lg". Clicking outside it closes it, unless// dismissable={false}.import ui/theme.qwe use ui_clslet modal_css = css`.ui-modal-host { display: contents; }.ui-modal { width: min(520px, calc(100vw - 32px)); max-height: calc(100vh - 64px); 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);}.ui-modal-sm { width: min(380px, calc(100vw - 32px)); }.ui-modal-lg { width: min(760px, calc(100vw - 32px)); }.ui-modal::backdrop { background: var(--ui-overlay); }.ui-modal[open] { animation: ui-modal-in .16s ease-out; }@keyframes ui-modal-in { from { opacity: 0; transform: translateY(8px) scale(.98); } }.ui-modal-box { padding: 20px 22px 22px; }.ui-modal-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 10px; }.ui-modal-head h2 { margin: 0; font-size: 18px; font-weight: 600; line-height: 1.3; }.ui-modal-x { flex: none; width: 30px; height: 30px; margin: -4px -6px 0 0; border: 0; border-radius: var(--ui-radius-sm); background: transparent; color: var(--ui-muted); font-size: 20px; line-height: 1; cursor: pointer;}.ui-modal-x:hover { background: var(--ui-subtle); color: var(--ui-fg); }.ui-modal-x:focus-visible { outline: none; box-shadow: var(--ui-focus); }.ui-modal-body > :first-child { margin-top: 0; }.ui-modal-body > :last-child { margin-bottom: 0; }@media (prefers-reduced-motion: reduce) { .ui-modal[open] { animation: none; } }`component Modal(title: str, trigger: str? = null, open: bool = false, size: str = "md", dismissable: bool = true, on_close = null, children) { style global modal_css state dlg = null if open && dlg != null && !dlg.open { dlg.showModal() } if !open && dlg != null && dlg.open && trigger == null { dlg.close() } <div.ui-modal-host> {trigger != null && <button.ui-btn.ui-btn-outline type="button" aria-haspopup="dialog" @click={() => dlg.showModal()}>{trigger}</button>} <dialog class={ui_cls(["ui-modal", size != "md" && `ui-modal-${size}`])} aria-label={title} @mount={(el) => { dlg = el if open { el.showModal() } }} @close={() => { on_close && on_close() }} @click={(e) => { if dismissable && e.target.tagName == "DIALOG" { dlg.close() } }}> <div.ui-modal-box> <div.ui-modal-head> <h2>{title}</h2> <button.ui-modal-x type="button" aria-label="Close" @click={() => dlg.close()}>×</button> </div> <div.ui-modal-body>{children}</div> </div> </dialog> </div>}Related
AlertDialogA modal that only informs, with one OK button, on the browser's own <dialog> and no JavaScript.6 views · 7 downloadsFullscreenModalA dialog taking the whole window, with a bar holding close, its title and a main action, on the browser's <dialog>.5 views · 4 downloadsPromptDialogA modal asking for one text value with OK and Cancel, on the browser's own <dialog>; Enter submits, Escape cancels.4 views · 5 downloadsQuickViewA Quick view button on a card that opens a preview of the item, its picture, price, text and actions, in a dialog.5 views · 5 downloadsSearchOverlayA full-screen search with results as one types, grouped and marked, from a list or a server, opened with /.7 views · 5 downloadsShareSheetA bottom sheet with the ways to share a link: copy it, email, X, LinkedIn, WhatsApp and more, and the system's own.6 views · 4 downloads
Thread
Thread 0 posts
On Modal: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.