Display overlay area on top of a page, represents a modal or non-modal dialog box. Build on top of the native HTML <dialog/>
element.
import {Dialog} from ' @kaiverse/k/ui '
Basic usage
Some examples of how to use the Dialog component.
Simple dialog
Open Dialog Dialog content Lorem ipsum, dolor sit amet consectetur adipisicing elit. const [ openDialog , setOpenDialog ] = useState ( false )
< button type = " button " onClick = { () => setOpenDialog ( true ) } >
< Dialog open = { openDialog } onClose = { () => setOpenDialog ( false ) } >
< Dialog.Title > Dialog header </ Dialog.Title >
< Dialog.Content > Dialog content </ Dialog.Content >
< button type = " button " onClick = { () => setOpenDialog ( false ) } >
When submitting a <form>
that is nested within a <Dialog>
,
you can close that <Dialog>
by using form method method="dialog"
or set formmethod="dialog"
to the <button>
used to submit the form.
Also as a result, the onClose
event will be fired.
Combination - Controlled by React state
If you use a React state to control the open
state of <Dialog>
, even though the dialog is closed by the form method, remember to set that state to false
via the onClose
event.
The <Dialog>
component can’t and shouldn’t directly manipulate outside/parent React state(s) by itself.
Let use the drawer
variant for this example.
Open Drawer Submitted form values: {
"keyword": ""
} Section 2: Very long content here, scroll down ↓ for more Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis minus velit non quibusdam? Magni labore consequatur accusantium eum exercitationem esse velit, ex saepe nesciunt itaque quibusdam autem? Sequi, aut dolorem.
Section 3: No idea Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis minus velit non quibusdam? Magni labore consequatur accusantium eum exercitationem esse velit, ex saepe nesciunt itaque quibusdam autem? Sequi, aut dolorem.
Section 4: Ok there are no contents Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis minus velit non quibusdam? Magni labore consequatur accusantium eum exercitationem esse velit, ex saepe nesciunt itaque quibusdam autem? Sequi, aut dolorem.
import {Dialog} from ' @kaiverse/k/ui '
import {useState, type FormEventHandler} from ' react '
import {useDisclosure} from ' @kaiverse/k/hooks '
import JunkArticle from ' ./junk-articles '
export default function DialogControlledByForm () {
const [ opened , { open , close }] = useDisclosure ( false )
const [ keyword , setKeyword ] = useState ( '' ) // just for display form value
const handleFormSubmit : FormEventHandler < HTMLFormElement > = ( e ) => {
const formData = new FormData (e . currentTarget )
setKeyword ((formData . get ( ' keyword ' ) as string ) || '' )
if ( ! formData . get ( ' keepFormValue ' )) {
< button className = " btn btn-neutral " type = " button " onClick = { open } >
< section className = " my-4 " >
< strong > Submitted form values: </ strong >
< pre className = " p-2 w-full rounded-lg bg-base-100 whitespace-pre-wrap [overflow-wrap:anywhere] " >
{ JSON . stringify ({keyword} , null , 2 ) }
< Dialog open = { opened } variant = " drawer " offset = " 1.5rem " onClose = { close } >
< Dialog.Title > Drawer controlled by form </ Dialog.Title >
< Dialog.Content className = " py-0 " >
className = " space-y-4 border-2 border-neutral-200 rounded-lg p-4 "
id = " form-manipulate-drawer "
onSubmit = { handleFormSubmit }
< h3 className = " sticky sticky-bg inset-x-0 top-0 " > Drawer Form </ h3 >
In this form example, all inputs are uncontrolled input.
Check the "Keep form value" checkbox to persist the form value after submitting and
< label className = " form-control w-full max-w-xs " >
< span className = " label label-text " > Keyword </ span >
className = " input input-bordered w-full max-w-xs "
placeholder = " Type some keyword "
< label className = " items-center flex gap-2 cursor-pointer " >
< span className = " label-text " > Keep form value: </ span >
< input className = " checkbox " name = " keepFormValue " type = " checkbox " />
< JunkArticle /> { /* Just to make the drawer's content long */ }
className = " btn btn-neutral btn-outline "
form = " form-manipulate-drawer "
onClick = { close } // remove this line if you only want to reset the form value
< button className = " btn btn-secondary " form = " form-manipulate-drawer " type = " submit " >
Compound components
Dialog.Header
— a fully customizable header, usually contains a title and the close button.
Dialog.Title
— render a h2
element.
Dialog.CloseButton
— a button that closes the Dialog
, this component must be used inside the Dialog
.
Dialog.Content
— a wrapper for the main content of the Dialog
.
Dialog.Footer
— bottom section, usually contains actions.
Uh… No thanks!
For instance, here is a drawer with similar UI as the previous “Controlled by form” example that doesn’t use any compound components.
Open Drawer Drawer controlled by form Section 2: Very long content here, scroll down ↓ for more Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis minus velit non quibusdam? Magni labore consequatur accusantium eum exercitationem esse velit, ex saepe nesciunt itaque quibusdam autem? Sequi, aut dolorem.
Section 3: No idea Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis minus velit non quibusdam? Magni labore consequatur accusantium eum exercitationem esse velit, ex saepe nesciunt itaque quibusdam autem? Sequi, aut dolorem.
Section 4: Ok there are no contents Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis minus velit non quibusdam? Magni labore consequatur accusantium eum exercitationem esse velit, ex saepe nesciunt itaque quibusdam autem? Sequi, aut dolorem.
import {useDisclosure} from ' @kaiverse/k/hooks '
import {Dialog} from ' @kaiverse/k/ui '
import JunkArticle from ' ./junk-articles '
import {IconX} from ' ../icon '
export default function WithoutCompoundComponents () {
const [ opened , { open , close }] = useDisclosure ( false )
< button className = " btn btn-neutral " type = " button " onClick = { open } >
className = " [&[open]]:flex flex-col "
< header className = " p-4 flex items-center justify-between gap-4 " >
< h2 > Drawer controlled by form </ h2 >
< button className = " btn btn-ghost btn-circle btn-sm " type = " button " onClick = { close } >
< article className = " flex-1 px-4 overflow-y-auto " >
className = " space-y-4 border-2 border-neutral-200 rounded-lg p-4 "
id = " form-dialog-without-compound "
< h3 className = " sticky inset-x-0 top-0 sticky-bg " > Drawer Form </ h3 >
< p > In this form example, all inputs are uncontrolled input. </ p >
< label className = " form-control w-full max-w-xs " >
< span className = " label label-text " > Keyword </ span >
className = " input input-bordered w-full max-w-xs "
placeholder = " Type something... "
< JunkArticle /> { /* Just to make the drawer's content long */ }
< footer className = " p-4 flex justify-between items-center " >
className = " btn btn-neutral btn-outline "
form = " form-dialog-without-compound "
onClick = { close } // remove this line if you only want to reset the form value
< button className = " btn btn-secondary " form = " form-dialog-without-compound " type = " submit " >
Use in server components
Note
Dialog compound components and the <Dialog>
itself have 'use client';
directive at the top of the file.
import {Dialog} from ' @kaiverse/k/ui '
export default function ClientComponent () {
< Dialog.Title > Dialog header </ Dialog.Title >
< Dialog.Content > Dialog content </ Dialog.Content >
< Dialog.Footer > Dialog Footer </ Dialog.Footer >
import {Dialog, DialogHeader, DialogTitle, DialogCloseButton, DialogContent, DialogFooter} from ' @kaiverse/k/ui '
export default function ServerComponent () {
< DialogTitle > Dialog header </ DialogTitle >
< DialogContent > Dialog content </ DialogContent >
< DialogFooter > Dialog Footer </ DialogFooter >
Variants
drawer
A Dialog that slides in from the side of the screen.
Position
Position of the drawer. It only available when variant is set to 'drawer'
.
Open at the top Open on the left Open at the bottom Open on the right
Position
< Dialog variant = " drawer " position = " ... " />
mobile
WIP
A variant that utilizes modern web technologies to replicate the iOS drawer (sheets) experience on the web.
Coming soon…
non-modal
mode
A Dialog that has no backdrop and also doesn’t render on the top-layer. It can NOT be closed by pressing the ESC
key and the below page content can be interacted.
Open Dialog Count++ Read more about non-modal mode and related accessibility considerations.Count: 0
Some page content You can interact with the content below the non-modal dialog. Try click the 2 buttons above.
< Dialog dialogMode = " non-modal " />
Accessibility
Dialog
component build on top of the HTML <dialog/>
element.
By default, it respects the default accessibility behavior and settings of a <dialog/>
element.
You can opt-out some behaviors by using the preventFocus
and preventClose
props .
Keyboard interactions
Key Description Escape Close the Dialog
. This behavior will be disabled if dialogMode
is 'non-modal'
. Spacebar Trigger focusing element.eg: The close button when a Dialog just opened, and the button that opens the Dialog after closing it. You can continue press Spacebar to open/close a Dialog.
Tab | Shift + Tab
Cycles through all the focusable elements of the Dialog
only.
Customization
CSS Variables
Variable Default Description --k-dialog-animation-timing-fn
cubic-bezier(0.32, 0.72, 0, 1)
Dialog opening and closing stages animation timing function. --k-dialog-open-animation-duration
500ms
Dialog opening animation duration. --k-dialog-close-animation-duration
200ms
Dialog closing animation duration. --k-dialog-transition-timing-fn
cubic-bezier(0.32, 0.72, 0, 1)
Dialog opening and closing stages transition timing function. --k-dialog-open-transition-duration
200ms
Opening transition duration of the Dialog and its backdrop.It affects opacity
, and discrete transitions (display
, overlay
) . --k-dialog-close-transition-duration
200ms
Same as --k-dialog-open-transition-duration
but for the closing stage. --k-dialog-offset
0
Dialog’s offset from the edge of current viewport. --k-dialog-backdrop-bg
rgba(0, 0, 0, 0.4)
Backdrop background. --k-dialog-backdrop-blur
0
Backdrop blur effect. It will be used by backdrop-filter
’s blur
function. --k-dialog-backdrop-opacity
1
Backdrop opacity.
Offset
You can set the offset
prop to adjust the Dialog’s position from the edge of the viewport.
Styling Backdrop
We can customize the Dialog’s backdrop by
Using the backdropProps
prop.
Update related CSS variables .
Apply styles directly to the ::backdrop
pseudo-element.
Open styled drawer We can use backdropProps
to style the Dialog's backdrop. className = " bg-base-100/90 "
background: ' linear-gradient(-25deg,rgba(238,174,202,0.6) 0%,rgba(148,187,233,0.6) 100%) ' ,
< Dialog.Header className = " italic " >
< Dialog.Title > Drawer header </ Dialog.Title >
< Dialog.Content className = " [&_code]:text-info " >
We can use < code > backdropProps </ code > to style the Dialog's backdrop.
< footer className = " bg-[radial-gradient(circle,rgba(34,193,195,0.4)_0%,rgba(253,187,45,0.2)_100%)] " >
Special Types
Name Type Description BackdropProps
{background?: string; blur?: string | number; opacity?: number}
backdropProps
prop’s options.DialogStylingSelectors
DEPRECATED 'header'
| 'content'
| 'footer'
Available selectors of special styles APIs: classNames
, styles
.
<Dialog>
Props
Name Type Default Description variant
'default'
| 'drawer'
'default'
dialogMode
'modal'
| 'non-modal'
'modal'
'modal'
(1) 'non-modal'
(2) preventFocus
boolean
false
If true
, disable a default behavior of <dialog>
element: Browser won’t autofocus on the first nested focusable element anymore. preventClose
boolean
false
If true
, the Dialog won’t close when users press Escape
key or click/tap on the backdrop. backdropProps
BackdropProps
background: rgba(0,0,0,0.4)
blur: 0
opacity: 1
Styling Dialog’s backdrop. offset
number
| string
0
(px)Dialog’s offset from the edge of current viewport. classNames
DEPRECATED Partial<Record<DialogStylingSelectors, string>>
— Applies className to related selector element. styles
DEPRECATED Partial<Record<DialogStylingSelectors, CSSProperties>>
— styles[selector]
applies inline styles to related selector element ONLY if its style
property has not been provided.
Variant 'drawer'
Name Type Default Description position
'right'
| 'bottom'
| 'left'
| 'top'
'right'
Side of the screen where the 'drawer'
variant will be opened.