<c-modal>
Modals interrupt the page for a task that must be completed (or explicitly cancelled) before continuing — confirmations, short forms, destructive-action warnings. The slotted content is typically a `c-card`.
Usage
Open state is controlled: bind value (v-model) and react to changeValue.
Modals may be stacked — opening a second modal makes it the active one; the
one beneath is dimmed and inert until it becomes topmost again.
When to use
- Confirming a destructive or irreversible action.
- A short, focused task that must not be left half-done.
When not to use
- Passive notifications — use
c-toasts, which stays visible and clickable above any open modal. - Large multi-step flows — prefer a dedicated page.
Dismissal
dismissable governs both light-dismiss gestures: clicking the backdrop and
pressing Escape. A non-dismissable modal (the default) responds to either
with a nudge animation instead of closing — it can only be closed by an
explicit action inside it. Escape peels overlays innermost-first: with a
menu or select open inside the modal, the first press closes that overlay,
the next one reaches the modal.
Accessibility
- Always set
aria-labelon thec-modalelement — the dialog's title lives in your slotted content, across a shadow boundary the platform'saria-labelledbycannot reach, so the label is mirrored from the host. Opening an unlabeled modal logs a console warning. - Focus moves to the first
[autofocus]element in your content if present, else the first focusable element, else the dialog itself. Putautofocuson the least destructive action of a confirmation dialog. - On close, focus returns to the element that was focused when the modal opened.
- Everything outside the active modal is made
inert— except toast notifications, which stay interactive.
Scrolling
Page scroll is locked while any modal is open. If your page has a visible
document scrollbar, add scrollbar-gutter: stable to the page root to avoid
a layout shift when the scrollbar disappears.
Layering
Modals do not use the browser top layer: fixed-position
c-toasts placed at or near body level always paint above every modal and
remain fully interactive. Transient popovers (menus, selects, autocomplete
panels) use the top layer and paint above everything, including modals they
are opened from. There is no z-index knob — paint order is managed by the
library.
Customization
Restyle via CSS parts from your own stylesheet:
c-modal::part(root) {
padding: 0;
}
c-modal::part(backdrop) {
background: rgb(0 0 0 / 0.7);
}
Examples
<template>
<div class="example-row">
<c-button @click="open = true">Open modal</c-button>
<c-modal v-model="open" dismissable>
<c-card>
<c-card-title>Delete project</c-card-title>
<c-card-content>
<p>This action cannot be undone.</p>
</c-card-content>
<c-card-actions justify="end">
<c-button text @click="open = false">Cancel</c-button>
<c-button danger @click="open = false">Delete</c-button>
</c-card-actions>
</c-card>
</c-modal>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const open = ref(false);
</script>
API reference
<c-modal>
Modals interrupt the page for a task that must be completed (or explicitly cancelled) before continuing — confirmations, short forms, destructive-action warnings. The slotted content is typically a `c-card`.
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
disableBackdropBlur | disable-backdrop-blur | boolean | false | Disable backdrop blur effect |
dismissable | dismissable | boolean | false | Dismissed when touching/clicking outside the content or pressing Escape. A non-dismissable modal responds to either gesture with a nudge animation instead of closing. |
value | value | boolean | false | Is the modal visible |
width | width | number | string | 600 | Width of the dialog. Numeric value is considered as pixel value (400 -> 400px) |
Events
| Event | Detail | Description |
|---|---|---|
changeValue | boolean | Fired when the modal dismisses itself — a backdrop click or the Escape key on a `dismissable` modal, or a platform-initiated close (e.g. a slotted `<form method="dialog">`). The detail is the new open state, always `false`. Also dispatched as `change-value` — bind that name in Vue templates. |
input | void | Native bubbling input event dispatched alongside every value change so a plain `v-model` stays in sync. Carries no detail. |
update:value | boolean | Fired alongside `changeValue` with the same detail — the `v-model` contract. |
Slots
| Slot | Description |
|---|---|
default | The modal contents (typically a c-card) |
CSS parts
Style from outside with c-modal::part(name) — parts are the library's only styling customization API.
| Part | Description |
|---|---|
backdrop | The dimming overlay under the dialog; visible only while this modal is the active (topmost) one |
root | The native dialog element forming the modal box |