Package Exports
- @j1nn0/vue-modal-dialog
- @j1nn0/vue-modal-dialog/dist/vue-modal-dialog.es.js
- @j1nn0/vue-modal-dialog/dist/vue-modal-dialog.umd.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@j1nn0/vue-modal-dialog) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
vue-modal-dialog
A reusable Vue 3 modal dialog component with focus trap and ARIA accessibility support.
π¦ Project Info
βοΈ Build & Quality
π Tech Stack
π Table of Contents
- β¨ Features
- πΎ Installation
- βοΈ Peer Dependencies
- π Usage
- π CDN Usage
- π Props
- π Slots
- βΏ Accessibility
- π¨ Styles
- π Notes on Multiple Modals
- π· License
β¨ Features
- Vue 3 support
- Focus trap inside the modal
- Keyboard accessibility (Escape to close)
- Backdrop with blur and fade animation
- Header, body, and footer slots
- Optional footer slot
- Close button in the header
- Configurable dialog size:
sm
,md
,lg
,fullscreen
- Configurable dialog width (supports custom widths via width prop for flexible layouts)
- Supports dark mode and light mode via the
mode
prop ("light"
,"dark"
, ornull
to follow OS/browser preference)
πΎ Installation
npm install @j1nn0/vue-modal-dialog
or
yarn add @j1nn0/vue-modal-dialog
βοΈ Peer Dependencies
Before using this component, make sure you have installed the following peer dependencies:
npm install vue @vueuse/core @vueuse/integrations focus-trap
or
yarn add vue @vueuse/core @vueuse/integrations focus-trap
These dependencies are required for the library to function properly.
π Usage
You can use this component in two ways:
- Import individually (recommended, enables tree-shaking)
- Register globally as a Vue plugin
1οΈβ£ Individual Import (recommended)
<script setup>
import { ref } from 'vue';
import { VueModalDialog } from '@j1nn0/vue-modal-dialog';
import '@j1nn0/vue-modal-dialog/dist/vue-modal-dialog.css';
const isOpen = ref(false);
const submitForm = () => {
alert('Form submitted!');
isOpen.value = false;
};
</script>
<template>
<button @click="isOpen = true">Open Dialog</button>
<VueModalDialog v-model="isOpen">
<!-- Header slot -->
<template #header> Dialog Title </template>
<!-- Body slot (default) -->
<p>
This is the body content of the dialog. It supports long text and will wrap automatically.
</p>
<!-- Footer slot (optional) -->
<template #footer>
<button @click="isOpen = false">Cancel</button>
<button @click="submitForm">Submit</button>
</template>
</VueModalDialog>
</template>
2οΈβ£ Global Plugin Registration
// main.js
import { createApp } from 'vue';
import App from './App.vue';
import { VueModalDialogPlugin } from '@j1nn0/vue-modal-dialog';
import '@j1nn0/vue-modal-dialog/dist/vue-modal-dialog.css';
const app = createApp(App);
// Registers globally as <VueModalDialog> by default
app.use(VueModalDialogPlugin);
// Or specify a custom name
// app.use(VueModalDialogPlugin, { name: 'CustomName' });
app.mount('#app');
Use <VueModalDialog>
(or your custom name) anywhere in your app without importing it:
<template>
<VueModalDialog v-model="isOpen">
<template #header> Global Dialog </template>
<p>Body content</p>
</VueModalDialog>
</template>
π CDN Usage
You can use @j1nn0/vue-modal-dialog
via CDN without any bundler. Both individual import and global plugin usage are supported.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue Modal Dialog CDN Example</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script src="https://unpkg.com/tabbable/dist/index.umd.js"></script>
<script src="https://unpkg.com/focus-trap/dist/focus-trap.umd.js"></script>
<script src="https://unpkg.com/@vueuse/shared"></script>
<script src="https://unpkg.com/@vueuse/core"></script>
<script src="https://unpkg.com/@vueuse/integrations"></script>
<link
rel="stylesheet"
href="https://unpkg.com/@j1nn0/vue-modal-dialog/dist/vue-modal-dialog.css"
/>
<script src="https://unpkg.com/@j1nn0/vue-modal-dialog/dist/vue-modal-dialog.umd.js"></script>
</head>
<body>
<div id="app">
<!-- Individual Import -->
<button type="button" @click="isOpenImport = true">Open Import Dialog</button>
<vue-modal-dialog v-model="isOpenImport">
<template #header>Import Dialog Title</template>
<p>Body content goes here</p>
<template #footer>
<button @click="isOpenImport = false">Close</button>
</template>
</vue-modal-dialog>
<!-- Global Plugin -->
<button type="button" @click="isOpenGlobal = true">Open Global Dialog</button>
<global-plugin-modal-dialog v-model="isOpenGlobal">
<template #header>Global Dialog Title</template>
<p>Body content goes here</p>
<template #footer>
<button @click="isOpenGlobal = false">Close</button>
</template>
</global-plugin-modal-dialog>
</div>
<script>
const { createApp, ref } = Vue;
const { VueModalDialogPlugin, VueModalDialog } = J1nn0VueModalDialog;
const app = createApp({
setup() {
const isOpenImport = ref(false);
const isOpenGlobal = ref(false);
return { isOpenImport, isOpenGlobal };
},
});
// Individual import registration
app.component('VueModalDialog', VueModalDialog);
// Global plugin registration (default name: 'VueModalDialog')
app.use(VueModalDialogPlugin, { name: 'GlobalPluginModalDialog' });
app.mount('#app');
</script>
</body>
</html>
π Props
Prop | Type | Default | Description |
---|---|---|---|
backdrop |
Boolean | String |
true |
true = click on backdrop closes dialog, "static" = backdrop does nothing |
escape |
Boolean |
true |
Pressing Escape key closes the dialog |
position |
String |
"center" |
Position of the dialog: "center" or "top" |
width |
String |
"md" |
Dialog width. Presets: sm , md , lg , fullscreen . Also supports custom CSS width, e.g. "400px" , "50%" , "80vw" |
mode |
String | null |
null |
Dialog color mode: "light" for light mode, "dark" for dark mode, null to follow the OS/browser preference |
π Slots
Slot | Description |
---|---|
header |
Optional. Content for the header. Γ button always present |
default | Content for the body of the dialog |
footer |
Optional. Content for footer, not rendered if empty |
βΏ Accessibility
role="dialog"
+aria-modal="true"
aria-labelledby
points to header slotaria-describedby
points to body slot- Close button has
aria-label="Close"
- Focus trap inside the dialog ensures keyboard navigation
- Escape key closes the dialog if enabled
π¨ Styles
- Dialog width:
sm
,md
,lg
,fullscreen
- Dialog height: auto, max
80vh
(default), scrollable if content overflows - Word wrapping enabled in header and body
- Backdrop has fade-in/out animation with blur effect
- Supports Light and Dark mode via
mode
prop
CSS Custom Properties
:root {
/* Backdrop */
--j1nn0-vue-modal-dialog-backdrop-z-index: 1000;
--j1nn0-vue-modal-dialog-backdrop-background: rgba(0, 0, 0, 0.6);
--j1nn0-vue-modal-dialog-backdrop-blur: 2px;
/* Dialog */
--j1nn0-vue-modal-dialog-border: none;
--j1nn0-vue-modal-dialog-border-radius: 8px;
--j1nn0-vue-modal-dialog-width: 90%;
--j1nn0-vue-modal-dialog-max-width-sm: 300px;
--j1nn0-vue-modal-dialog-max-width-md: 600px;
--j1nn0-vue-modal-dialog-max-width-lg: 900px;
--j1nn0-vue-modal-dialog-max-height: 80vh;
--j1nn0-vue-modal-dialog-text-color: #000000;
/* Header */
--j1nn0-vue-modal-dialog-header-background: #f5f5f5;
--j1nn0-vue-modal-dialog-header-padding: 1rem;
/* Body */
--j1nn0-vue-modal-dialog-body-background: #fff;
--j1nn0-vue-modal-dialog-body-padding: 1rem;
/* Footer */
--j1nn0-vue-modal-dialog-footer-background: #f5f5f5;
--j1nn0-vue-modal-dialog-footer-padding: 1rem;
/* Dark mode */
--j1nn0-vue-modal-dialog-backdrop-background-dark: rgba(255, 255, 255, 0.2);
--j1nn0-vue-modal-dialog-border-dark: none;
--j1nn0-vue-modal-dialog-header-background-dark: #1f2937;
--j1nn0-vue-modal-dialog-footer-background-dark: #1f2937;
--j1nn0-vue-modal-dialog-body-background-dark: #111827;
--j1nn0-vue-modal-dialog-text-color-dark: #f9fafb;
}
π Notes on Multiple Modals
This library is designed with the assumption that only one modal is open at a time.
Opening multiple modals simultaneously may cause the following issues:
- Backdrops stacking, making the screen too dark
- Focus trap not functioning correctly
- Escape key behavior becoming ambiguous
- Accessibility attributes (e.g.
aria-hidden
) breaking
Therefore, it is recommended to control modals on the application side so that only one is visible at any given time.
At present, supporting multiple simultaneous modals is not planned.
If there is strong demand, it may be considered in the future.
π· License
MIT License
Copyright Β© 2025βPRESENT j1nn0