QuesoModal
A modal component with comprehensive accessibility support, focus management, and automatic body scroll locking. It uses Vue's Teleport to render at the document body level and provides automatic ARIA attributes and keyboard navigation.
Breaking Change
Version: These breaking changes are introduced in version 0.5.0. If you're upgrading from version 0.4.1 or earlier to 0.5.0, you must follow the migration guide to update your code.
Two breaking changes have been introduced:
- Method names changed:
open()/close()→openModal()/closeModal()for better clarity - New recommended approach: Use the
triggerslot instead ofreffor simpler code
Migration required: Update all references from open → openModal and close → closeModal, and consider migrating to the trigger slot approach. See the QuesoModal migration guide for full examples and steps.
Basic Usage
<template>
<queso-modal>
<template #trigger="{ openModal }">
<button @click="openModal">Open modal</button>
</template>
<template #content>
<p>This is the modal content.</p>
</template>
</queso-modal>
</template>
<script setup lang="ts">
import { QuesoModal } from "@components/QuesoModal";
</script>Legacy Usage
Warning
The method below is still supported for backward compatibility, but the trigger slot approach is preferred for new code. Use it only if you need programmatic control from multiple places or complex conditional logic.
See the QuesoModal migration guide for detailed examples and migration steps.
Show example code
<template>
<button @click="openModal()">Open modal</button>
<queso-modal ref="myModal">
<p>This is the modal content.</p>
</queso-modal>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { QuesoModal } from "@components/QuesoModal";
const myModal = ref<InstanceType<typeof QuesoModal> | null>(null);
const openModal = () => {
myModal.value?.openModal();
};
const closeModal = () => {
myModal.value?.closeModal();
};
</script>Props
hasOverlay
- Type:
boolean - Default:
true - Description: Whether to show the overlay when the modal is open.
isScrollLocked
- Type:
boolean - Default:
true - Description: Whether to lock the scroll of the document body when the modal is open
Emits
modal:open
- Payload:
void - Description: Emitted when the modal is opened.
modal:close
- Payload:
void - Description: Emitted when the modal is closed.
Slots
trigger
- Props:
{ isModalOpen: boolean, openModal: () => void, closeModal: () => void } - Description: The trigger element that opens the modal. This slot is rendered outside the Teleport, allowing you to place the trigger anywhere in your component. Use the
openModalfunction to open the modal when the trigger is clicked.
default
- Props:
{ isModalOpen: boolean, openModal: () => void, closeModal: () => void } - Description: The main modal content.
beforeContent
- Props:
{ isModalOpen: boolean, openModal: () => void, closeModal: () => void } - Description: Content to display before the main modal content.
content (alias for default)
- Props:
{ isModalOpen: boolean, openModal: () => void, closeModal: () => void } - Description: The main modal content. This slot is an alias for the default slot - you can use either
#contentor#defaultslot.
afterContent
- Props:
{ isModalOpen: boolean, openModal: () => void, closeModal: () => void } - Description: Content to display after the main modal content.
overlay
- Props:
{ isModalOpen: boolean, openModal: () => void, closeModal: () => void } - Description: Custom overlay component. Defaults to
<queso-modal-overlay />. Only displayed ifhasOverlayistrue.
Exposed Data
isModalOpen
Reactive boolean indicating the current open state.
openModal()
Opens the modal and activates focus trapping.
closeModal()
Closes the modal and deactivates focus trapping.
Behavior
- Teleport to Body: Modal is automatically teleported to the document body
- Scroll Locking: Automatically prevents body scrolling when modal is open
- Keyboard Support: Escape key closes the modal
- Accessibility: Complete ARIA attributes and screen reader support
- Overlay: Default overlay component with click-to-close functionality
Accessibility
The component automatically handles:
aria-expandedattribute for screen readers- Focus trapping within the modal when it contains focusable elements
- Escape key support for closing
- Proper semantic structure
- Body scroll locking to prevent background interaction
Focus Management
The modal automatically detects if it contains focusable elements and manages focus accordingly:
- With focusable elements: Activates focus trap to keep focus within the modal
- Without focusable elements: No focus trap activation
- Focusable selectors: Includes links, inputs, buttons, and other interactive elements
CSS
Classes
The component applies the following CSS classes:
.queso-modal- Base class.is-modal-open- Applied when modal is open.queso-modal__content- Content container
Custom Properties
--queso-modal-opacity- Modal opacity (default: 0, changes to 1 when open)--queso-modal-max-width- Content max width (default: 80%)--queso-modal-max-height- Content max height (default: 80%)--queso-modal-z- Modal z-index (default: 400)
Type Declaration
export type QuesoModalProps = {
hasOverlay?: boolean;
isScrollLocked?: boolean;
};
// Methods
export type QuesoModalOpen = () => void;
export type QuesoModalClose = () => void;
export interface QuesoModalMethods {
openModal: QuesoModalOpen;
closeModal: QuesoModalClose;
}