Playground
A modal built on the native <dialog>. The browser traps focus, closes on Escape and handles the top layer. Open it with show() and close it with close(), or mark footer buttons with data-sb-close.
Examples
Open from Datastar
data-ref gives you the element as a signal, so any Datastar expression can call its methods.
<div>
<sb-button data-on:click="$_launch.show()">Launch sequence</sb-button>
<sb-modal data-ref:_launch heading="Mission Control" data-on:sb-close="$_answer = evt.detail.value">
Are you ready to launch? This cannot be undone.
<sb-button slot="footer" variant="ghost" data-sb-close="cancel">Cancel</sb-button>
<sb-button slot="footer" data-sb-close="launch">Launch</sb-button>
</sb-modal>
<p data-signals:_answer="''" data-show="$_answer" data-text="'You chose: ' + $_answer"></p>
</div>
Inline
The inline attribute renders the panel in place, which is handy for docs and previews.
<sb-modal inline heading="Docking complete" closable="false">
All systems nominal. Welcome aboard.
</sb-modal>
Opened by the server
In a CQRS app the server owns "is the dialog open?". Render <sb-modal open> and your SSE stream decides. Datastar's morph keeps the element, and its state lives inside the component.
Accessibility
The native modal <dialog> sets aria-modal, traps focus and restores it on close. The heading labels the dialog. Escape and a click on the backdrop both close it, and sb-close reports why in detail.reason.
Installation
Add Datastar with Rocket and the Starbase autoloader once per page, then use the tag. The autoloader imports each component the first time its tag appears, including tags added later by a Datastar morph.
<!-- Once per page: Datastar with Rocket, and the Starbase autoloader.
It loads every <sb-…> component the first time its tag appears. -->
<script type="importmap">
{ "imports": { "datastar": "https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.4/bundles/datastar-rocket.js" } }
</script>
<script type="module" src="https://rocket.libretto.ch/c/autoloader.js"></script>
<!-- Optional, no flash of undefined elements: class="sb-cloak" on <html>, and -->
<style>.sb-cloak :not(:defined) { visibility: hidden }</style>
<sb-modal inline heading="Mission Control" style="--sb-radius-lg: 8px"><span>Are you ready to launch?</span><sb-button slot="footer" size="sm" variant="outline" data-sb-close>Cancel</sb-button><sb-button slot="footer" size="sm" data-sb-close>Launch</sb-button></sb-modal>
<!-- Or skip the autoloader and load just this component: -->
<!-- <script type="module" src="https://rocket.libretto.ch/c/modal/modal.js"></script> -->API reference
Props
| Attribute | Type | Default | Description |
|---|---|---|---|
heading | string | "Dialog" | Title of the dialog. |
open | boolean | false | Open on first render. Use show() and close() afterwards. |
inline | boolean | false | Render in place, without an overlay (previews, docs). |
closable | boolean | true | Show the close button. |
Slots
| Name | Description |
|---|---|
default | Dialog body. |
footer | Action buttons. Elements with data-sb-close close the dialog. |
Events
| Name | Description |
|---|---|
sb-open | After opening. |
sb-close | After closing. detail: { reason, value }. |