# ModalStack
A single, stackable modal container for Phoenix LiveView. One stack per page —
only one modal is visible at a time — built on a struct in assigns + a function
component + a LiveView hook (no `live_component`, no hidden process).
## Installation
```elixir
def deps do
[{:modal_stack, "~> 0.1"}]
end
```
### Tailwind
The default chrome ships Tailwind utility classes, so your app must include the
package in its Tailwind content scan. For Tailwind v4, add to your `app.css`:
```css
@source "../../deps/modal_stack/lib";
```
(Adjust the relative path to point at `deps/modal_stack/lib` from your CSS file.)
## Usage
```elixir
def mount(_params, _session, socket) do
{:ok, ModalStack.attach(socket)}
end
def handle_event("delete", _p, socket), do: {:noreply, ModalStack.push(socket, :confirm_delete)}
```
```heex
<ModalStack.modal_stack stack={@modal_stack}>
<:modal name={:confirm_delete} on_cancel={JS.push("delete_cancelled")}>
<p>Are you sure?</p>
<.button phx-click="really_delete">Delete</.button>
</:modal>
</ModalStack.modal_stack>
```
The close button, `Escape`, and click-away pop the stack automatically — no
close handler needed. `on_cancel` is an optional extra side-effect.
## API
- `ModalStack.attach(socket)` — seed state + hook (call in `mount/3`)
- `ModalStack.push(socket, name)` — open a modal (atom name; dedups)
- `ModalStack.pop(socket)` — close the topmost
- `ModalStack.clear(socket)` — close all