Modal Component
An accessible modal/dialog component with focus trapping, keyboard navigation, and backdrop overlay.
Add this component
The command below includes <strong>Modal</strong>—run it in your project directory to install this component (and the CSS if needed). No prompts.
Choose your package manager — click a tab to select, then copy the command.
pnpm dlx rizzo-css add Modal
Modal Sizes
Modals come in three sizes: small, medium (default), and large. Click the buttons below to see each size:
Live Examples
This is a small modal (24rem / 384px max-width). Perfect for:
- Confirmation dialogs
- Simple alerts
- Quick actions
This is a medium modal (32rem / 512px max-width) - the default size. Great for:
- Standard forms
- Content dialogs
- Most use cases
This is a large modal (48rem / 768px max-width). Ideal for:
- Complex forms
- Detailed content
- Multi-step processes
- Rich media content
Large modals provide more space for content while still maintaining a focused, centered dialog experience.
Standard Example
Live Example
Click the button below to open a standard modal dialog:
This is an example modal dialog. It demonstrates:
- Focus trapping - Tab cycles within the modal
- Keyboard navigation - Escape key closes the modal
- Backdrop overlay with blur effect
- Theme-aware styling
Usage
Usage
Full example for each framework (Astro, Vanilla, Svelte, Vue, React). Switch framework via View as or by clicking a Usage tab—both stay in sync.
Astro Vanilla Svelte Vue React ---
import Modal from '../../../components/astro/Modal.astro';
import Button from '../../../components/astro/Button.astro';
---
<Modal id="example-modal" title="Example Modal" size="md">
<p>Modal content goes here.</p>
<div slot="footer">
<Button onclick="window.closeModal_example_modal()">Cancel</Button>
<Button class="btn-primary" onclick="window.closeModal_example_modal()">Confirm</Button>
</div>
</Modal>
<Button onclick="window.openModal_example_modal()">Open Modal</Button>
<!-- Use .modal-root so overlay stacks behind the modal. Ensure Rizzo CSS + js/main.js. Overlay id: `<modalId>-overlay`. -->
<div class="modal-root">
<div class="modal__overlay" id="example-modal-overlay" aria-hidden="true"></div>
<div class="modal modal--md" id="example-modal" role="dialog" aria-modal="true" aria-labelledby="example-modal-title" aria-hidden="true">
<div class="modal__header">
<h2 id="example-modal-title" class="modal__title">Example Modal</h2>
<button type="button" class="modal__close" data-modal-close aria-label="Close">×</button>
</div>
<div class="modal__body"><p>Modal content goes here.</p></div>
<div class="modal__footer">
<button type="button" class="btn" onclick="window.closeModal_example_modal()">Cancel</button>
<button type="button" class="btn btn-primary" onclick="window.closeModal_example_modal()">Confirm</button>
</div>
</div>
</div>
<button type="button" class="btn btn-primary" data-modal-open="example-modal">Open Modal</button>
<script>
import { Modal, Button } from '$lib/rizzo';
</script>
<Modal id="example-modal" title="Example Modal" size="md">
<p>Modal content goes here.</p>
<div slot="footer">
<Button onclick="window.closeModal_example_modal()">Cancel</Button>
<Button class="btn-primary" onclick="window.closeModal_example_modal()">Confirm</Button>
</div>
</Modal>
<Button onclick="window.openModal_example_modal()">Open Modal</Button>
<script setup>
import { ref } from 'vue';
import Modal from '@/components/rizzo/Modal.vue';
import Button from '@/components/rizzo/Button.vue';
</script>
<template>
<Button @click="open = true">Open modal</Button>
<Modal v-model:open="open" title="Modal title">
<p>Modal content.</p>
</Modal>
</template>
import { useState } from 'react';
import { Modal, Button } from './components/react';
const [open, setOpen] = useState(false);
<Button onClick={() => setOpen(true)}>Open modal</Button>
<Modal open={open} onOpenChange={setOpen} title="Modal title">
<p>Modal content.</p>
</Modal>
Props
id (string, optional) - Unique ID for the modal (auto-generated if not provided) title (string, optional) - Modal title (default: "Modal") size ('sm' | 'md' | 'lg', optional) - Modal size (default: "md") open (boolean, optional) - Whether modal is open by default (default: false) closeOnOverlayClick (boolean, optional) - Close when clicking overlay (default: true) closeOnEscape (boolean, optional) - Close on Escape key (default: true) class (string, optional) - Additional CSS classes
Slots
- Default slot - Main modal content (goes in
modal__body) - footer - Footer content with action buttons
Programmatic Control
Each modal exposes global functions based on its ID (hyphens converted to underscores):
// Open modal (ID: example-modal becomes example_modal)
window.openModal_example_modal();
// Close modal
window.closeModal_example_modal();
Features
- Full keyboard accessibility (Tab, Shift+Tab, Escape)
- Focus trapping - focus stays within modal when open
- ARIA attributes for screen readers
- Backdrop overlay with blur effect
- Responsive design (mobile-friendly)
- Theme-aware styling using semantic variables
- Respects
prefers-reduced-motion
Size Variants
Control the modal width using the size prop:
sm - Small (24rem / 384px max-width) - Best for confirmation dialogs and simple alerts md - Medium (32rem / 512px max-width) - Default size, great for most use cases lg - Large (48rem / 768px max-width) - Ideal for complex forms and detailed content
Note: On mobile devices (≤640px), all modals automatically use 95vw width for better mobile experience.
Other frameworks: Vanilla · Svelte · Vue · React