See CModal's accessibility report
Modal
A dialog is a window overlaid on either the primary window or another dialog window. Contents behind a modal dialog are inert meaning that users cannot interact with content behind the dialog.
Import#
Chakra exports eight components to help you create any modal dialog.
CModal: The wrapper that provides context for its childrenCModalOverlay: The dimmed overlay behind the modal dialogCModalContent: The container for the modal dialog's contentCModalHeader: The header that labels the modal dialogCModalFooter: The footer that houses the modal actionsCModalBody: The wrapper that houses the modal's main contentCModalCloseButton: The button that closes the modal.
import {
CModal,
CModalOverlay,
CModalContent,
CModalHeader,
CModalFooter,
CModalBody,
CModalCloseButton,
} from "@chakra-ui/vue";
Usage#
When the modal opens, focus is sent into the modal and set to the first tabbable
element. If there are no tabbable elements, focus is set on the CModalContent.
<template>
<div>
<c-button left-icon="check" mb="3" variant-color="blue" @click="open" variant="outline">Open Modal</c-button>
<c-modal
:is-open="isOpen"
:on-close="close"
>
<c-modal-content ref="content">
<c-modal-header>Modal Title</c-modal-header>
<c-modal-close-button />
<c-modal-body>
<Lorem add="2s" />
</c-modal-body>
<c-modal-footer>
<c-button variant-color="blue" mr="3">
Save
</c-button>
<c-button @click="close">Cancel</c-button>
</c-modal-footer>
</c-modal-content>
<c-modal-overlay />
</c-modal>
</div>
</template>
<script>
export default {
data () {
return {
isOpen: false
}
},
methods: {
open() {
this.isOpen = true
},
close() {
this.isOpen = false
}
}
}
</script>
Block Scrolling when Modal opens#
For accessibility, it's recommended to block scrolling on the main document
behind the modal. Chakra does this by default but you can set
blockScrollOnMount to false to allow scrolling behind modal.
<template>
<div>
<c-button left-icon="check" mb="3" variant-color="blue" @click="open" variant="outline">Open Modal</c-button>
<c-modal
:is-open="isOpen"
:on-close="close"
:block-scroll-on-mount="blockScrollOnMount"
>
<c-modal-content ref="content">
<c-modal-header>Modal Title</c-modal-header>
<c-modal-close-button />
<c-modal-body>
You can scroll the content behind the modal
</c-modal-body>
<c-modal-footer>
<c-button variant-color="blue" mr="3">
Close
</c-button>
<c-button @click="close">Secondary Action</c-button>
</c-modal-footer>
</c-modal-content>
<c-modal-overlay />
</c-modal>
</div>
</template>
<script>
export default {
data () {
return {
isOpen: false,
blockScrollOnMount: false
}
},
methods: {
open() {
this.isOpen = true
},
close() {
this.isOpen = false
}
}
}
</script>
Focus on a specific element#
Chakra automatically sets focus on the first tabbable element in the modal. However, there might be scenarios where you need to manually control where focus goes.
Chakra provides two props for this use-case:
initialFocusRef: Accepts a$refor a function that returns the$refof the component that receives focus when the modal opens.finalFocusRef: Accepts a$refor a function that returns the$refof the component that receives focus when the modal closes.
If you set
finalFocusRef, internally we setreturnFocusOnClosetofalseso it doesn't return focus to the element that triggered it.
<template>
<div>
<c-button mr="3" @click="open">Open Modal</c-button>
<c-button ref="finalRef">
I'll receive focus on close
</c-button>
<c-modal
:initial-focus-ref="() => $refs.initialRef"
:final-focus-ref="() => $refs.finalRef"
:is-open="isOpen"
:on-close="close"
>
<c-modal-content ref="content">
<c-modal-header>Create your account</c-modal-header>
<c-modal-close-button />
<c-modal-body>
<c-form-control>
<c-form-label>First name</c-form-label>
<c-input ref="initialRef" placeholder="First name" />
</c-form-control>
<c-form-control mt="4">
<c-form-label>Last name</c-form-label>
<c-input placeholder="Last name" />
</c-form-control>
</c-modal-body>
<c-modal-footer>
<c-button variant-color="blue" mr="3">
Cancel
</c-button>
<c-button @click="close">Save</c-button>
</c-modal-footer>
</c-modal-content>
<c-modal-overlay />
</c-modal>
</div>
</template>
<script>
export default {
data () {
return {
isOpen: false
}
},
methods: {
open() {
this.isOpen = true
},
close() {
this.isOpen = false
}
}
}
</script>
Close Modal on Overlay Click#
By default, the modal closes when you click its overlay. You can set
closeOnOverlayClick to false if you want the modal to stay visible.
<template>
<div>
<c-button variant-color="blue" @click="open">Open Modal</c-button>
<c-modal
:is-open="isOpen"
:on-close="close"
:closeOnOverlayClick="false"
>
<c-modal-content ref="content">
<c-modal-header>Modal Title</c-modal-header>
<c-modal-close-button />
<c-modal-body>
<Lorem add="2s" />
</c-modal-body>
<c-modal-footer>
<c-button variant-color="blue" mr="3">
Save
</c-button>
<c-button @click="close">Cancel</c-button>
</c-modal-footer>
</c-modal-content>
<c-modal-overlay />
</c-modal>
</div>
</template>
<script>
export default {
data () {
return {
isOpen: false
}
},
methods: {
open() {
this.isOpen = true
},
close() {
this.isOpen = false
}
}
}
</script>
Make modal vertically centered#
By default, the modal has a vertical offset of 3.75rem which you can change by
passing top to the CModalContent. If you need to vertically center the modal,
pass the isCentered
If the content within the modal overflows beyond the viewport, don't use this prop. Try setting the overflow behavior instead
<template>
<div>
<c-button variant-color="blue" @click="open">Open Modal</c-button>
<c-modal
:is-open="isOpen"
:on-close="close"
:closeOnOverlayClick="false"
is-centered
>
<c-modal-content ref="content">
<c-modal-header>Modal Title</c-modal-header>
<c-modal-close-button />
<c-modal-body>
<Lorem add="2s" />
</c-modal-body>
<c-modal-footer>
<c-button @click="close">Cancel</c-button>
</c-modal-footer>
</c-modal-content>
<c-modal-overlay />
</c-modal>
</div>
</template>
<script>
export default {
data () {
return {
isOpen: false
}
},
methods: {
open() {
this.isOpen = true
},
close() {
this.isOpen = false
}
}
}
</script>
Modal overflow behavior#
If the content within the modal overflows beyond the viewport, you can use the
scrollBehavior to control how scrolling should happen.
- If set to
inside, scroll only occurs within theModalBody. - If set to
outside, the entireModalContentwill scroll within the viewport.
<template>
<div>
<c-radio-group
isInline
v-model="scrollBehavior"
>
<c-radio value="inside">inside</c-radio>
<c-radio value="outside">outside</c-radio>
</c-radio-group>
<c-button variant-color="blue" @click="open">Open Modal</c-button>
<c-modal
:is-open="isOpen"
:on-close="close"
:closeOnOverlayClick="false"
:scroll-behavior="scrollBehavior"
is-centered
>
<c-modal-content ref="content" >
<c-modal-header>Modal Title</c-modal-header>
<c-modal-close-button />
<c-modal-body>
<div>
<lorem add="5p" />
</div>
</c-modal-body>
<c-modal-footer>
<c-button @click="close">Cancel</c-button>
</c-modal-footer>
</c-modal-content>
<c-modal-overlay />
</c-modal>
</div>
</template>
<script>
export default {
data () {
return {
isOpen: false,
scrollBehavior: 'inside'
}
},
methods: {
open() {
this.isOpen = true
},
close() {
this.isOpen = false
}
}
}
</script>
Modal Sizes#
Pass the size prop if you need to adjust the size of the modal. Values can be
xs, sm, md, lg, xl, or full.
<template>
<div>
<c-button v-for="(size, i) in sizes" m="4" :key="i" @click="openSizedModal(size)">
Open {{ size }} Modal
</c-button>
<c-modal
:is-open="isOpen"
:on-close="close"
:closeOnOverlayClick="false"
:size="size"
is-centered
>
<c-modal-content ref="content">
<c-modal-header>Modal Title</c-modal-header>
<c-modal-close-button />
<c-modal-body>
<div>
<lorem add="5s" />
</div>
</c-modal-body>
<c-modal-footer>
<c-button @click="close">Cancel</c-button>
</c-modal-footer>
</c-modal-content>
<c-modal-overlay />
</c-modal>
</div>
</template>
<script>
export default {
data () {
return {
isOpen: false,
sizes: ['xs', 'sm', 'md', 'lg', 'xl', 'full'],
size: 'xs'
}
},
methods: {
open() {
this.isOpen = true
},
close() {
this.isOpen = false
},
openSizedModal(size) {
this.size = size
this.open()
}
}
}
</script>
Making other elements Inert#
When the CModal is open, it's rendered within a portal and all its siblings have
aria-hidden set to true so the only thing screen readers see is the modal.
Accessibility#
Keyboard and Focus Management#
- When the modal opens, focus is trapped within it.
- When the dialog opens, focus is automatically set to the first enabled
element, or the element with the
initialFocusRef. - When the modal closes, focus returns to the element that was focused right
before the modal activated, or the element with the
finalFocusRef - Clicking on the overlay closes the Modal.
- Pressing Esc closes the Modal.
- Scrolling is blocked on the elements behind the modal.
- The modal is portalled to the end of
document.bodyto break it out of the source order and make it possible to addaria-hiddento its siblings.
ARIA attributes#
- The
CModalContenthasaria-modalset totrue. - The
CModalContenthasaria-labelledbyset to theidof theCModalHeader - The
CModalContenthasaria-describedbyset to theidof theCModalBody
Props
CModal#
CModal#Props#
| Name | Type | Default | Description |
|---|---|---|---|
| isOpen | boolean | If true, the modal will open | |
| isCentered | boolean | If true, the Modal will be centered on screen | |
| initialFocusRef | vm.$ref, () => vm.ref or CSS selector | The ref of the element to receive focus when the dialog opens | |
| finalFocusRef | vm.$ref, () => vm.ref or CSS selector | The ref of element to receive focus when the dialog closes | |
| blockScrollOnMount | boolean | true | If true, scrolling will be disabled on the body when the modal opens. |
| preserveScrollBarGap | boolean | If true, a padding-right will be applied to the body element to preserve the scrollbar gap. | |
| useInert | boolean | true | A11y: If true, all elements behing the Modal will have aria-hidden set to true so that screen readers can only see the Modal. |
| size | $chakraTheme.sizes | md | The size (maxWidth) of the modal. |
| scrollBehavior | inside, outside | outside | Where scroll behaviour should originate. |
| closeOnOverlayClick | boolean | true | If true, the modal will close when the overlay is clicked |
| returnFocusOnClose | boolean | true | If true, the modal will return focus to the element that triggered it when it closes. |
| closeOnEsc | boolean | true | If true, the modal will close when the Esc key is pressed |
| addAriaLabels | boolean, {header: boolean, body: boolean} | true | If false, no aria-* properties will be added by default. |
| id | string | The id of the modal |
Events#
| Name | Payload | Description |
|---|---|---|
@close | - | The event emitted when the CModalCloseButton is clicked. |
Slots#
| Name | Description |
|---|---|
| default | Slot for CModal components |
Other components#
CModalOverlay,CModalHeader,CModalFooterandCModalBodycompose theCBoxcomponent.CModalCloseButtoncomposesCCloseButton
❤️ Contribute to this page
Caught a mistake or want to contribute to the documentation? Edit this page on GitHub!