Modal
A window that opens on top of the page and dims what is behind it. Use it for short forms, detail views and situations where the user needs to focus on one task; only one modal stays open at a time.
Basic usage
Every modal has a unique name value. The window is opened by this name with acun:modal.trigger, the acun:modal:open event or, in JavaScript, AcunUI.modal.open('catalog-customer'). The X in the header, the Esc key and a click on the backdrop close the window. When it opens, focus moves to the first interactive element, the Tab key cycles inside the window, and when it closes, focus returns to the button that opened it.
<acun:modal.trigger name="catalog-customer"><acun:button>Open the customer summary</acun:button></acun:modal.trigger>
<acun:modal name="catalog-customer" title="Customer summary">
<dl class="space-y-3 text-sm">
<div class="flex justify-between"><dt class="text-gray-500 dark:text-zinc-400">Customer</dt><dd>Ayşe Yılmaz</dd></div>
<div class="flex justify-between"><dt class="text-gray-500 dark:text-zinc-400">Membership</dt><dd>Premium</dd></div>
<div class="flex justify-between"><dt class="text-gray-500 dark:text-zinc-400">Last order</dt><dd>September 12, 2026</dd></div>
</dl>
</acun:modal>
| Prop | Values |
|---|---|
name | Unique name; events carry this name |
title | Header line (with the X button) |
Widths
The maximum width of the window is chosen with max-width; the default is md. Use sm for confirmations and short messages, and xl or 2xl for two-column forms or tables. On narrow screens the window is always full width.
<div class="flex flex-wrap gap-2" x-data>
<acun:modal.trigger name="catalog-width-sm"><acun:button variant="white">sm</acun:button></acun:modal.trigger>
<acun:modal.trigger name="catalog-width-lg"><acun:button variant="white">lg</acun:button></acun:modal.trigger>
<acun:modal.trigger name="catalog-width-2xl"><acun:button variant="white">2xl</acun:button></acun:modal.trigger>
</div>
<acun:modal name="catalog-width-sm" title="Narrow window" max-width="sm"><p class="text-sm">For a short confirmation or message.</p></acun:modal>
<acun:modal name="catalog-width-lg" title="Wide window" max-width="lg"><p class="text-sm">For single-column forms.</p></acun:modal>
<acun:modal name="catalog-width-2xl" title="Widest window" max-width="2xl"><p class="text-sm">For two-column forms and small tables.</p></acun:modal>
| Prop | Values |
|---|---|
max-width | sm | md | lg | xl | 2xl |
Window without a title
When title is not given, the header line and the X button are not rendered; the content uses the whole window. In this case, give an aria-label so the screen reader can name the window (when empty, "Dialog" is read, in the app language).
<acun:modal.trigger name="catalog-done"><acun:button variant="white">Finish the import</acun:button></acun:modal.trigger>
<acun:modal name="catalog-done" max-width="sm" aria-label="Import complete">
<div class="flex flex-col items-center gap-3 py-4 text-center">
<acun:icon name="check-circle" size="12" stroke="1.4" class="text-emerald-600" />
<p class="text-base font-semibold text-gray-900 dark:text-zinc-100">Import complete</p>
<p class="text-sm text-gray-500 dark:text-zinc-400">148 products were added to the Electronics category.</p>
<acun:modal.close><acun:button class="mt-2">Done</acun:button></acun:modal.close>
</div>
</acun:modal>
| Prop | Values |
|---|---|
aria-label | Accessible name of the window when there is no title |
Do not close on outside click
So that a long form is not lost by accident, turn off closing on backdrop click with :closable-on-backdrop="false"; X and Esc keep working. Pass the value with a colon: if you write closable-on-backdrop="false", the text "false" is passed and the feature stays on.
<acun:modal.trigger name="catalog-sticky"><acun:button variant="white">Open the ticket form</acun:button></acun:modal.trigger>
<acun:modal name="catalog-sticky" title="Support ticket" :closable-on-backdrop="false">
<acun:textarea name="ticket_note" label="Ticket note" rows="4" />
<acun:slot:footer>
<acun:modal.close><acun:button variant="white">Close</acun:button></acun:modal.close>
</acun:slot:footer>
</acun:modal>
| Prop | Values |
|---|---|
closable-on-backdrop | true | false |
Asking before closing
When request-close-event is given, X, Esc and the backdrop do not close the window directly; they emit an event with this name. The code listening for the event (e.g. an unsaved changes check) decides and closes the window: close() if the listener is on the window, AcunUI.modal.close('catalog-note') if it is elsewhere. close() in the footer skips this check.
<acun:modal.trigger name="catalog-note"><acun:button variant="white">Add a note</acun:button></acun:modal.trigger>
<acun:modal
name="catalog-note"
title="Add a note"
request-close-event="catalog-note-close-requested"
x-on:catalog-note-close-requested.window="if (confirm('The note you wrote will not be saved. Close anyway?')) close()"
>
<acun:textarea name="customer_note" label="Note" rows="3" />
</acun:modal>
| Prop | Values |
|---|---|
request-close-event | Name of the event emitted on a close request |
With Livewire
Send the acun:modal:open and acun:modal:close events from the server. close-action is the Livewire method called when the user closes the window (X, Esc, backdrop, close()); an acun:modal:close sent from the server does not call this method again. When the window opens, acun:modal:opened is emitted, and on every close acun:modal:closed, both with { name }. The original names (open-modal, close-modal and the emitted modal-closed) keep working as well.
// <acun:modal name="product-form" title="Product" close-action="resetForm"> … </acun:modal>
public function edit(int $productId): void
{
$this->product = Product::findOrFail($productId);
$this->dispatch('acun:modal:open', name: 'product-form');
}
public function save(): void
{
$this->product->save();
$this->dispatch('acun:modal:close', name: 'product-form');
}
public function resetForm(): void
{
$this->reset('product');
}
| Prop | Values |
|---|---|
close-action | Livewire method called when the user closes the window |
Composable parts
Wrap the button that opens the modal in acun:modal.trigger; no JavaScript is needed. Build the window from the acun:modal.header (title, description and X), acun:modal.body (scrolling body) and acun:modal.footer parts; a button inside acun:modal.close closes the window. The content must start with a part; to wrap the body and footer in a form, put the form after the header.
<acun:modal.trigger name="add-task">
<acun:button>New task</acun:button>
</acun:modal.trigger>
<acun:modal name="add-task" max-width="lg">
<acun:modal.header title="New task" description="Choose the branch and the assignee." />
<acun:modal.body class="space-y-4">
<acun:input name="task_name" label="Task name" placeholder="Kadıköy – stock count" />
<acun:select name="branch" label="Branch" :options="['kadikoy' => 'Kadıköy', 'uskudar' => 'Üsküdar', 'atasehir' => 'Ataşehir']" placeholder="Choose a branch" />
<acun:select name="assignee" label="Assignee" :options="['ahmet' => 'Ahmet Yılmaz', 'elif' => 'Elif Kaya']" placeholder="Choose a team member" />
</acun:modal.body>
<acun:modal.footer>
<acun:modal.close>
<acun:button variant="white">Cancel</acun:button>
</acun:modal.close>
<acun:modal.close>
<acun:button x-on:click="$dispatch('toast', { type: 'success', message: 'Task saved.' })">Save</acun:button>
</acun:modal.close>
</acun:modal.footer>
</acun:modal>
| Prop | Values |
|---|---|
acun:modal.trigger | name (the modal to open) |
acun:modal.header | title, description; slot: header content |
acun:modal.body · footer | Body and footer |
acun:modal.close | The element inside closes the modal |
Close button in a classic modal
acun:modal.close also works in the classic usage with title and the footer slot.
<acun:modal.trigger name="delivery-note">
<acun:button variant="white">Delivery note</acun:button>
</acun:modal.trigger>
<acun:modal name="delivery-note" title="Delivery note">
<p class="text-sm text-gray-600 dark:text-zinc-400">If the customer is not at the address, leave the package with the building attendant and take a photo.</p>
<acun:slot:footer>
<acun:modal.close><acun:button variant="white">Close</acun:button></acun:modal.close>
</acun:slot:footer>
</acun:modal>
Props and slots
The values the component accepts.
| Prop | Default | Description |
|---|---|---|
name | — | Required. The name used in the acun:modal:open / acun:modal:close events and the AcunUI.modal.open('…') / close('…') calls; closing without a name closes all modals. acun:modal:opened is emitted on open and acun:modal:closed on close ({ name }) |
title | null | Header line and X button; not rendered when empty |
max-width | md | sm · md · lg · xl · 2xl |
aria-label | null | Accessible name when there is no title; when empty, "Dialog" (in the app language) |
closable-on-backdrop | true | Closes on a backdrop click |
close-action | null | Livewire method called when the user closes the window |
request-close-event | null | X / Esc / backdrop emit this event instead of closing |
modal.trigger: name | — | Required; opens the modal with this name on click (confirmation-dialog included) |
modal.header: title · description | null | Title, description and X; when title is not given, the slot is the title itself |
modal.body · modal.footer | — | Scrolling body and right-aligned footer |
modal.close | — | Clicking the element inside closes the modal |