Overlays

FullscreenModal

A dialog taking the whole window, with a bar holding close, its title and a main action, on the browser's <dialog>.

#dialog#overlay#mobile#interactive

Demo

Preview

Terms of service

1. Section 1

These terms say what you may do with the service and what we promise in return. The body scrolls under the bar, which stays.

2. Section 2

These terms say what you may do with the service and what we promise in return. The body scrolls under the bar, which stays.

3. Section 3

These terms say what you may do with the service and what we promise in return. The body scrolls under the bar, which stays.

4. Section 4

These terms say what you may do with the service and what we promise in return. The body scrolls under the bar, which stays.

5. Section 5

These terms say what you may do with the service and what we promise in return. The body scrolls under the bar, which stays.

6. Section 6

These terms say what you may do with the service and what we promise in return. The body scrolls under the bar, which stays.

7. Section 7

These terms say what you may do with the service and what we promise in return. The body scrolls under the bar, which stays.

8. Section 8

These terms say what you may do with the service and what we promise in return. The body scrolls under the bar, which stays.

9. Section 9

These terms say what you may do with the service and what we promise in return. The body scrolls under the bar, which stays.

10. Section 10

These terms say what you may do with the service and what we promise in return. The body scrolls under the bar, which stays.

11. Section 11

These terms say what you may do with the service and what we promise in return. The body scrolls under the bar, which stays.

12. Section 12

These terms say what you may do with the service and what we promise in return. The body scrolls under the bar, which stays.

Not saved yet.

Edit profile

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

Example

The code of the preview above.

demos/fullscreen_modal.qwe
component FullscreenModalDemoLive() {    state editing = false    state saved = 0    <div.demo-row>        <Button variant="soft" on_click={() => { editing = true }}>Edit profile (from state)</Button>        <span.demo-note>{saved == 0 ? "Not saved yet." : `Saved ${saved} ${saved == 1 ? "time" : "times"}.`}</span>        <FullscreenModal title="Edit profile" open={editing} done="Save" on_done={() => { saved += 1 }}                         on_close={() => { editing = false }}>            <div.demo-stack>                <TextField label="Name" name="name" field="fullscreen-modal-demo-name" value="Ada Lovelace"/>                <TextArea label="Bio" name="bio" field="fullscreen-modal-demo-bio" value="Engineer in London."/>            </div>        </FullscreenModal>    </div>}view FullscreenModalDemo() =    <div.demo-stack>        <div.demo-row>            <FullscreenModal title="Terms of service" trigger="Read the terms">                {for i in range(1, 13) => <section>                    <h3>{i}. Section {i}</h3>                    <p>These terms say what you may do with the service and what we promise in return. The body scrolls under the bar, which stays.</p>                </section>}            </FullscreenModal>            <FullscreenModalDemoLive/>        </div>    </div>

Source

