MagicModal
MagicModal is a flexible, unstyled modal component. Useful for things like confirmation dialogs, detail views, and forms.
<template>
<m-button @click="modalApi.open">Open Modal</m-button>
<magic-modal id="magic-modal-default-demo" :options="{ focusTrap: false }">
<div class="bg-surface-high aspect-4/3 w-[40rem] rounded-md" />
</magic-modal>
</template>
<script lang="ts" setup>
import { MButton } from '@maas/mirror/vue'
import { useMagicModal } from '@maas/vue-equipment/plugins/MagicModal'
const modalApi = useMagicModal('magic-modal-default-demo')
</script>Overview
Anatomy
MagicModal can be used as a single self-contained component or composed from its individual parts for full control.
Simple
<template>
<magic-modal id="your-modal-id">
<!-- your content -->
</magic-modal>
</template>
<script setup>
const { open } = useMagicModal('your-modal-id')
</script>Composed
<template>
<magic-modal-provider id="your-modal-id">
<magic-modal-trigger as-child>
<button>Open</button>
</magic-modal-trigger>
<magic-modal-teleport>
<magic-modal-backdrop />
<magic-modal-content>
<!-- your content -->
</magic-modal-content>
</magic-modal-teleport>
</magic-modal-provider>
</template>TIP
MagicModalTeleport is optional. Omit it to keep the modal mounted at all times, with visibility controlled via v-show.
Installation
CLI
Add @maas/vue-equipment to your dependencies.
pnpm install @maas/vue-equipmentnpm install @maas/vue-equipmentyarn add @maas/vue-equipmentbun install @maas/vue-equipmentVue
If you are using Vue, import and add MagicModalPlugin to your app.
import { createApp } from 'vue'
import { MagicModalPlugin } from '@maas/vue-equipment/plugins/MagicModal'
const app = createApp({})
app.use(MagicModalPlugin)Nuxt
The modal is available as a Nuxt module. In your Nuxt config file add @maas/vue-equipment/nuxt to your modules and add MagicModal to the plugins in your configuration.
export default defineNuxtConfig({
modules: ['@maas/vue-equipment/nuxt'],
vueEquipment: {
plugins: ['MagicModal'],
},
})Direct Import
If you prefer a more granular approach, components can be directly imported.
<script setup>
import {
MagicModalProvider,
MagicModalTeleport,
MagicModalBackdrop,
MagicModalContent,
MagicModalTrigger,
} from '@maas/vue-equipment/plugins/MagicModal'
</script>Composable
In order to interact with the modal from anywhere within your app, we provide a useMagicModal composable. Import it directly when needed.
import { onMounted } from 'vue'
import { useMagicModal } from '@maas/vue-equipment/plugins/MagicModal'
const { open } = useMagicModal('your-modal-id')
onMounted(() => {
open()
})TIP
If you have installed the modal as a Nuxt module, the composable will be auto-imported and is automatically available in your Nuxt app.
Peer Dependencies
If you haven’t installed the required peer dependencies automatically, you’ll need to install the following packages manually.
Installation
pnpm install @nuxt/kit @vueuse/core @vueuse/integrations defu focus-trapnpm install @nuxt/kit @vueuse/core @vueuse/integrations defu focus-trapyarn add @nuxt/kit @vueuse/core @vueuse/integrations defu focus-trapbun install @nuxt/kit @vueuse/core @vueuse/integrations defu focus-trapAPI Reference
MagicModalProvider
The MagicModalProvider wraps the modal and configures all child components according to the provided options.
Props
| Prop | Type | Required |
|---|---|---|
MaybeRef<string> | true | |
MagicModalOptions | false |
Options
To customize the modal, override the necessary options. Any custom options will be merged with the default options.
| Option | Type | Default |
|---|---|---|
'dialog' | ||
boolean | FocusTrapOptions | object | |
boolean | object | object | |
boolean | true | |
string | 'body' | |
boolean | false | |
string | 'magic-modal-content' | |
string | 'magic-modal-backdrop' | |
string[] | false | ['Escape'] |
MagicModalTeleport
Teleports all child components to a target in the DOM. Uses v-if to mount and unmount its contents when the modal opens and closes — keeping the DOM clean when the modal is inactive. Omit this component if you want the modal to remain mounted at all times.
Props
| Prop | Type | Required |
|---|---|---|
string | RendererElement | false | |
boolean | false |
MagicModalBackdrop
Renders a full-viewport overlay behind the modal panel. Closes the modal when clicked.
CSS Variables
| Variable | Default |
|---|---|
--magic-modal-backdrop-position | fixed |
--magic-modal-backdrop-top | 0 |
--magic-modal-backdrop-left | 0 |
--magic-modal-backdrop-right | 0 |
--magic-modal-backdrop-bottom | 0 |
--magic-modal-backdrop-width | 100% |
--magic-modal-backdrop-height | 100% |
--magic-modal-backdrop-color | rgba(0, 0, 0, 0.5) |
--magic-modal-backdrop-filter | unset |
--magic-modal-backdrop-z-index | 998 |
MagicModalContent
Renders the modal panel with focus trap and scroll lock support.
CSS Variables
| Variable | Default |
|---|---|
--magic-modal-position | fixed |
--magic-modal-inset | 0 |
--magic-modal-width | 100% |
--magic-modal-height | 100% |
--magic-modal-display | flex |
--magic-modal-justify-content | center |
--magic-modal-align-items | center |
--magic-modal-z-index | 999 |
--magic-modal-content-max-height | 100% |
--magic-modal-content-width | 100% |
--magic-modal-content-display | flex |
--magic-modal-content-align-items | center |
--magic-modal-content-justify-content | center |
--magic-modal-content-overflow-y | auto |
MagicModalTrigger
Opens or closes the modal on click.
Props
| Prop | Type | Required |
|---|---|---|
MaybeRef<string> | false | |
boolean | false | |
boolean | false |
Slot Props
| Prop | Type | Description |
|---|---|---|
active | boolean | Whether the modal is currently open. |
disabled | boolean | Whether the trigger is currently disabled. |
MagicModal
A self-contained component that composes all primitives internally. Use this for simple cases where you don’t need to customise the markup.
Props
| Prop | Type | Required |
|---|---|---|
MaybeRef<string> | true | |
MagicModalOptions | false |
Slots
| Slot | Description |
|---|---|
default | Content rendered inside the modal panel. |
backdrop | Content rendered inside the backdrop element. |
Errors
| Source | Error Code | Message |
|---|---|---|
MagicModalTeleport | missing_instance_id | MagicModalTeleport must be nested inside MagicModalProvider |
MagicModalBackdrop | missing_instance_id | MagicModalBackdrop must be nested inside MagicModalProvider |
MagicModalContent | missing_instance_id | MagicModalContent must be nested inside MagicModalProvider |
MagicModalTrigger | missing_instance_id | MagicModalTrigger must be nested inside MagicModalProvider or an id must be provided |
Examples
Default
<template>
<m-button @click="modalApi.open">Open Modal</m-button>
<magic-modal id="magic-modal-default-demo" :options="{ focusTrap: false }">
<div class="bg-surface-high aspect-4/3 w-[40rem] rounded-md" />
</magic-modal>
</template>
<script lang="ts" setup>
import { MButton } from '@maas/mirror/vue'
import { useMagicModal } from '@maas/vue-equipment/plugins/MagicModal'
const modalApi = useMagicModal('magic-modal-default-demo')
</script>Composed
<template>
<magic-modal-provider id="magic-modal-composed-demo" :options="{ focusTrap: false }">
<magic-modal-trigger as-child>
<m-button>Open Modal</m-button>
</magic-modal-trigger>
<magic-modal-teleport>
<magic-modal-backdrop />
<magic-modal-content>
<div class="bg-surface-high aspect-[4/3] w-[40rem] rounded-md" />
</magic-modal-content>
</magic-modal-teleport>
</magic-modal-provider>
</template>
<script lang="ts" setup>
import { MButton } from '@maas/mirror/vue'
import {
MagicModalProvider,
MagicModalTeleport,
MagicModalBackdrop,
MagicModalContent,
MagicModalTrigger,
} from '@maas/vue-equipment/plugins/MagicModal'
</script>