Connection lost. Reconnecting… attempt 1 of 8
Paused. Your work is held on the server.
Could not reconnect.
This session has expired on the server.
DR.Simple_UI
Showing main. This can be ahead of the version your app has installed — check what a class says it needs before copying it. Releases (opens in a new tab)

Modal

tier 2 — classes

A question that has to be answered before anything else happens. .modal is the panel; header, body and footer are three optional rows. You own the content and what the answer means.

Put it on a <dialog> and open it with showModal(). The top layer, the focus trap, Escape, inert content behind and returning focus on close all come from the platform, and none of them are things a div overlay does without a lot of code that is usually subtly wrong. There is no .modal-backdrop involved: ::backdrop is the platform's own, so the dimming is a pseudo-element rather than an element in the page. drSimpleUi.confirm() is built exactly this way.

A real modal

Live — open it. Escape closes it, focus is trapped inside while it is open and returns to the button afterwards, and the page behind is inert. None of that is written here.

The whole panel is wrapped in <form method="dialog">, which is what makes every button in it close the dialog with no JavaScript at allvalue becomes dialog.returnValue, so “which button” is answered without a handler. Only opening needs a line of script.

aria-labelledby pointing at the heading is worth the two attributes: a modal dialog is announced by its accessible name, and without one a screen reader says “dialog” and stops.

<button class="btn btn-warn-solid" type="button"
        onclick="document.getElementById('takeover').showModal()">
    <i class="ri-user-shared-line"></i> Take over this order
</button>

<dialog class="modal" id="takeover" aria-labelledby="takeover-title">
    <form method="dialog">
        <div class="modal-header">
            <h3 id="takeover-title">Take over this order?</h3>
            <button class="modal-close" value="cancel" aria-label="Close"><i class="ri-close-line"></i></button>
        </div>
        <div class="modal-body">
            <p>Automation stops handling <strong>ORD-4209</strong> and you become the owner. Any pending reservation on it expires.</p>
            <p class="form-hint">A note is written to the order's work log in your name.</p>
        </div>
        <div class="modal-footer">
            <button class="btn" value="cancel">Cancel</button>
            <button class="btn btn-warn-solid" value="accept"><i class="ri-user-shared-line"></i> Accept &amp; take over</button>
        </div>
    </form>
</dialog>

The panel

The three rows on their own, so they can be read without opening anything. This is the same markup as above with the <dialog> and the form taken off — a .modal is an ordinary bordered panel until something puts it in the top layer.

<div class="modal">
    <div class="modal-header">
        <h3>Take over this order?</h3>
        <button class="modal-close" type="button" aria-label="Close"><i class="ri-close-line"></i></button>
    </div>
    <div class="modal-body">
        <p>Automation stops handling <strong>ORD-4209</strong> and you become the owner. Any pending reservation on it expires.</p>
        <p class="form-hint">A note is written to the order's work log in your name.</p>
    </div>
    <div class="modal-footer">
        <button class="btn" type="button">Cancel</button>
        <button class="btn btn-warn-solid" type="button"><i class="ri-user-shared-line"></i> Accept &amp; take over</button>
    </div>
</div>

Destructive confirm

Name the consequence in the body, and make the confirming button the destructive one. Focus the safe choice when it opens: showModal() focuses the first focusable element, which is Cancel by source order here, and for a destructive action that ordering is the point rather than an accident.

<div class="modal" style="max-width:420px">
    <div class="modal-header">
        <h3>Delete this warehouse?</h3>
        <button class="modal-close" type="button" aria-label="Close"><i class="ri-close-line"></i></button>
    </div>
    <div class="modal-body">
        <div class="alert alert-danger" style="margin:0">
            <i class="ri-error-warning-line"></i>
            <span>This cannot be undone. 14 open orders currently route here.</span>
        </div>
        <p><strong>EU-West</strong> will be removed from every automatic reservation.</p>
    </div>
    <div class="modal-footer">
        <button class="btn" type="button">Keep it</button>
        <button class="btn btn-danger" type="button"><i class="ri-delete-bin-line"></i> Delete</button>
    </div>