ui/fullscreen_modal.qwe
// FullscreenModal: a dialog that takes the whole window, with a bar across the top// holding a close button, its title and, if given, a main action ("Save", "Done"):// for an editor, a long form or a preview on a phone. It is the browser's own// <dialog>, so focus stays in it, Escape closes it and screen readers announce it;// the page behind does not scroll while it is open.////   <FullscreenModal title="New post" trigger="Write a post">//       <form id="post"> ... </form>//   </FullscreenModal>//   <FullscreenModal title="Edit photo" open={editing} done="Save" on_done={() => { save() }}//                    on_close={() => { editing = false }}> ... </FullscreenModal>//// title: the bar's heading, and its name for screen readers. children: the body,// which scrolls under the bar. 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: open={...} and on_close().// done: the text of a main action in the bar; it calls on_done() and closes it.// close_label ("Close"): the close button's name. width: how wide the body may be,// in pixels (720), centred under the bar.import ui/theme.qwe use ui_clslet fullscreen_modal_css = css`.ui-fullscreen-modal-host { display: contents; }.ui-fullscreen-modal {  box-sizing: border-box; width: 100vw; height: 100dvh; max-width: none; max-height: none; margin: 0; padding: 0; border: 0;  background: var(--ui-bg); color: var(--ui-fg); font: 15px/1.5 var(--ui-font);}.ui-fullscreen-modal[open] { display: flex; flex-direction: column; animation: ui-fullscreen-modal-in .2s cubic-bezier(.2, .8, .2, 1); }@keyframes ui-fullscreen-modal-in { from { opacity: 0; transform: translateY(24px); } }.ui-fullscreen-modal::backdrop { background: var(--ui-overlay); }:root:has(.ui-fullscreen-modal[open]) { overflow: hidden; }.ui-fullscreen-modal-bar {  display: flex; align-items: center; gap: 10px; flex: none; min-height: 56px; padding: 8px max(12px, env(safe-area-inset-right)) 8px max(12px, env(safe-area-inset-left));  border-bottom: 1px solid var(--ui-border); background: var(--ui-surface);}.ui-fullscreen-modal-bar h2 { flex: 1; min-width: 0; margin: 0; overflow: hidden; font-size: 17px; font-weight: 600; line-height: 1.3; text-overflow: ellipsis; white-space: nowrap; }.ui-fullscreen-modal-x {  display: grid; place-items: center; flex: none; width: 38px; height: 38px; padding: 0; border: 0; border-radius: var(--ui-radius-sm);  background: transparent; color: var(--ui-fg); cursor: pointer;}.ui-fullscreen-modal-x:hover { background: var(--ui-subtle); }.ui-fullscreen-modal-x:focus-visible { outline: none; box-shadow: var(--ui-focus); }.ui-fullscreen-modal-x svg { width: 20px; height: 20px; }.ui-fullscreen-modal-done {  flex: none; height: 36px; padding: 0 16px; border: 0; border-radius: var(--ui-radius-sm); background: var(--ui-accent);  color: var(--ui-accent-fg); font: 600 14px/1 var(--ui-font); cursor: pointer;}.ui-fullscreen-modal-done:hover { background: color-mix(in srgb, var(--ui-accent) 88%, var(--ui-fg)); }.ui-fullscreen-modal-done:focus-visible { outline: none; box-shadow: var(--ui-focus); }.ui-fullscreen-modal-body { flex: 1; min-height: 0; overflow: auto; overscroll-behavior: contain; }.ui-fullscreen-modal-inner { box-sizing: border-box; width: min(100%, var(--w, 720px)); margin: 0 auto; padding: 24px 20px 40px; }.ui-fullscreen-modal-inner > :first-child { margin-top: 0; }.ui-fullscreen-modal-inner > :last-child { margin-bottom: 0; }@media (prefers-reduced-motion: reduce) { .ui-fullscreen-modal[open] { animation: none; } }`component FullscreenModal(title: str, trigger: str? = null, open: bool = false, done: str? = null, close_label: str = "Close",                          width: int = 720, on_done = null, on_close = null, class: str? = null, children) {    style global fullscreen_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-fullscreen-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-fullscreen-modal", class])} aria-label={title} style={`--w: ${width}px`}                @mount={(el) => {                    dlg = el                    if open { el.showModal() }                }}                @close={() => { on_close && on_close() }}>            <div.ui-fullscreen-modal-bar>                <button.ui-fullscreen-modal-x type="button" aria-label={close_label} title={close_label} @click={() => dlg.close()}>                    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"                         aria-hidden="true"><path d="M18 6 6 18M6 6l12 12"/></svg>                </button>                <h2>{title}</h2>                {done != null && <button.ui-fullscreen-modal-done type="button" @click={() => {                    on_done && on_done()                    dlg.close()                }}>{done}</button>}            </div>            <div.ui-fullscreen-modal-body><div.ui-fullscreen-modal-inner>{children}</div></div>        </dialog>    </div>}
Thread
54Useful? Like it, or add to its thread below.

Thread 0 posts

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

No posts yet. The first one opens the thread.