Skip to content

CdsModal

A modal opens on top of the page and keeps the focus on a single task: confirm an action, fill in a short form, look at the details — without taking you away from the current screen. While the modal is open, the page behind it is dimmed, does not scroll and does not accept clicks.

Storybook GitLab
Test coverage: 100% — good coverage
API in specs
58 / 58 props, events, slots and exposed members with a test
Test cases
45 unit · 4 browser
Known bugs
2 pinned by an it.fails test
Code coverage
100% lines · 95.5% branches measured in v4.0.0-alpha.13
A11y (axe)
9 recommendations in 11 stories measured in v4.0.0-alpha.11

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:

  1. Through the activator slot — the props from the slot (v-bind="props") attach the handlers and the aria attributes to the element. The default way.
  2. Through the parent — activator="parent" takes the element that CdsModal is placed inside of.
  3. 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 ​

SlotDescription
activatorThe element that opens the modal. The slot props (props) attach the handlers to it
defaultThe 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

NameTypeDefaultDescription
absoluteboolean | 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
activatorPropsRecord<string, any> | undefined{}Props for the activator element
attachstring | 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.
captureFocusboolean | undefinedtrueReturns 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.
closeDelaystring | number | undefined—Delay in milliseconds before the component closes. Works only together with open-on-hover.
closeOnBackboolean | undefinedtrueCloses 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.
closeOnContentClickboolean | undefined—Hide the component on a click on the content
containedboolean | 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.)
contentClassany—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.
contentPropsany—Applies custom props to the moved content
disabledboolean | null | undefinednullDisabled state
eagerboolean | 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.
fullscreenboolean | undefined—Expands the modal to the whole screen.
heightstring | number | undefined—Component height
locationAnchor | 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.
maxHeightstring | number | undefined—Maximum component height
maxWidthstring | number | undefined—Maximum component width
minHeightstring | number | undefined—Minimum component height
minWidthstring | number | undefined—Minimum component width
modelValueboolean | undefined—The v-model value of the component. If the component supports the multiple prop, an empty array is used by default.
noClickAnimationboolean | undefined—Disables the bounce effect on a click outside the content element when persistent is set.
offsetstring | 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.
opacitystring | number | undefined—Opacity of the scrim element. Applies only when scrim=true.
openDelaystring | number | undefined—Delay in milliseconds before the component opens. Works only together with open-on-hover.
openOnClickboolean | undefined—Show the component on a click on the activator
openOnFocusboolean | undefined—Show the component when the activator gets focus
openOnHoverboolean | undefined—Show the component on hover over the activator
originAnchor | "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.
persistentboolean | undefined—A click outside the element or the Esc key does not close it.
retainFocusboolean | undefinedtrueTraps Tab navigation inside the content: from the last focusable element the focus moves to the first one and back.
scrimstring | boolean | undefinedtrueBackdrop shown while the overlay is active. Accepts true/false to toggle the backdrop and a string to set its color.
scrollableboolean | 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.
sizenumber | "xs" | "sm" | "md" | "lg" | "xl" | undefined'md'Size of the component
stickToTargetboolean | 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.
themestring | undefined—Component theme
transitionstring | boolean | (TransitionProps & { component?: Component | undefined; }) | { component: Component; } | null | undefined{ component: CdsModalTransition }Transition animation
viewportMarginstring | number | undefined12Sets a custom viewport margin for the overlay content
widthstring | number | undefined—Component width
zIndexstring | number | undefined2400The z-index value of the component.

Events

NameTypeDescription
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

NameTypeDescription
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

NameTypeDescription
activatorElHTMLElement | undefinedThe DOM element of the activator.
activatorRefTemplateRefTemplate ref for assigning the activator manually.
animateClick() => voidRuns the bounce animation of the content — the same as on a click outside a persistent overlay.
contentElHTMLElement | undefinedThe DOM element of the overlay content.
globalTopbooleanWhether the overlay is the top one in the global stack of open overlays.
rootElHTMLElement | undefinedThe root DOM element of the overlay.
scrimElHTMLElement | undefinedThe DOM element of the scrim that dims the page.
targetElHTMLElement | undefinedThe DOM element of the positioning target.
targetRefTemplateRefTemplate ref for assigning the positioning target manually.
updateLocation((e: Event) => void) | undefinedRecalculates the position of the content relative to the target.