</div>

Scrolling body

Cap the body with max-height and overflow-y:auto, then add .dr-scroll to theme the scrollbar and add the fading scroll edges. .dr-scroll styles a scroll container; it does not create one. Inside a .modal the library already re-points --scroll-bg at the elevated surface, so the fade matches the panel instead of showing as a dark band.

<div class="modal">
    <div class="modal-header">
        <h3>Change history</h3>
        <button class="modal-close" type="button" aria-label="Close"><i class="ri-close-line"></i></button>
    </div>
    <div class="modal-body dr-scroll" style="max-height:200px; overflow-y:auto" tabindex="0" role="group" aria-label="Change history">
        <div class="kv"><span class="k">03:15</span><span class="v">Reconciliation ran</span></div>
        <div class="kv"><span class="k">02:58</span><span class="v">Priority raised to P2</span></div>
        <div class="kv"><span class="k">02:41</span><span class="v">Rerouted to EU-West</span></div>
        <div class="kv"><span class="k">01:12</span><span class="v">Comment added by customer</span></div>
        <div class="kv"><span class="k">00:47</span><span class="v">Reservation created automatically</span></div>
        <div class="kv"><span class="k">00:31</span><span class="v">Order opened</span></div>
        <div class="kv"><span class="k">00:30</span><span class="v">E-mail received</span></div>
    </div>
    <div class="modal-footer">
        <button class="btn btn-primary" type="button">Close</button>
    </div>
</div>

Widths

.modal-sm is 360px and .modal-lg is 760px, against the default 480px. Width is the only thing they change — a yes/no question does not need the room a form does, and a diff or a table needs more. Pick by how wide the content has to be, not by how important the dialog is.

<div class="dr-col">
    <div class="modal modal-sm">
        <div class="modal-body"><p>Release the lock on ORD-4199?</p></div>
        <div class="modal-footer">
            <button class="btn" type="button">Cancel</button>
            <button class="btn btn-primary" type="button">Release</button>
        </div>
    </div>
    <div class="modal modal-lg">
        <div class="modal-header">
            <h3>Order detail</h3>
            <button class="modal-close" type="button" aria-label="Close"><i class="ri-close-line"></i></button>
        </div>
        <div class="modal-body"><p>A wider panel, for content that would wrap badly at 480px.</p></div>
    </div>
</div>

Without a dialog

.modal-backdrop dims and centres a panel inside the page instead. It is the fallback, for markup that cannot be a <dialog> — you are already inside a form and cannot nest another, or the panel has to stay in the page's own stacking context.

Then role="dialog" and aria-modal="true" are on you, and so is everything the platform was doing: Escape, keeping Tab inside the panel, making the rest of the page unreachable, and putting focus back where it came from.

<!-- Only when the panel cannot be a <dialog> — you are already inside a form and
     cannot nest another, or the panel has to stay in the page's own stacking
     context. Everything showModal() gives you has to be written by hand here:
     Escape, the focus trap, inert content behind, and returning focus on close. -->
<div class="modal-backdrop">
    <div class="modal" role="dialog" aria-modal="true" aria-labelledby="lock-title">
        <div class="modal-header">
            <h3 id="lock-title">Release the lock?</h3>
            <button class="modal-close" type="button" aria-label="Close"><i class="ri-close-line"></i></button>
        </div>
        <div class="modal-body">
            <p>ORD-4199 goes back to the queue.</p>
        </div>
        <div class="modal-footer">
            <button class="btn" type="button">Cancel</button>
            <button class="btn btn-primary" type="button">Release</button>
        </div>
    </div>
</div>
Z-order: the backdrop sits at 500, and a top-layer <dialog> ignores the scale entirely — the platform orders it by promotion. Anything you layer above a modal respects the shared chain: spotlight 510 < popover and dropdown 550 < toast 600 < tips and the reconnect banner 1000. A drawer sits below at 480/490, so a modal opened from inside one still covers it. Do not invent a value; pick the rung you belong on — a test fails on anything else.