Modals & Drawers
Overlay content with modals and slide-in panels. What includes built-in components for dialogs, confirmation prompts, and drawers — all with keyboard support and smooth transitions.
Opening a Modal
Add w-modal-trigger to any element. The value is the id of the modal to open.
<button w-modal-trigger="edit-modal" class="btn btn-primary">
Edit Profile
</button>
When clicked, the modal with id="edit-modal" becomes visible. The trigger works on any element — buttons, links, table rows, or cards.
The Modal Component
Use the <what-modal> component to create a modal. It accepts id, title, and an optional size prop.
<what-modal id="edit-modal" title="Edit Profile">
<form action="/w-action/update-profile" method="post">
<div class="form-group">
<label class="form-label">Name</label>
<input type="text" name="name" class="form-input" value="#user.full_name#">
</div>
<div class="form-group">
<label class="form-label">Email</label>
<input type="email" name="email" class="form-input" value="#user.email#">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline" w-modal-close>Cancel</button>
<button type="submit" class="btn btn-primary">Save</button>
</div>
</form>
</what-modal>
Everything inside the component tags becomes the modal body via <slot/>. The component renders the backdrop, header with close button, and body wrapper automatically.
Modal sizes
Pass a size prop to control the modal width:
| Size | Max Width |
|---|---|
modal-sm | 360px |
| (default) | 500px |
modal-lg | 720px |
modal-xl | 960px |
<what-modal id="preview-modal" title="Preview" size="modal-lg">
<div class="p-4">
<img src="/uploads/image.jpg" style="width:100%">
</div>
</what-modal>
Closing Modals
Modals can be closed in three ways:
w-modal-closeattribute — Add to any element inside the modal. Clicking it closes the nearest modal or drawer.- Close button — The
<what-modal>component includes a close button (×) in the header automatically. - Programmatic — Toggle the
activeclass on the backdrop element from JavaScript.
<!-- Close button inside modal content -->
<button class="btn btn-outline" w-modal-close>Cancel</button>
<!-- Close link -->
<a href="#" w-modal-close>Dismiss</a>
When a modal closes, overflow: hidden is removed from the body, restoring scroll.
w-modal-close attribute works the same way in modals, confirm modals, and drawers. It finds the nearest .modal-backdrop or .drawer-backdrop ancestor and removes its active class.
Confirm Modals
Use <what-confirm-modal> for destructive actions that need explicit user confirmation. It renders a centered dialog with icon, message, and action buttons.
<button w-modal-trigger="delete-confirm" class="btn btn-danger">
Delete Project
</button>
<what-confirm-modal
id="delete-confirm"
title="Delete Project?"
message="This will permanently remove the project and all its data."
icon="warning"
confirmText="Delete"
cancelText="Keep It"
confirmClass="btn-danger"
/>
Confirm modal props
| Prop | Default | Description |
|---|---|---|
id | — | Unique identifier for the modal |
title | — | Dialog heading |
message | — | Descriptive text below the title |
icon | warning | Icon style for the dialog |
confirmText | Confirm | Label for the confirm button |
cancelText | Cancel | Label for the cancel button |
confirmClass | btn-primary | CSS class for the confirm button |
w-modal-close, so clicking either one closes the dialog. To wire the confirm button to an action, add w-post and w-target attributes to the button or wrap the modal in a form.
Drawers
Drawers are slide-in panels anchored to a screen edge. Use the <what-drawer> component with the same trigger pattern as modals.
<button w-modal-trigger="settings-drawer" class="btn btn-outline">
Settings
</button>
<what-drawer id="settings-drawer" title="Settings" position="right">
<div class="p-4 space-y-4">
<div class="form-group">
<label class="form-label">Language</label>
<select class="form-select">
<option>English</option>
<option>Spanish</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Notifications</label>
<input type="checkbox" class="form-switch" checked>
</div>
</div>
</what-drawer>
Drawer positions
The position prop controls which edge the drawer slides from:
| Position | Behavior |
|---|---|
right | Slides from the right edge (default) |
left | Slides from the left edge |
top | Slides down from the top |
bottom | Slides up from the bottom |
Drawer sizes
Pass a size prop to control the drawer width (for left/right) or height (for top/bottom):
| Size | Dimension |
|---|---|
drawer-sm | 280px |
| (default) | 400px |
drawer-lg | 560px |
drawer-xl | 720px |
<what-drawer id="detail-drawer" title="Details" position="right" size="drawer-lg">
<div class="p-4">
<p>Detailed content here...</p>
</div>
</what-drawer>
Blurred Backdrop
By default, modals use a semi-transparent dark overlay. Add the modal-backdrop-blur class to use a frosted glass effect instead.
<div id="#id#" class="modal-backdrop modal-backdrop-blur">
<div class="modal">
...
</div>
</div>
The blur effect combines a lighter overlay (rgba(0, 0, 0, 0.3)) with backdrop-filter: blur(8px). You can customize both values through CSS variables:
.modal-backdrop-blur {
--w-modal-backdrop: rgba(0, 0, 0, 0.2);
backdrop-filter: blur(12px);
}
For drawers, the same technique applies on the .drawer-backdrop element:
.drawer-backdrop {
background-color: rgba(0, 0, 0, 0.3);
backdrop-filter: blur(8px);
}
Dynamic Content
Combine modal triggers with w-get to load modal content from the server on demand. This avoids rendering heavy content upfront.
<button
w-get="/w-partial/user-detail?id=42"
w-target="#detail-body"
w-modal-trigger="detail-modal"
class="btn btn-outline"
>
View Details
</button>
<what-modal id="detail-modal" title="User Details">
<div id="detail-body">
<p class="text-muted">Loading...</p>
</div>
</what-modal>
When the button is clicked, What fetches the partial, injects it into #detail-body, and opens the modal. The modal starts with a loading placeholder that gets replaced by the server response.
The same pattern works with drawers:
<button
w-get="/w-partial/activity-log"
w-target="#log-content"
w-modal-trigger="log-drawer"
class="btn btn-ghost"
>
Activity Log
</button>
<what-drawer id="log-drawer" title="Recent Activity" position="right" size="drawer-lg">
<div id="log-content" class="p-4">
<p class="text-muted">Loading...</p>
</div>
</what-drawer>
w-loading="is-loading" to the trigger button to show a visual indicator while the content loads. The modal opens immediately, and the content swaps in when the response arrives.
CSS Reference
Key CSS classes for modals and drawers:
| Class | Purpose |
|---|---|
modal-backdrop | Full-screen overlay container |
modal-backdrop-blur | Frosted glass backdrop variant |
modal | The modal panel itself |
modal-header | Top section with title and close button |
modal-body | Scrollable content area |
modal-footer | Bottom section for action buttons |
modal-sm / modal-lg / modal-xl | Size variants |
drawer-backdrop | Full-screen overlay for drawers |
drawer | The drawer panel |
drawer-right / drawer-left / drawer-top / drawer-bottom | Position variants |
drawer-sm / drawer-lg / drawer-xl | Size variants |
active | Added to backdrop to show modal/drawer |