Example
Hello from modal!
<div>
<wui-button variant="primary" v-w-modal.modal-1>Launch demo modal</wui-button>
<wui-modal id="modal-1" title="WuiVue">
<p class="my-4">Hello from modal!</p>
</wui-modal>
</div>
Usage
Modals can keep the user within a context, concentrating their attention on the specific task they need to perform, without distractions from other content on the page. This results in improved usability, as users can complete the task more quickly and efficiently. It also ensures that the user answers the modal before working on other items within the UI.
Additionally, modals can assist in breaking down complex tasks into smaller steps. Although this may require additional clicks, each click is made easier, resulting in minimized cognitive load.
Anatomy
Key elements

- Headline. Maintain consistency between the heading of a modal and the label of the button that triggers it (font-size: 16px).
- Closing the modal. The modal can be closed either by clicking on the "X" button, pressing the ESC key, or clicking outside the modal. In addition, almost every modal includes a "Cancel" button in the footer.
- Action bar (optional). All actions concerning the modal content are placed here (e.g., items filter, search, pagination, etc.).
- Body. The content is highly diverse and not determined by the component. Please take a look at the different variants to get a better understanding. The list is not exhaustive and serves as a reference for different options.
- Footer. Apart from a few exceptions, each modal has a Cancel button and a primary action (right-aligned).
Variants
Modal with content tiles

Content tiles. The different types of content tiles can be used in the body section of the modal. The basic structure of the modal remains the same, with the exception of some optional features. You can find examples of different content tiles below.

Language select
- Top default, Bottom selected
- Icon: (
fa-solid fa-earth-americas
) - Language name
- Two letter abbreviation for language and country
- Further read on translations and content tiles

User select
- Top default, Bottom selected
- Name of the user
- Rounded user-image (60 x 60px)
- Email address of the user (not clickable)
- Department of the user
- Further read on content tiles

Group select
- Top default, Bottom selected
- Icon: (
fa-solid fa-users
) - Group name
- External ID. If there are more than one, they are comma separated. instead of wrapping to a new line, they are truncated.
- Further read on content tiles
Destructive modal

We utilize the danger button to draw the user's attention and emphasize the importance of the action. A similar modal, also with a danger button, is used for the "discard changes" modal.
Modal with tree select

Modal with form

