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
modeprop ("light","dark", ornullto follow OS/browser preference)
πΎ Installation
npm install @j1nn0/vue-modal-dialogor
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-trapor
yarn add vue @vueuse/core @vueuse/integrations focus-trapThese 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-labelledbypoints to header slotaria-describedbypoints 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
modeprop
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