For a list of actions on an element take CdsMenu, for a short hint — CdsTooltip, for a notification about the result — CdsToast. Complex modal content — a header, scrollable text and a row of buttons — is assembled from CdsCard.
Inside the modal works CdsOverlay: it renders the content on top of the page, dims it, blocks scrolling and keeps the focus inside the modal — Tab will not get you out, and after closing the focus returns to the activator. That is why the opening and positioning props of the modal are shared with the overlay.
Variations
Activators
The modal is opened by an activator element — you can set it in three ways:
- Through the
activatorslot — the props from the slot (v-bind="props") attach the handlers and the aria attributes to the element. The default way. - Through the parent —
activator="parent"takes the element thatCdsModalis placed inside of. - Through a selector —
activator="#id"links the modal to any element on the page, even a distant one in the markup.
Using v-model
The state of the modal can be controlled directly through v-model — without an activator. This is how you open a modal from code: on a server response, from a handler in a table, from a store.
Sizes
The size prop sets the width of the modal: xs (250), sm (350), md (500, the default), lg (700), xl — almost the full width of the screen. A number instead of an alias is an exact width in pixels. The height always adapts to the content, and width and max-width override the size when you need a non-standard grid.
Persistent modals
The persistent prop turns off closing by a click outside the modal and by the Esc key — the modal can only be closed with the buttons inside it. A click outside shakes the modal, hinting that an answer is still required; the animation is removed by no-click-animation. Take persistent when closing would lose data or when the user has to make a choice.
Scrolling inside the modal
By default long content scrolls together with the whole modal. The scrollable prop pins the header and the buttons and scrolls only the middle — for that the content is assembled from CdsCard: the card becomes the surface of the modal, and the scrolling area is its CdsCardText.
Fullscreen
The fullscreen prop expands the modal to the whole screen: the size props are ignored, the paddings and the rounded corners are removed. It fits document viewing and complex forms on small screens.
Animations
By default the modal grows out of the activator and shrinks back — so you can see where it came from. The transition prop changes the animation: the ready modal-transition-top and modal-transition-bottom slide the modal in from the edge of the screen, and any custom CSS animation works too.
Slots
| Slot | Description |
|---|---|
activator | The element that opens the modal. The slot props (props) attach the handlers to it |
default | The content of the modal. The slot exposes isActive — use it to close the modal without an external variable |
Guidelines
Scenarios
Delete confirmation
An irreversible action is confirmed with a compact size="sm" modal: the title names the action, the text specifies what exactly will be deleted, the main button repeats the verb from the title. One modal serves the whole list — the selected record is put into it right before opening.
A form in a modal
A short form is opened on top of the list so that the user is not taken away to a separate page. persistent protects what has been typed from an accidental click outside, the submit button shows loading, and "Cancel" is blocked while the request is running. The fields are reset in the after-leave event — after the closing animation, so that the values do not flash in a still visible modal.
An agreement with scrolling
A document that requires an explicit answer is shown in a modal with persistent and scrollable: the text scrolls, and the "Accept" and "Decline" buttons are always in sight. Closing the modal without answering is not possible — the decision is recorded only with the buttons.
API
Props
| Name | Type | Default | Description |
|---|---|---|---|
absolute | boolean | undefined | — | Positions the element absolutely instead of fixed. This is useful for overlays that must stay inside a certain area, for example inside a container with position: relative. |
activator | (string & {}) | Element | "parent" | ComponentPublicInstance | undefined | — | The element that activates the component |
activatorProps | Record<string, any> | undefined | {} | Props for the activator element |
attach | string | boolean | Element | undefined | — | Sets the DOM element the overlay content is moved to. It accepts a direct element reference, a querySelector string, or true to disable moving. By default the body element is used. Changing this prop is usually not recommended except for edge cases: the default positioning algorithm handles most scenarios better than it can without moving, and unexpected behavior may appear if the menu ends up being a child of its activator. |
captureFocus | boolean | undefined | true | Returns the focus back into the content if it has left it — for example on Tab from the end of the page. A mouse click does not steal the focus. |
closeDelay | string | number | undefined | — | Delay in milliseconds before the component closes. Works only together with open-on-hover. |
closeOnBack | boolean | undefined | true | Closes the overlay content when the browser Back button is pressed or $router.back() is called, cancelling the original navigation. persistent overlays cancel the navigation and animate as if they were clicked outside instead of closing. |
closeOnContentClick | boolean | undefined | — | Hide the component on a click on the content |
contained | boolean | undefined | — | Limits the size of the component and of its grid to the offset parent. Implies the absolute and attach props. (Note: the parent element must have position: relative.) |
contentClass | any | — | Applies a custom class to the content element. This is handy because the content is moved to the beginning of the cds-app component (unless attach is set) and cannot be selected with classes passed to the component directly. |
contentProps | any | — | Applies custom props to the moved content |
disabled | boolean | null | undefined | null | Disabled state |
eager | boolean | undefined | — | Renders the content of the component right away. Useful when you have content that is not in the DOM but has to be indexed for SEO. |
fullscreen | boolean | undefined | — | Expands the modal to the whole screen. |
height | string | number | undefined | — | Component height |
location | Anchor | undefined | 'center center' | Sets the anchor point used to position the component, aligning it horizontally, vertically or in both directions |
locationStrategy | "static" | "connected" | LocationStrategyFunction | undefined | 'static' | Sets the way the overlay content is positioned. By default static is used, which centers the content in its container. Use connected to attach the content to the activator element. |
maxHeight | string | number | undefined | — | Maximum component height |
maxWidth | string | number | undefined | — | Maximum component width |
minHeight | string | number | undefined | — | Minimum component height |
minWidth | string | number | undefined | — | Minimum component width |
modelValue | boolean | undefined | — | The v-model value of the component. If the component supports the multiple prop, an empty array is used by default. |
noClickAnimation | boolean | undefined | — | Disables the bounce effect on a click outside the content element when persistent is set. |
offset | string | number | number[] | undefined | — | Increases the distance to the target. If two numbers are passed, the second one shifts the anchor sideways, away from the target. |
opacity | string | number | undefined | — | Opacity of the scrim element. Applies only when scrim=true. |
openDelay | string | number | undefined | — | Delay in milliseconds before the component opens. Works only together with open-on-hover. |
openOnClick | boolean | undefined | — | Show the component on a click on the activator |
openOnFocus | boolean | undefined | — | Show the component when the activator gets focus |
openOnHover | boolean | undefined | — | Show the component on hover over the activator |
origin | Anchor | "auto" | "overlap" | undefined | 'center center' | Sets the anchor point of the overlay content that is aligned with the location anchor of the target. With "auto" the opposite side of the location point is used. With "overlap" the same anchor point is used, so the overlay overlaps the target. It also sets the CSS transform-origin property for the enter/leave transitions. |
persistent | boolean | undefined | — | A click outside the element or the Esc key does not close it. |
retainFocus | boolean | undefined | true | Traps Tab navigation inside the content: from the last focusable element the focus moves to the first one and back. |
scrim | string | boolean | undefined | true | Backdrop shown while the overlay is active. Accepts true/false to toggle the backdrop and a string to set its color. |
scrollable | boolean | undefined | — | Enables scrolling of the content inside the modal. |
scrollStrategy | "none" | "close" | "block" | "reposition" | ScrollStrategyFunction | undefined | 'block' | Strategy applied when the component is active and the user scrolls the page. |
size | number | "xs" | "sm" | "md" | "lg" | "xl" | undefined | 'md' | Size of the component |
stickToTarget | boolean | undefined | — | Lets the overlay content go off screen while scrolling. |
target | (string & {}) | Element | "parent" | ComponentPublicInstance | "cursor" | [x: number, y: number] | undefined | — | When locationStrategy is set to "connected", specify the element or the array of x, y coordinates the overlay is positioned against. By default the activator element is used. |
theme | string | undefined | — | Component theme |
transition | string | boolean | (TransitionProps & { component?: Component | undefined; }) | { component: Component; } | null | undefined | { component: CdsModalTransition } | Transition animation |
viewportMargin | string | number | undefined | 12 | Sets a custom viewport margin for the overlay content |
width | string | number | undefined | — | Component width |
zIndex | string | number | undefined | 2400 | The z-index value of the component. |
Events
| Name | Type | Description |
|---|---|---|
afterEnter | [] | Fired after the opening animation of the modal has finished. |
afterLeave | [] | Fired after the closing animation of the modal has finished. |
update:modelValue | [value: boolean] | Fired when the state of the modal changes. |
Slots
| Name | Type | Description |
|---|---|---|
activator | { isActive: boolean; props: Record<string, any>; targetRef: TemplateRef; } | Slot for the element that acts as the activator of the overlay. |
default | { isActive: Ref<boolean, boolean>; } | Slot for the overlay content. |
Exposed
| Name | Type | Description |
|---|---|---|
activatorEl | HTMLElement | undefined | The DOM element of the activator. |
activatorRef | TemplateRef | Template ref for assigning the activator manually. |
animateClick | () => void | Runs the bounce animation of the content — the same as on a click outside a persistent overlay. |
contentEl | HTMLElement | undefined | The DOM element of the overlay content. |
globalTop | boolean | Whether the overlay is the top one in the global stack of open overlays. |
rootEl | HTMLElement | undefined | The root DOM element of the overlay. |
scrimEl | HTMLElement | undefined | The DOM element of the scrim that dims the page. |
targetEl | HTMLElement | undefined | The DOM element of the positioning target. |
targetRef | TemplateRef | Template ref for assigning the positioning target manually. |
updateLocation | ((e: Event) => void) | undefined | Recalculates the position of the content relative to the target. |