For some items, it may be appropriate to create multiple instances. To facilitate this, a checkbox is available in the footer. When the user clicks "Create," an item is created and a growl notification is displayed as confirmation. The modal remains open, allowing for the creation of additional items.
See also forms.
WUI decisions 
- Keyboard navigation (trap focus). Keyboard navigation in a modal should be limited to the modal itself, ensuring the user's focus on the task at hand and preventing unintended interactions with content outside of the modal.
- Height. Regarding content. If there is too much content use pagination or scrollbar.
- Nesting modals. We avoid nesting modals because it can result in a confusing user experience, causing disorientation and overwhelming users with too many layers. It can also be challenging for users to keep track of their progress in the workflow.
- Sequential modals. We use a sequence of modals to guide users through a specific process or task and keep them focused, preventing distractions from other UI elements.
import { WuiModal } from "@wui/wui-vue/lib/modal";
// And/or in your main.js where you instantiate Vue.js:
// Install the w-modal directive
import { VWModal, ModalPlugin } from "@wui/wui-vue/lib/modal";
Vue.directive("w-modal", VWModal);
// Or install the `ModalPlugin` plugin which includes the component and directive
import { ModalPlugin } from "@wui/wui-vue/lib/modal";
Vue.use(ModalPlugin);
new Vue({
...
});
Description
Modals are streamlined, but flexible dialog prompts powered by JavaScript and CSS. They support a number of use cases from user notification to completely custom content and feature a handful of helpful sub-components, sizes, variants, accessibility, and more.
<div>
<wui-button v-w-modal.modal-1>Launch demo modal</wui-button>
<wui-modal id="modal-1" title="WuiVue">
<p class="m-y-20">Hello from modal!</p>
</wui-modal>
</div>
<!-- w-modal.vue -->
Overview
<wui-modal>
, by default, has an OK and Cancel buttons in the footer. These buttons can be customized by setting various props on the component. You can customize the size of the buttons, disable buttons, hide the Cancel button (i.e. ok-only
), choose a variant (e.g. danger
for a red OK button) using the ok-variant
and cancel-variant
props, and provide custom button content using the ok-title
and cancel-title
props, or using the named slots modal-ok
and modal-cancel
.
<wui-modal>
supports close on ESC (enabled by default), close on backdrop click (enabled by default), and the X
close button in the header (enabled by default). These features may be disabled by setting the props no-close-on-esc
, no-close-on-backdrop
, and hide-header-close
respectively.
You can override the modal title via the named slot modal-title
, override the header completely via the modal-header
slot, and override the footer completely via the modal-footer
slot.
Note: when using the modal-footer
slot, the default OK and Cancel buttons will not be present. Also, if you use the modal-header
slot, the default header X
close button will not be present, nor can you use the modal-title
slot.
Modals will not render their content in the document until they are shown (lazily rendered). Modals, when visible, are rendered appended to the <body>
element. The placement of the <wui-modal>
component will not affect layout, as it always renders as a placeholder comment node (<!---->
). You can revert to the behaviour of older WuiVue versions via the use of the static
prop.
Toggle modal visibility
There are several methods that you can employ to toggle the visibility of <wui-modal>
.
Using v-w-modal
directive
Other elements can easily show modals using the v-w-modal
directive.
<div>
<!-- Using modifiers -->
<wui-button v-w-modal.my-modal>Show Modal</wui-button>
<!-- Using value -->
<wui-button v-w-modal="'my-modal'">Show Modal</wui-button>
<!-- The modal -->
<wui-modal id="my-modal">Hello From My Modal!</wui-modal>
</div>
<!-- w-modal-directive.vue -->
This approach will automatically return focus to the trigger element once the modal closes (similar to default Bootstrap functionality). Other approaches for toggling modal visibility may require additional code to implement this accessibility feature.
See the Accessibility section below for details.
Using this.$wvModal.show()
and this.$wvModal.hide()
instance methods
When WuiVue is installed as a plugin, or the WVModalPlugin
plugin is used, WuiVue will inject a $wvModal
object into every Vue instance (components, apps). this.$wvModal
exposes several methods, of which two are for showing and hiding modals:
Method | Description |
---|---|
this.$wvModal.show(id) | Show the modal with the specified id |
this.$wvModal.hide(id) | Hide the modal with the specified id |
Both methods return immediately after being called.
<div>
<wui-button id="show-btn" @click="$wvModal.show('wv-modal-example')"
>Open Modal</wui-button
>
<wui-modal id="wv-modal-example" hide-footer>
<template #modal-title> Using <code>$wvModal</code> Methods </template>
<div class="display--block text-center">
<h3>Hello From This Modal!</h3>
</div>
<wui-button class="m-t-15" block @click="$wvModal.hide('wv-modal-example')"
>Close Me</wui-button
>
</wui-modal>
</div>
<!-- w-modal-wv-modal-hide-show.vue -->
The this.$wvModal
object is also used for displaying modal message boxes.
Using show()
, hide()
, and toggle()
component methods
You can access modal using ref
attribute and then call the show()
, hide()
or toggle()
methods.
<template>
<div>
<wui-button id="show-btn" @click="showModal">Open Modal</wui-button>
<wui-button id="toggle-btn" @click="toggleModal">Toggle Modal</wui-button>
<wui-modal ref="my-modal" hide-footer title="Using Component Methods">
<div class="display--block text-center">
<h3>Hello From My Modal!</h3>
</div>
<wui-button
class="m-t-15"
variant="outline-danger"
block
@click="hideModal"
>Close Me</wui-button
>
<wui-button
class="m-t-10"
variant="outline-warning"
block
@click="toggleModal"
>Toggle Me</wui-button
>
</wui-modal>
</div>
</template>
<script>
export default {
methods: {
showModal() {
this.$refs["my-modal"].show();
},
hideModal() {
this.$refs["my-modal"].hide();
},
toggleModal() {
// We pass the ID of the button that we want to return focus to
// when the modal has hidden
this.$refs["my-modal"].toggle("#toggle-btn");
},
},
};
</script>
<!-- w-modal-methods.vue -->
The hide()
method accepts an optional string trigger
argument for defining what triggered the modal to close. See section Prevent Closing below for details.
Note: It is recommended to use the this.$wvModal.show()
and this.$wvModal.hide()
methods (mentioned in the previous section) instead of using $ref
methods.
Using v-model
property
v-model
property is always automatically synced with <wui-modal>
visible state and you can show/hide using v-model
.
<template>
<div>
<wui-button @click="modalShow = !modalShow">Open Modal</wui-button>
<wui-modal v-model="modalShow">Hello From Modal!</wui-modal>
</div>
</template>
<script>
export default {
data() {
return {
modalShow: false,
};
},
};
</script>
<!-- w-modal-v-model.vue -->
When using the v-model
prop, do not use the visible
prop at the same time.
Using scoped slot scope methods
Refer to the Custom rendering with slots section on using the various methods passed to scoped slots for closing modals.
Emitting events on $root
You can emit wv::show::modal
, wv::hide::modal
, and wv::toggle::modal
events on $root
with the first argument set to the modal's id. An optional second argument can specify the element to return focus to once the modal is closed. The second argument can be a CSS selector, an element reference, or a component reference (the root element of the component will be focused).
<template>
<div>
<wui-button @click="showModal" ref="btnShow">Open Modal</wui-button>
<wui-button @click="toggleModal" ref="btnToggle">Toggle Modal</wui-button>
<wui-modal id="modal-1">
<div class="display--block">Hello From My Modal!</div>
<wui-button @click="hideModal">Close Me</wui-button>
<wui-button @click="toggleModal">Toggle Me</wui-button>
</wui-modal>
</div>
</template>
<script>
export default {
methods: {
showModal() {
this.$root.$emit("wv::show::modal", "modal-1", "#btnShow");
},
hideModal() {
this.$root.$emit("wv::hide::modal", "modal-1", "#btnShow");
},
toggleModal() {
this.$root.$emit("wv::toggle::modal", "modal-1", "#btnToggle");
},
},
};
</script>
Note: It is recommended to use the this.$wvModal.show()
and this.$wvModal.hide()
methods (mentioned in a previous section) instead of emitting $root
events.
Prevent closing
To prevent <wui-modal>
from closing (for example when validation fails). you can call the .preventDefault()
method of the event object passed to your ok
(OK button), cancel
(Cancel button), close
(modal header close button) and hide
event handlers. Note that .preventDefault()
, when used, must be called synchronously, as async is not supported.
<template>
<div>
<wui-button v-w-modal.modal-prevent-closing>Open Modal</wui-button>
<div class="m-t-15">
Submitted Names:
<div v-if="submittedNames.length === 0">--</div>
<ul v-else class="m-b-0 p-l-15">
<li v-for="name in submittedNames">{ name }</li>
</ul>
</div>
<wui-modal
id="modal-prevent-closing"
ref="modal"
title="Submit Your Name"
@show="resetModal"
@hidden="resetModal"
@ok="handleOk"
>
<form ref="form" @submit.stop.prevent="handleSubmit">
<wui-form-group
label="Name"
label-for="name-input"
invalid-feedback="Name is required"
:state="nameState"
>
<wui-form-input
id="name-input"
v-model="name"
:state="nameState"
required
></wui-form-input>
</wui-form-group>
</form>
</wui-modal>
</div>
</template>
<script>
export default {
data() {
return {
name: "",
nameState: null,
submittedNames: [],
};
},
methods: {
checkFormValidity() {
const valid = this.$refs.form.checkValidity();
this.nameState = valid;
return valid;
},
resetModal() {
this.name = "";
this.nameState = null;
},
handleOk(wvModalEvent) {
// Prevent modal from closing
wvModalEvent.preventDefault();
// Trigger submit handler
this.handleSubmit();
},
handleSubmit() {
// Exit when the form isn't valid
if (!this.checkFormValidity()) {
return;
}
// Push the name to submitted names
this.submittedNames.push(this.name);
// Hide the modal manually
this.$nextTick(() => {
this.$wvModal.hide("modal-prevent-closing");
});
},
},
};
</script>
<!-- w-modal-prevent-closing.vue -->
Note: events ok
, cancel
, and close
are emitted by modal's built in OK, Cancel, and header close (X) buttons respectively. These events will not be emitted, by default, if you have provided your own buttons in the modal-footer
slot or have hidden the footer. In this case use the hide
event to control cancelling of the modal close. Event hide
is always emitted, even if ok
, cancel
, and close
are emitted.
The ok
, cancel
, close
and hide
event object (wvModalEvent
) contains several properties and methods:
Property or Method | Type | Description |
---|---|---|
preventDefault() | Method | When called prevents the modal from closing |
trigger | Property | Will be one of: ok (Default OK Clicked), cancel (Default Cancel clicked), esc (if the Esc key was pressed), backdrop (if the backdrop was clicked), headerclose (if the header X button was clicked), the first argument provided to the hide() method, or null otherwise. |
target | Property | A reference to the modal element |
vueTarget | property | A reference to the modal's Vue VM instance |
componentId | property | The modal's ID |
You can set the value of trigger
by passing an argument to the component's hide()
method for advanced control (i.e. detecting what button or action triggered the modal to hide).
Note: ok
, cancel
, or close
events will be only emitted when the argument to hide()
is strictly 'ok'
, 'cancel'
, or 'headerclose'
respectively. The argument passed to hide()
will be placed into the trigger
property of the event object.
Modal content
Using the grid
Utilize the WUI grid system within a modal by nesting <wui-container fluid>
within the modal-body. Then, use the normal grid system <wui-row>
(or <wui-form-row>
) and <wui-col>
as you would anywhere else.
Tooltips and popovers
Tooltips and popovers can be placed within modals as needed. When modals are closed, any tooltips and popovers within are also automatically dismissed. Tooltips and popovers are automatically appended to the modal element (to ensure correct z-indexing), although you can override where they are appended by specifying a container ID (refer to tooltip and popover docs for details).
<div>
<wui-button v-w-modal.modalPopover>Show Modal</wui-button>
<wui-modal id="modalPopover" title="Modal with Popover" ok-only>
<p>
This
<wui-button v-w-popover="'Popover inside a modal!'" title="Popover"
>Button</wui-button
>
triggers a popover on click.
</p>
<p>
This <a href="#" v-w-tooltip title="Tooltip in a modal!">Link</a> will
show a tooltip on hover.
</p>
</wui-modal>
</div>
<!-- w-modal-popover.vue -->
Lazy loading and static modals
By default, modals will not render their content in the document until they are shown (lazily rendered). Modals that, when visible, are rendered appended to the <body>
element. The <wui-modal>
component will not affect layout, as they render as a placeholder comment node (<!---->
) in the DOM position they are placed. Due to the portalling process, it can take two or more $nextTick
s to render changes of the content into the target.
Modals can be rendered in-place in the document (i.e. where the <wui-modal>
component is placed in the document) by setting the static
prop to true
. Note that the content of the modal will be rendered in the DOM even if the modal is not visible/shown when static
is true
. To make static
modals lazy rendered, also set the lazy
prop to true
. The modal will then appear in the document only when it is visible. Note, when in static
mode, placement of the <wui-modal>
component may affect layout of your document and the modal.
The lazy
prop will have no effect if the prop static
is not true
(non-static modals will always be lazily rendered).
Styling, options, and customization
Modal sizing
Modals have three optional sizes, available via the prop size
. These sizes kick in at certain breakpoints to avoid horizontal scrollbars on narrower viewports. Valid optional sizes are sm
, lg
, and xl
.
<div>
<wui-button v-w-modal.modal-xl variant="primary">xl modal</wui-button>
<wui-button v-w-modal.modal-lg variant="primary">lg modal</wui-button>
<wui-button v-w-modal.modal-sm variant="primary">sm modal</wui-button>
<wui-modal id="modal-xl" size="xl" title="Extra Large Modal"
>Hello Extra Large Modal!</wui-modal
>
<wui-modal id="modal-lg" size="lg" title="Large Modal"
>Hello Large Modal!</wui-modal
>
<wui-modal id="modal-sm" size="sm" title="Small Modal"
>Hello Small Modal!</wui-modal
>
</div>
<!-- w-modal-sizes.vue -->
The size
prop maps the size to the .modal-<size>
classes.
Scrolling long content
When modals become too long for the user's viewport or device, they scroll independent of the page itself. Try the demo below to see what we mean.
<div>
<wui-button v-w-modal.modal-tall>Launch overflowing modal</wui-button>
<wui-modal id="modal-tall" title="Overflowing Content">
<p class="m-y-20" v-for="i in 20" :key="i">
Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus
ac facilisis in, egestas eget quam. Morbi leo risus, porta ac consectetur
ac, vestibulum at eros.
</p>
</wui-modal>
</div>
<!-- w-modal-scroll-overflow.vue -->
You can also create a scrollable modal that allows the scrolling of the modal body by setting the prop scrollable
to true
.
<div>
<wui-button v-w-modal.modal-scrollable>Launch scrolling modal</wui-button>
<wui-modal id="modal-scrollable" scrollable title="Scrollable Content">
<p class="m-y-20" v-for="i in 20" :key="i">
Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus
ac facilisis in, egestas eget quam. Morbi leo risus, porta ac consectetur
ac, vestibulum at eros.
</p>
</wui-modal>
</div>
<!-- w-modal-scrollable-content.vue -->
Vertically centered modal
Vertically center your modal in the viewport by setting the centered
prop.
<div>
<wui-button v-w-modal.modal-center>Launch centered modal</wui-button>
<wui-modal id="modal-center" centered title="WuiVue">
<p class="m-y-20">Vertically centered modal!</p>
</wui-modal>
</div>
<!-- w-modal-center-vertically.vue -->
Feel free to mix vertically centered
with scrollable
.
Variants
Control the header, footer, and body background and text variants by setting the header-bg-variant
, header-text-variant
, body-bg-variant
, body-text-variant
, footer-bg-variant
, and footer-text-variant
props. Use any of the standard WUI variants such as danger
, warning
, info
, success
, dark
, light
, etc.
The variants for the bottom border of the header and top border of the footer can be controlled by the header-border-variant
and footer-border-variant
props respectively.
<template>
<div>
<wui-button @click="show=true" variant="primary">Show Modal</wui-button>
<wui-modal
v-model="show"
title="Modal Variants"
:header-bg-variant="headerBgVariant"
:header-text-variant="headerTextVariant"
:body-bg-variant="bodyBgVariant"
:body-text-variant="bodyTextVariant"
:footer-bg-variant="footerBgVariant"
:footer-text-variant="footerTextVariant"
>
<wui-container fluid>
<wui-row class="m-b-10 text-center">
<wui-col cols="4"></wui-col>
<wui-col cols="4">Background</wui-col>
<wui-col cols="4">Text</wui-col>
</wui-row>
<wui-row class="m-b-15">
<wui-col cols="4">Header</wui-col>
<wui-col cols="4">
<wui-select
v-model="headerBgVariant"
:choices="variants"
></wui-select>
</wui-col>
<wui-col>
<wui-select
v-model="headerTextVariant"
:choices="variants"
></wui-select>
</wui-col>
</wui-row>
<wui-row class="m-b-15">
<wui-col cols="4">Body</wui-col>
<wui-col cols="4">
<wui-select
v-model="bodyBgVariant"
:choices="variants"
></wui-select>
</wui-col>
<wui-col>
<wui-select
v-model="bodyTextVariant"
:choices="variants"
></wui-select>
</wui-col>
</wui-row>
<wui-row>
<wui-col cols="4">Footer</wui-col>
<wui-col cols="4">
<wui-select
v-model="footerBgVariant"
:choices="variants"
></wui-select>
</wui-col>
<wui-col>
<wui-select
v-model="footerTextVariant"
:choices="variants"
></wui-select>
</wui-col>
</wui-row>
</wui-container>
<template #modal-footer>
<div class="display--table w-full">
<p class="display--table-cell">Modal Footer Content</p>
<p class="display--table-cell text--right">
<wui-button variant="primary" size="sm" @click="show=false">
Close
</wui-button>
</p>
</div>
</template>
</wui-modal>
</div>
</template>
<script>
export default {
data() {
return {
show: false,
variants: [
{ label: "primary", value: "primary" },
{ label: "secondary", value: "secondary" },
{ label: "success", value: "success" },
{ label: "warning", value: "warning" },
{ label: "danger", value: "danger" },
{ label: "info", value: "info" },
{ label: "light", value: "light" },
{ label: "dark", value: "dark" },
],
headerBgVariant: "dark",
headerTextVariant: "light",
bodyBgVariant: "light",
bodyTextVariant: "dark",
footerBgVariant: "warning",
footerTextVariant: "dark",
};
},
};
</script>
<!-- w-modal-variants.vue -->
You can also apply arbitrary classes to the modal dialog container, content (modal window itself), header, body and footer via the modal-class
, content-class
, header-class
, body-class
and footer-class
props, respectively. The props accept either a string or array of strings.
Hiding the backdrop
Hide the modal's backdrop via setting the hide-backdrop
prop.
<div>
<wui-button v-w-modal.modal-no-backdrop>Open modal</wui-button>
<wui-modal
id="modal-no-backdrop"
hide-backdrop
content-class="shadow"
title="WuiVue"
>
<p class="m-y-15">
We've added the utility class <code>'shadow'</code>
to the modal content for added effect.
</p>
</wui-modal>
</div>
<!-- modal-no-backdrop.vue -->
Note that clicking outside of the modal will still close the modal even though the backdrop is hidden. You can disable this behaviour by setting the no-close-on-backdrop
prop on <wui-modal>
.
Disable open and close animation
To disable the fading transition/animation when modal opens and closes, just set the prop no-fade
on the <wui-modal>
component.
Footer button sizing
Fancy smaller or larger buttons in the default footer? Simply set the button-size
prop to 'sm'
for small buttons, or 'lg'
for larger buttons.
<div>
<wui-button v-w-modal.modal-footer-sm>Small Footer Buttons</wui-button>
<wui-button v-w-modal.modal-footer-lg>Large Footer Buttons</wui-button>
<wui-modal id="modal-footer-sm" title="WuiVue" button-size="sm">
<p class="m-y-15">This modal has small footer buttons</p>
</wui-modal>
<wui-modal id="modal-footer-lg" title="WuiVue" button-size="lg">
<p class="m-y-15">This modal has large footer buttons</p>
</wui-modal>
</div>
<!-- modal-footer-btn-sizes.vue -->
The prop button-size
has no effect if you have provided your own footer via the modal-footer
slot.
Disabling built-in footer buttons
You can disable the built-in footer buttons programmatically.
You can disable the Cancel and OK buttons individually by setting the cancel-disabled
and ok-disabled
props, respectively, to true
. Set the prop to false
to re-enable the button.
To disable both Cancel and OK buttons at the same time, simply set the busy
prop to true
. Set it to false
to re-enable both buttons.
Custom rendering with slots
<wui-modal>
provides several named slots (of which some are optionally scoped) that you can use to customize the content of various sections of the modal.
Slot | Optionally Scoped | Description |
---|---|---|
default | Yes | Main content of the modal |
modal-title | Yes | Content to place in the modal's title |
modal-header | Yes | Content to place in the header. Replaces the entire header including the close button |
modal-footer | Yes | Content to place in the footer. Replaces the entire footer including the button(s) |
modal-ok | No | Content to place inside the footer OK button |
modal-cancel | No | Content to place inside the footer CANCEL button |
modal-header-close | No | Content to place inside the header CLOSE (x ) button |
The scope available to the slots that support optional scoping are:
Method or Property | Description |
---|---|
ok() | Closes the modal and fires the ok and hide events, with wvModalEvent.trigger = 'ok' |
cancel() | Closes the modal and fires the cancel and hide events, with wvModalEvent.trigger = 'cancel' |
close() | Closes the modal and fires the close and hide events, with wvModalEvent.trigger = 'headerclose' |
hide(trigger) | Closes the modal and fires the hide event, with the wvModalEvent.trigger = trigger (trigger is optional) |
visible | The visibility state of the modal. true if the modal is visible and false if not visible |
Example modal using custom scoped slots
<template>
<wui-button @click="$wvModal.show('modal-scoped')">Open Modal</wui-button>
<wui-modal id="modal-scoped">
<template #modal-header="{ close }">
<!-- Emulate built in modal header close button action -->
<wui-button size="sm" variant="outline-danger" @click="close()">
Close Modal
</wui-button>
<h5>Modal Header</h5>
</template>
<template #default="{ hide }">
<p>Modal Body with button</p>
<wui-button @click="hide()">Hide Modal</wui-button>
</template>
<template #modal-footer="{ ok, cancel, hide }">
<b>Custom Footer</b>
<!-- Emulate built in modal footer ok and cancel button actions -->
<wui-button size="sm" variant="success" @click="ok()"> OK </wui-button>
<wui-button size="sm" variant="danger" @click="cancel()">
Cancel
</wui-button>
<!-- Button with custom close trigger value -->
<wui-button size="sm" variant="outline-secondary" @click="hide('forget')">
Forget it
</wui-button>
</template>
</wui-modal>
</template>
<!-- w-modal-scoped-slots.vue -->
Multiple modal support
Unlike native Bootstrap v4, WuiVue supports multiple modals opened at the same time.
To disable stacking for a specific modal, just set the prop no-stacking
on the <wui-modal>
component. This will hide the modal before another modal is shown.
<div>
<wui-button v-w-modal.modal-multi-1>Open First Modal</wui-button>
<wui-modal
id="modal-multi-1"
size="lg"
title="First Modal"
ok-only
no-stacking
>
<p class="m-y-15">First Modal</p>
<wui-button v-w-modal.modal-multi-2>Open Second Modal</wui-button>
</wui-modal>
<wui-modal id="modal-multi-2" title="Second Modal" ok-only>
<p class="m-y-15">Second Modal</p>
<wui-button v-w-modal.modal-multi-3 size="sm">Open Third Modal</wui-button>
</wui-modal>
<wui-modal id="modal-multi-3" size="sm" title="Third Modal" ok-only>
<p class="m-y-10">Third Modal</p>
</wui-modal>
</div>
<!-- w-modal-multiple.vue -->
Notes:
- Avoid nesting a
<wui-modal>
inside another<wui-modal>
, as it may get "constrained" to the boundaries of the parent modal dialog (specifically when static modals are used). - The opaque backdrop will appear progressively darker for each modal that is opened. This is expected behaviour as each backdrop is opened over top the other modals and backdrops.
Modal message boxes
WuiVue provides a few built in Message Box methods on the exposed this.$wvModal
object. These methods provide a way to generate simple OK and Confirm style modal messages, from anywhere in your app without having to explicitly place a <wui-modal>
component in your pages.
Method | Description |
---|---|
this.$wvModal.msgBoxOk(message, options) | Open a modal with message as the content and a single OK button |
this.$wvModal.msgBoxConfirm(message, options) | Open a modal with message as the content and CANCEL and OK buttons |
The options
argument is an optional configuration object for adding titles and styling the Message Box modal. The object properties correspond to <wui-modal>
props, except in camelCase format instead of kebab-case.
Both methods return a Promise
(requires a polyfill for IE 11 and older browser support) which resolve into a value when the modal hides. .msgBoxOk()
always resolves to the value true
, while .msgBoxConfirm()
resolves to either true
(OK button pressed), false
(CANCEL button pressed), or null
(if the modal was closed via backdrop click, Esc press, or some other means.
If message
is not provided, both methods will return immediately with the value undefined
.
You can use either the .then(..).catch(...)
or async await
code styles (async await
requires modern browsers or a transpiler).
OK message box
Example OK Message boxes
<template>
<div>
<div class="m-b-2">
<wui-button @click="showMsgBoxOne">Simple msgBoxOk</wui-button>
Return value: { String(boxOne) }
</div>
<div class="m-b-15">
<wui-button @click="showMsgBoxTwo">msgBoxOk with options</wui-button>
Return value: { String(boxTwo) }
</div>
</div>
</template>
<script>
export default {
data() {
return {
boxOne: "",
boxTwo: "",
};
},
methods: {
showMsgBoxOne() {
this.boxOne = "";
this.$wvModal
.msgBoxOk("Action completed")
.then((value) => {
this.boxOne = value;
})
.catch((err) => {
// An error occurred
});
},
showMsgBoxTwo() {
this.boxTwo = "";
this.$wvModal
.msgBoxOk("Data was submitted successfully", {
title: "Confirmation",
size: "sm",
buttonSize: "sm",
okVariant: "success",
headerClass: "p-10 border-bottom-0",
footerClass: "p-10 border-top-0",
centered: true,
})
.then((value) => {
this.boxTwo = value;
})
.catch((err) => {
// An error occurred
});
},
},
};
</script>
<!-- w-modal-msg-box-ok.vue -->
Confirm message box
Example Confirm Message boxes
<template>
<div>
<div class="m-b-2">
<wui-button @click="showMsgBoxOne">Simple msgBoxConfirm</wui-button>
Return value: { String(boxOne) }
</div>
<div class="m-b-15">
<wui-button @click="showMsgBoxTwo">msgBoxConfirm with options</wui-button>
Return value: { String(boxTwo) }
</div>
</div>
</template>
<script>
export default {
data() {
return {
boxOne: "",
boxTwo: "",
};
},
methods: {
showMsgBoxOne() {
this.boxOne = "";
this.$wvModal
.msgBoxConfirm("Are you sure?")
.then((value) => {
this.boxOne = value;
})
.catch((err) => {
// An error occurred
});
},
showMsgBoxTwo() {
this.boxTwo = "";
this.$wvModal
.msgBoxConfirm("Please confirm that you want to delete everything.", {
title: "Please Confirm",
size: "sm",
buttonSize: "sm",
okVariant: "danger",
okTitle: "YES",
cancelTitle: "NO",
footerClass: "p-10",
hideHeaderClose: false,
centered: true,
})
.then((value) => {
this.boxTwo = value;
})
.catch((err) => {
// An error occurred
});
},
},
};
</script>
<!-- w-modal-msg-box-confirm.vue -->
Message box notes
- The
this.$wvModal
injection is only available when using the fullWuiVue
plugin or theModalPlugin
plugin. It is not available if importing just thewui-modal
component. To just import the injection, use theWVModalPlugin
plugin. - A new
$wvModal
injection (mixin) is created for each Vue virtual machine (i.e. each instantiated component), and is not usable via direct access to theVue.prototype
, as it needs access to the instance'sthis
and$root
contexts. - Message Boxes require
Promise
support in the browser. If targeting your app for older browsers, such as IE 11, please include a polyfill that providesPromise
support. IfPromise
support is not detected, then the message box methods will immediately returnundefined
. - Message Boxes are an extension of the
<wui-modal>
component, and hence support the majority of<wui-modal>
props (using camelCase format), with the exception of the following props:lazy
,static
,busy
,visible
,noStacking
,okOnly
,okDisabled
, andcancelDisabled
. - When a
title
(ortitleHtml
) is not provided in the options, the header will not be shown. - When a
title
(ortitleHtml
) is provided in the options, the header close button is not shown by default. You can enable the header close button by settinghideHeaderClose: false
in the options. - Message Boxes will throw an error (promise rejection) if they are closed/destroyed before they are hidden. Always include a
.catch(errHandler)
reject handler, event if using the asyncawait
style code. - When using Vue Router (or similar), Message Boxes will close and reject if the route changes before the modal hides. If you wish for the message box to remain open when the route changes, use
this.$root.$wvModal
instead ofthis.$wvModal
. - Message boxes cannot be generated during Server Side Rendering (SSR).
- The Message Box
message
currently does not support HTML strings, however, you can pass an array ofVNodes
as themessage
for fine grained control of the markup. You can use Vue'sthis.$createElement
method to generate VNodes. This can also be done for the modal title (by passing VNodes to thetitle
option), OK button text (via theokTitle
option), and the CANCEL button text (via thecancelTitle
option).
Message box advanced usage
When using the this.$wvModal.msgBoxOk(...)
or this.$wvModal.msgBoxConfirm(...)
methods for generating modals, you may want the modal content to be more than just a string message. As mentioned in the message box notes section above, you can pass arrays of VNodes as the message and title for more complex content.
Use Vue's this.$createElement
method to generate VNodes.
<template>
<div>
<wui-button @click="showMsgOk"
>Show OK message box with custom content</wui-button
>
</div>
</template>
<script>
export default {
methods: {
showMsgOk() {
const h = this.$createElement;
// Using HTML string
const titleVNode = h("div", {
domProps: { innerHTML: "Title from <i>HTML<i> string" },
});
// More complex structure
const messageVNode = h("div", { class: ["foobar"] }, [
h("p", { class: ["text--center"] }, [
" Flashy ",
h("strong", "msgBoxOk"),
" message ",
]),
h("p", { class: ["text--center"] }, [h(WuiSpinner)]),
h("p", { class: ["text--center"] }, [
h("img", {
attrs: {
src: "https://picsum.photos/id/20/250/250",
// thumbnail: true,
// center: true,
// fluid: true, rounded: 'circle'
},
}),
]),
]);
// We must pass the generated VNodes as arrays
this.$wvModal.msgBoxOk([messageVNode], {
title: [titleVNode],
buttonSize: "sm",
centered: true,
size: "sm",
});
},
},
};
</script>
<!-- modal-msg-box-advanced.vue -->
Listening to modal changes via $root events
To listen to any modal opening, use:
export default {
mounted() {
this.$root.$on("wv::modal::show", (wvEvent, modalId) => {
console.log("Modal is about to be shown", wvEvent, modalId);
});
},
};
Refer to the Events section of this documentation for the full list of events emitted.
Accessibility
<wui-modal>
provides several accessibility features, including auto focus, return focus, keyboard (tab) focus containment, and automated aria-*
attributes.
Note: The animation effect of this component is dependent on the prefers-reduced-motion
media query. See the reduced motion section of our accessibility documentation for additional details.
Modal ARIA attributes
The aria-labelledby
and aria-describedby
attributes will appear on the modal automatically in most cases.
- The
aria-labelledby
attribute will not be present if you have the header hidden, or supplied your own header, or have not supplied a modal title. It is recommended to supply a title for your modals (when using the built in header). You can visually hide the header title, but still make it available to screen readers by setting thetitle-sr-only
prop. If you do not have a header, you can supply a label for the modal by passing a string to thearia-label
prop. - The
aria-describedby
attribute will always point to the modal's body content. - If the
aria-label
prop is specified with a string value, thearia-labelledby
attribute will not be rendered, even if you have a title/header for your modal.
Auto focus on open
<wui-modal>
will autofocus the modal container when opened.
You can pre-focus an element within the <wui-modal>
by listening to the <wui-modal>
shown
event, and call the element's focus()
method. <wui-modal>
will not attempt to autofocus if an element already has focus within the <wui-modal>
.
<template>
<wui-modal @shown="focusMyElement">
<div>
<wui-button>I Don't Have Focus</wui-button>
</div>
<div>
<wui-form-input></wui-form-input>
</div>
<div>
<!-- Element to gain focus when modal is opened -->
<wui-form-input ref="focusThis"></wui-form-input>
</div>
<div>
<wui-form-input></wui-form-input>
</div>
</wui-modal>
</template>
<script>
export default {
methods: {
focusMyElement() {
this.$refs.focusThis.focus();
},
},
};
</script>
Alternatively, if using wui-form-*
form controls, you can use the autofocus
prop to automatically focus a form control when the modal opens. Note that the autofocus
prop will not work with wui-modal
if the static
prop is used without the lazy
prop set, as autofocus
happens when the wui-form-*
controls are mounted in the DOM.
If you want to auto focus one of the built-in modal buttons (ok
, cancel
or the header close
button, you can set the prop auto-focus-button
to one of the values 'ok'
, 'cancel'
or 'close'
and <wui-modal>
will focus the specified button if it exists. This feature is also available for modal message boxes.
Note: it is not recommended to autofocus an input or control inside of a modal for accessibility reasons, as screen reader users will not know the context of where the input is (the announcement of the modal may not be spoken). It is best to let <wui-modal>
focus the modal's container, allowing the modal information to be spoken to the user, and then allow the user to tab into the input.
Returning focus to the triggering element
For accessibility reasons, it is desirable to return focus to the element that triggered the opening of the modal, when the modal closes.
<wui-modal>
will try and automatically determine which element had focus before the modal was opened, and will return the focus to that element when the modal has hidden if possible. However, several methods and options are provided to allow you to specify the element to return focus to once the modal has hidden.
Specify return focus element via the return-focus
prop
You can also specify an element to return focus to, when modal closes, by setting the return-focus
prop to one of the following:
- A CSS Query Selector string (or an element ID prepended with
#
) - A component reference (which is mounted on a focusable element, such as
<wui-button>
) - A reference to a DOM element that is focusable
If the passed in element is not focusable, then the browser will determine what has focus (usually <body>
, which is not desirable)
This method for returning focus is handy when you use the <wui-modal>
methods show()
and hide()
, or the v-model
prop. Note this property takes precedence over other methods of specifying the return focus element.
Auto return focus
When <wui-modal>
is opened via the v-w-modal
directive on an element, focus will be returned to this element automatically when <wui-modal>
closes, unless an element has been specified via the return-focus
prop.
Specify return focus via event
When using the wv::show::modal
event (emitted on $root
), you can specify a second argument which is the element to return focus to. This argument accepts the same types as the return-focus
prop.
this.$root.$emit("wv::show::modal", "modal-1", "#focusThisOnClose");
Tip: if using a click event (or similar) to trigger modal to open, pass the event's target
property:
<div>
<wui-button @click="$root.$emit('wv::show::modal', 'modal-1', $event.target)"
>Open Modal</wui-button
>
</div>
Note: If the <wui-modal>
has the return-focus
prop set, then the element specified via the event will be ignored.
Keyboard navigation
When tabbing through elements within a <wui-modal>
, if focus attempts to leave the modal into the document, it will be brought back into the modal.
Avoid setting tabindex
on elements within the modal to any value other than 0
or -1
. Doing so will make it difficult for people who rely on assistive technology to navigate and operate page content and can make some of your elements unreachable via keyboard navigation.
If some elements outside the modal need to be focusable (i.e. for TinyMCE), you can add them as CSS selectors to the ignore-enforce-focus-selector
prop 2.4.0+, e.g.:
<wui-modal
id="some-modal-id"
title="Modal with TinyMCE Editor"
ignore-enforce-focus-selector=".tox-tinymce-aux, .moxman-window, .tam-assetmanager-root"
>
<!-- Modal content with TinyMCE editor here -->
</wui-modal>
In some circumstances, you may need to disable the enforce focus feature completely. You can do this by setting the prop no-enforce-focus
, although this is highly discouraged for accessibility reasons.
v-w-modal
directive accessibility
Notes on v-w-modal
directive accessibility:
- If the element is anything other than a
<button>
(or component that renders a<button>
), the ARIArole
will be set tobutton
, and a keydown event listeners for Enter and Space will be added, along with aclick
listener. - If the element is anything other than a
<button>
or<a>
(or a component that renders either), then atabindex
of0
will be added to the element to ensure accessibility, unless there is already atabindex
set.
Component reference
<wui-modal>
<wui-modal>
Component aliases<wui-modal>
Properties<wui-modal>
v-model
<wui-modal>
Slots<wui-modal>
Events
Properties
Property | Type | Default | Description |
---|---|---|---|
aria-label | String | Explicitly supply an 'aria-label' attribute for the modal. Should be set when the modal has no title. When not set 'aria-labelledby' will point to the modal's title | |
auto-focus-button | String | null | Specify which built-in button to focus once the modal opens: 'ok', 'cancel', or 'close' |
body-bg-variant | String | Applies one of the WUI theme color variants to the body background | |
body-class | Array or Object or String | CSS class (or classes) to apply to the '.modal-body' wrapper element | |
body-text-variant | String | Applies one of the WUI theme color variants to the body text | |
busy | Boolean | false | Places the built in default footer OK and Cancel buttons in the disabled state |
button-size | String | Size of the built in footer buttons: 'sm', 'md' (default), or 'lg' | |
cancel-disabled | Boolean | false | Places the default footer Cancel button in the disabled state |
cancel-title | String | 'Cancel' | Text string to place in the default footer Cancel button |
cancel-title-html Use with caution | String | HTML string to place in the default footer Cancel button | |
cancel-variant | String | 'link' | Button color theme variant to apply to the default footer Cancel button |
centered | Boolean | false | Vertically centers the modal in the viewport |
content-class | Array or Object or String | CSS class (or classes) to apply to the '.modal-content' wrapper element | |
dialog-class | Array or Object or String | CSS class (or classes) to apply to the '.modal-dialog' wrapper element | |
footer-bg-variant | String | Applies one of the WUI theme color variants to the footer background | |
footer-border-variant | String | Applies one of the WUI theme color variants to the footer border | |
footer-class | Array or Object or String | CSS class (or classes) to apply to the '.modal-footer' wrapper element | |
footer-tag | String | 'footer' | Specify the HTML tag to render instead of the default tag for the footer |
footer-text-variant | String | Applies one of the WUI theme color variants to the footer text | |
header-bg-variant | String | Applies one of the WUI theme color variants to the header background | |
header-border-variant | String | Applies one of the WUI theme color variants to the header border | |
header-class | Array or Object or String | CSS class (or classes) to apply to the '.modal-header' wrapper element | |
header-close-content | String | '&times;' | Content of the header close button |
header-close-label | String | 'Close' | Value of the 'aria-label' on the header close button |
header-close-variant | String | Text theme color variant to apply to the header close button | |
header-tag | String | 'header' | Specify the HTML tag to render instead of the default tag for the footer |
header-text-variant | String | Applies one of the WUI theme color variants to the header text | |
hide-backdrop | Boolean | false | Disables rendering of the modal backdrop |
hide-footer | Boolean | false | Disables rendering of the modal footer |
hide-header | Boolean | false | Disables rendering of the modal header |
hide-header-close | Boolean | false | Disables rendering of the modal header's close button |
id | String | Used to set the id attribute on the rendered content, and used as the base to generate any additional element IDs as needed | |
ignore-enforce-focus-selector | Array or String | Ignore certain elements from the enforce focus routine, specified by css selector(s) | |
lazy | Boolean | false | When the modal has the static prop set, renders the modal content lazily |
modal-class | Array or Object or String | CSS class (or classes) to apply to the '.modal' wrapper element | |
no-close-on-backdrop | Boolean | false | Disables the ability to close the modal by clicking the backdrop |
no-close-on-esc | Boolean | false | Disables the ability to close the modal by pressing ESC |
no-enforce-focus | Boolean | false | Disables the enforce focus routine which maintains focus inside the modal |
no-fade | Boolean | false | When set to true , disables the fade animation/transition on the component |
no-stacking | Boolean | false | Prevents other modals from stacking over this one |
ok-disabled | Boolean | false | Places the default footer OK button in the disabled state |
ok-only | Boolean | false | Disables rendering of the default footer Cancel button |
ok-title | String | 'OK' | Text string to place in the default footer OK button |
ok-title-html Use with caution | String | HTML string to place in the default footer OK button | |
ok-variant | String | 'primary' | Button color theme variant to apply to the default footer OK button |
return-focus | HTMLElement or Object or String | HTML Element reference, CSS selector, or component reference to return focus to when the modal closes. When not set, will return focus to the element that last had focus before the modal opened | |
scrollable | Boolean | false | Enables scrolling of the modal body |
size | String | 'md' | Set the size of the modal's width. 'sm', 'md' (default), 'lg', or 'xl' |
static | Boolean | false | Renders the content of the component in-place in the DOM, rather than portalling it to be appended to the body element |
title | String | Text content to place in the title | |
title-class | Array or Object or String | CSS class (or classes) to apply to the title | |
title-html Use with caution | String | HTML string content to place in the title | |
title-sr-only | Boolean | false | Wraps the title in an '.sr-only' wrapper |
title-tag | String | 'h5' | Specify the HTML tag to render instead of the default tag for the title |
visible v-model | Boolean | false | The current visibility state of the modal |
v-model
Property | Event |
---|---|
visible | change |
Slots
Name | Scoped | Description |
---|---|---|
default | Yes, see below | Content of modal body. Optionally scoped |
modal-backdrop | No | Content |
modal-cancel | No | Content |
modal-footer | Yes, see below | Content |
modal-header | Yes, see below | Content |
modal-header-close | No | Content |
modal-ok | No | Content |
modal-title | Yes, see below | Content |
Scope for scoped slots
Method or Property | Type | Description |
---|---|---|
cancel | Function | Closes the modal and fires the 'cancel' and 'hide' events, with wvModalEvent.trigger = 'cancel' |
close | Function | Closes the modal and fires the close and hide events, with wvModalEvent.trigger = 'headerclose' |
hide | Function | Accepts one argument trigger . Closes the modal and fires the 'hide' event, with the wvModalEvent.trigger = trigger (trigger is optional) |
ok | Function | Closes the modal and fires the 'ok' and 'hide' events, with wvModalEvent.trigger = 'ok' |
visible | Boolean | The visibility state of the modal. true if the modal is visible and false if not visible |
Events
Event | Arguments | Description |
---|---|---|
cancel | wvModalEvent - WvModalEvent object. Call wvModalEvent.preventDefault() to cancel hide | When default CANCEL button pressed, just before modal has hidden. Cancelable |
change | isVisible - The visibility state of the modal. true if the modal is visible and false if not visible | New modal visibility state. Used to update the v-model |
close | wvModalEvent - WvModalEvent object. Call wvModalEvent.preventDefault() to cancel hide | When default header close button pressed, just before modal has hidden. Cancelable |
hidden | wvModalEvent - WvModalEvent object | Always emits after modal is hidden |
hide | wvModalEvent - WvModalEvent object. Inspect wvModalEvent.trigger to find out what action triggered the hide. Call wvModalEvent.preventDefault() to cancel hide | Always emits just before modal has hidden. Cancelable (as long as modal wasn't forcibly hidden) |
ok | wvModalEvent - WvModalEvent object. Call wvModalEvent.preventDefault() to cancel hide | When default OK button pressed, just before modal has hidden. Cancelable |
show | wvModalEvent - WvModalEvent object. Call wvModalEvent.preventDefault() to cancel show | Always emits just before modal is shown. Cancelable |
shown | wvModalEvent - WvModalEvent object | Always emits when modal is shown |
Importing individual components
You can import individual components into your project via the following named exports:
Component | Named Export |
---|---|
<wui-modal> | WuiModal |
Example
import { WuiModal } from "@wui/wui-vue/lib/modal";
Vue.component("wui-modal", WuiModal);
Importing as a Vue.js plugin
This plugin includes all of the above listed individual components. Plugins also include any component aliases.
Named Export | Import Path |
---|---|
ModalPlugin | @wui/wui-vue/lib/modal |
Example
import { ModalPlugin } from "@wui/wui-vue/lib/modal";
Vue.use(ModalPlugin);