Dialog
Dialogs are elements that are overlaid on a web page or app in order to present necessary information and tasks. Dialogs are also sometimes referred to as modals or overlays.
Guidelines
When to use dialogs
Dialogs facilitate communication between the system and user. They perform best when used for urgent decisions or as a workflow within a bigger task, as they don’t require loading a new page and keep actions in context.
Dialogs can be intentionally disruptive, since the user needs to interact with or close the dialog before moving on. For this reason, they should be used sparingly and only when necessary (more information below).
When to use:
- When the user needs to make a decision or provide input to the system before continuing with the task at hand.
- When additional information needs to be displayed and separated from the page content.
- When the user needs to provide additional confirmation before taking an action.
When not to use:
- When the information can be displayed inline within the main interface.
- When the information is not important enough to interrupt the user's flow.
- When the content within the dialog is so long that it requires scrolling. In this case, split the dialog content into different steps or provide it on a dedicated page.
- When dealing with a lengthy form comprising numerous form fields and actions. In this case it's more appropriate to create the form on a separate page.
Specifications
A dialog contains:
- Title
All dialogs should include a title. This should be a short, one-line overview of the purpose of the dialog. - Subtitle (optional)
A subtitle can be used to provide additional information about the dialog. - Body
Any type of content or components can be included within the dialog’s body. - Footer text (optional)
Further text can be included above the action buttons, in order to provide additional information (e.g. terms and conditions to read before publishing). - Permanent action (optional)
A permanent action can be included (e.g. “Don’t show again”). Main action: A primary button (either progressive or destructive) is used to indicate the main action. - Main action A primary button (either progressive or destructive) is used to indicate the main action.
- Secondary action (optional)
A normal neutral button can be used to indicate a secondary action (e.g. “Cancel”). - Close button (optional)
A quiet, icon-only button may be used to close the dialog. It can also be replaced with a text button in some cases. - Overlay Behind every dialog, there is an overlay that displays the color White (#fff) at 65% opacity. This is to provide continued context while the user focuses on the dialog.
Component limitations
Keep the content of the dialog concise as needed, while making sure it includes a title, body text, and at least one button. If the body content exceeds the available space, a scrollbar will be displayed. Additionally, top and bottom dividers will appear to distinguish the body from the header and footer. The padding on these sections will differ from the default state. The subtitle will not be visible while scrolling.
All dialogs are vertically and horizontally centered on the canvas. We aim to keep dialogs at a fixed width of size-3200
(equivalent to 512px
in the default Codex theme) on desktop, while allowing them to use 90% of the width on mobile web. This makes dialogs the focus of the screen.
Stacked actions
Footer actions may stack depending on the length of the text. If the text of a button or a permanent action exceeds the available space, the permanent action will be positioned above the buttons. In cases where button texts are long, prioritize the main button over the secondary one and ensure that the buttons occupy the entire width.
Closing
A dialog can be dismissed by:
- The close button (X)
- A dismissive action like “Cancel“
- Tapping or clicking anywhere outside of the dialog on the background
- Pressing the key Esc
Refer to the Dialog component in Codex Figma.
Interaction states
Buttons may be disabled until a required action is completed.
Best practices
Consider the following recommendations when using dialogs.
Buttons
- Use both progressive and destructive buttons as primary action within the dialog.
- Place the primary button before the secondary one.
- Use a non-primary button as the main action.
- Stack footer actions based on text length when needed, giving priority to the primary button over secondary ones if required.
- Stack buttons when they can be placed side by side.
- Include at least one button in the Dialog's footer to guide the user, even if the Dialog is purely informative.
- Remove all buttons from the Dialog's footer.
Body content
- Replace the body content with other components or group of elements.
- Replace the body content with cards or other elevated components.
Custom Header and Footer
- Customize the header and footer with custom buttons and styles.
- Ensure the main primary button remains in the footer.
- Strive for consistency with the rest of the system and designs.
- Always use a quiet button for the close button of the dialog.
- Use styles not present in other system elements or projects.
- Alter the weight of each button in the dialog.
Content
Simple dialogs are for confirmations and information that the user needs in order to continue. It is easier for users to move through the flow when they know what to do from the title and CTAs.
Keyboard navigation
Key | Function |
---|---|
Tab | It moves the focus to the next interactive element in tab order within the Dialog. |
Shift + Tab | It moves the focus to the previous interactive element within the Dialog. |
Enter | If the focus is placed on one of the Dialog’s buttons, it activates the button. |
Esc | It closes the Dialog. |
Demos
Configurable Dialog
Name | Value |
---|---|
Props | |
title | |
subtitle | |
hideTitle | |
useCloseButton | |
stackedActions | |
usePrimaryAction | |
primaryActionLabel | |
primaryActionType | |
primaryActionDisabled | |
useDefaultAction | |
defaultActionLabel | |
defaultActionDisabled | |
Slots | |
default | |
footer-text | |
View | |
Reading direction |
Basic example
This example includes a title, close button, primary action, and default action.
With expandable menus
Expandable menus, like the one used by the Select component, will extend past the end of the dialog frame (instead of being cut off by it).
With overflowing content
When content in the default slot (the dialog body) is longer than the available space, the body section will scroll while the dialog header and footer will remain in view.
With stacked actions
When action button text is long, use the stackedActions
prop to stack the action buttons vertically.
With optional Footer Text
The footer-text
slot can accept plain text, links, and basic formatting markup; do not use it to provide images or block-level elements. All content provided is wrapped inside of a <p>
tag. Use this slot for situations like showing a disclaimer, linking to help or legal documentation, etc. The footer-text
content will appear above the dialog actions.
With custom Header and Footer
By default, the Dialog displays a header with an optional title, subtitle, and close button, and a footer with optional buttons and footer text.
The entire content of the header and footer can be replaced with user-provided markup (by using the #header
and #footer
slots, respectively). This allows for the creation of one-off custom dialogs as well as variant Dialog components that wrap the base Dialog with some additional content and styling.
Reusable custom Dialog example
The example above demonstrates a unique Dialog instance, suitable for a one-off dialog. For a re-usable custom dialog, consider using a wrapper component.
A wrapper component could pre-apply certain customizations (a consistent custom header or footer, for example) while still accepting <slot>
content from the user that gets forwarded to the Dialog's own slots.
An example of how to write such a component can be found below. This example relies on Codex's useModelWrapper
composable to pass a v-model
binding from the parent context down to the inner Dialog component.
Usage of the custom component would look like this:
<wrapped-dialog title="Custom dialog header" v-model:open="wrappedDialogState">
Custom dialog content.
</wrapped-dialog>
Vue usage
The Dialog overtakes the user's entire viewport until it is dismissed, preventing mouse and keyboard interaction with other parts of the page while open. This is a significant interruption in the user experience, so this component should be used with care.
The parent component controls whether the Dialog is open via v-model:open
.
A Dialog can offer two kinds of actions (represented by buttons of the appropriate type): primary action (can be progressive or destructive), and default action (typically a safe option like "cancel").
When open, the Dialog adds a class to the document body to prevent scrolling; this is applied whether or not teleport is used.
Attributes passed to inner element
This component forwards any attributes applied by the user to the inner .cdx-dialog
element, instead of applying them to the outermost backdrop element.
Dialog and <teleport>
Dialogs rely on Vue's built-in <teleport>
feature, and a "target" prop can be supplied which will be passed to the teleport's to
prop. This prop is optional and defaults to the <body>
element on the page (although if Dialog is being used with SSR, a dedicated target should be provided).
An alternative default target can be set using Vue's provide/inject feature, with provide( 'CdxTeleportTarget', '#my-teleport-target' )
. This provided target will be used if the "target" prop is not set.
Finally, Dialog teleportation behavior can be disabled by setting renderInPlace: true
.
The examples on this page are all wrapped with Vitepress's built-in <client-only>
component, since the Codex documentation site (built with Vitepress) uses SSR. Other SSRed applications will need to do something similar (only rendering Dialog after the mounted
hook has been fired, etc.).
Use of the Dialog component in features which don't rely on SSR (which includes all MediaWiki usage for now) can dispense with this.
Props
Prop name | Description | Type | Default |
---|---|---|---|
open | Whether the dialog is visible. Should be provided via a v-model:open binding in the parent scope. | boolean | false |
title (required) | Title for the dialog header. Used for ARIA purposes even if no visible header element is displayed. | string | |
subtitle | Optional subtitle for the dialog. | string | null |
hideTitle | Whether the dialog header should hide the title & subtitle | boolean | false |
useCloseButton | Add an icon-only close button to the dialog header. | boolean | false |
closeButtonLabel | Visually-hidden label text for the icon-only close button in the header. Omit this prop to use the default value, "Close". | string | '' |
primaryAction | Primary user action. This will display a primary button with the specified action (progressive or destructive). | PrimaryDialogAction | null |
defaultAction | Default user action. This will display a normal button. | DialogAction | null |
stackedActions | Whether action buttons should be vertically stacked and 100% width. | boolean | false |
target | Selector or DOM element identifying the container the dialog should be rendered in. The dialog will be <teleport> ed to this element. An ID selector is recommended, e.g. #foo-bar , but providing an actual element is also supported.If this prop is not set, and the parent or one of its ancestors provides a teleport target using provide( 'CdxTeleportTarget', '#foo-bar' ) , the provided target will be used. If there is no provided target, the dialog will be teleported to the end of the <body> element. | string|HTMLElement|null | null |
renderInPlace | Whether to disable the use of teleport and render the Dialog in its original location in the document. If this is true, the target prop is ignored. | boolean | false |
Events
Event name | Properties | Description |
---|---|---|
primary | When the primary action button is clicked. | |
default | When the default action button is clicked. | |
update:open | newValue boolean - The new open/close state (true for open, false for closed) | When the open/close state changes, e.g. when the close button is clicked. |
Slots
Name | Description | Bindings |
---|---|---|
header | Customizable Dialog header | |
default | Dialog content | |
footer | Customizable Dialog footer | |
footer-text | Optional footer text |