The modal component wraps the native
<dialog>
element with animated open/close, backdrop blur, scroll locking, and a declarative slot system for header, body, and footer. Add
data-wf-modal
to any
<dialog>
and the JS scaffolds the structure automatically.
wf-action-modal
extends this for server-driven AJAX workflows.
Basic Modal
Minimal - Auto-Scaffolded Header
Add
data-wf-modal
and
data-wf-label
to the dialog. JS builds the header, body, and close button automatically. A trigger element uses
data-wf-open
pointing to the dialog's
id
.
Add a child element with
data-wf-slot="footer"
. JS moves it to
wf-modal__footer
at the bottom of the panel. Use
data-wf-close
on cancel buttons to close the modal.
HTML
<dialog id="my-modal" class="wf-modal" data-wf-modal data-wf-label="Confirm action">
<p>Are you sure you want to publish this item?</p>
<div data-wf-slot="footer">
<button class="wf-btn wf-btn-plain" data-wf-close type="button">Cancel</button>
<button class="wf-btn wf-btn-primary" type="button">Publish</button>
</div>
</dialog>
Sizes
Width Presets
Set
data-wf-size
on the dialog to choose a panel width:
sm
(22rem), default
md
(32rem),
lg
(48rem),
xl
(64rem), or
full
(viewport width).
Listen for
wf:hide
and call
event.preventDefault()
to block closing. The modal will pulse (shake animation) to give the user feedback. Check
event.detail.source
to allow specific sources to bypass the guard.
JavaScript
document.getElementById('edit-modal').addEventListener('wf:hide', e => {
const isDirty = document.querySelector('#edit-form').dataset.dirty === '1'
if (!isDirty) return
// Allow the confirm button to bypass the guard
if (e.detail.source?.matches('[data-action="confirm-close"]')) return
e.preventDefault() // Blocks close - modal pulses instead
})
Data Attributes
Attribute
Element
Description
data-wf-modal
dialog
Required. Marks the element as a WF modal and triggers auto-init.
data-wf-label="Title"
dialog
Header title text rendered by the scaffold.
data-wf-size="sm|md|lg|xl|full"
dialog
Width preset applied to the panel. Default:
md
.
data-wf-light-dismiss
dialog
Close the modal when the user clicks the backdrop.
data-wf-without-header
dialog
Omit the header entirely – no title, no close button.
data-wf-show-duration="250"
dialog
Open animation duration in milliseconds. Default:
200
.
data-wf-hide-duration="200"
dialog
Close animation duration in milliseconds. Default:
200
.
data-wf-animation-in="wf-ani-*"
dialog
Open animation class from
wf-transitions.css
. Default:
wf-ani-scale-in
.
data-wf-animation-out="wf-ani-*"
dialog
Close animation class. Default:
wf-ani-scale-out
.
data-wf-i18n-close-label="..."
dialog
Per-instance override for the close button
aria-label
.
data-wf-open="modal-id"
Trigger
On any element – opens the modal matching the given
id
on click.
data-wf-close
Inside modal
Closes the containing modal when clicked.
data-wf-slot="footer"
Inside modal
Marks a child element as the footer slot – moved to
wf-modal__footer
by JS.
data-wf-slot="header-actions"
Inside modal
Marks a child as the header-actions slot – inserted into the header before the close button.
Custom Properties
Property
Default
Description
--wf-modal-width
32rem
Override the panel width directly. Set on
wf-modal__panel
or a parent.
--wf-modal-spacing
var(--wf-space-4)
Padding inside the header and footer bars.
--wf-anim-duration
Set by JS
Animation duration written by JS on open/close. Controls both panel and backdrop timing.
Events & Public API
Events
All events are dispatched on the
<dialog>
element and bubble up.
wf:show
and
wf:hide
are cancelable.
Event
Cancelable
Description
wf:show
Yes
Fired when
open()
is called. Call
event.preventDefault()
to block opening.
wf:after-show
No
Fired after the open animation completes.
wf:hide
Yes
Fired when close is requested.
event.detail.source
is the triggering element or string. Canceling blocks closing and shakes the modal.
wf:after-hide
No
Fired after the close animation completes.
event.detail.source
available.
JavaScript
import { WojoModal } from './src/js/components/wf-modal.js'
// Programmatic init and control
const modal = new WojoModal(document.getElementById('my-modal')).init()
modal.open() // Open with animation. Fires wf:show → wf:after-show.
modal.close() // Close with animation. Fires wf:hide → wf:after-hide.
modal.destroy() // Remove listeners and deregister instance.
// Event listeners
document.getElementById('my-modal').addEventListener('wf:show', e => {
console.log('Modal is about to open')
})
document.getElementById('my-modal').addEventListener('wf:hide', e => {
if (hasUnsavedChanges) {
e.preventDefault() // Blocks close, pulses the modal instead
}
})
Class Reference
All modifier classes available on
.wf-modal
Class Name
Type
Description
wf-modal
Base
Applied to the
<dialog>
. Full-viewport transparent flex container – never animate directly.
wf-modal__panel
Child
The visible animated box. Auto-created by JS. Receives animation classes from
wf-transitions.css
.