Dialog
A modal dialog that displays content in a layer above the page. Supports ESC key, overlay click, focus trap, and scroll lock.
#Installation
npx @barefootjs/cli add dialog#Usage
1import {2 Dialog,3 DialogTrigger,4 DialogOverlay,5 DialogContent,6 DialogHeader,7 DialogTitle,8 DialogDescription,9 DialogFooter,10 DialogClose,11} from '@/components/ui/dialog'#Examples
#Delete Confirmation
1"use client"23import { createSignal } from '@barefootjs/client'4import {5 Dialog,6 DialogTrigger,7 DialogOverlay,8 DialogContent,9 DialogHeader,10 DialogTitle,11 DialogDescription,12 DialogFooter,13 DialogClose,14} from '@/components/ui/dialog'1516function DeleteConfirmDialog() {17 const [open, setOpen] = createSignal(false)18 const [confirmText, setConfirmText] = createSignal('')19 const projectName = 'my-project'2021 const isConfirmed = () => confirmText() === projectName2223 const handleOpenChange = (isOpen: boolean) => {24 setOpen(isOpen)25 if (!isOpen) setConfirmText('')26 }2728 return (29 <Dialog open={open()} onOpenChange={handleOpenChange}>30 <DialogTrigger asChild>31 <button class="bg-destructive ...">Delete Project</button>32 </DialogTrigger>33 <DialogOverlay />34 <DialogContent ariaLabelledby="delete-dialog-title" ...>35 <DialogHeader>36 <DialogTitle>Delete Project</DialogTitle>37 <DialogDescription>38 This will permanently delete <strong>{projectName}</strong>.39 </DialogDescription>40 </DialogHeader>41 <div className="py-4">42 <label className="text-sm text-muted-foreground">43 Please type <strong>{projectName}</strong> to confirm.44 </label>45 <input46 type="text"47 value={confirmText()}48 onInput={(e) => setConfirmText(e.target.value)}49 placeholder={projectName}50 className="mt-2 flex h-10 w-full rounded-md border ..."51 />52 </div>53 <DialogFooter>54 <DialogClose>Cancel</DialogClose>55 <button56 onClick={() => { setOpen(false); setConfirmText('') }}57 disabled={!isConfirmed()}58 class="bg-destructive ..."59 >60 Delete Project61 </button>62 </DialogFooter>63 </DialogContent>64 </Dialog>65 )66}#Long Content
1"use client"23import { createSignal } from '@barefootjs/client'4import {5 Dialog,6 DialogTrigger,7 DialogOverlay,8 DialogContent,9 DialogHeader,10 DialogTitle,11 DialogDescription,12 DialogFooter,13 DialogClose,14} from '@/components/ui/dialog'1516function DialogLongContent() {17 const [open, setOpen] = createSignal(false)1819 return (20 <Dialog open={open()} onOpenChange={setOpen}>21 <DialogTrigger>Open Long Content Dialog</DialogTrigger>22 <DialogOverlay />23 <DialogContent24 ariaLabelledby="long-dialog-title"25 ariaDescribedby="long-dialog-description"26 class="max-h-[66vh]"27 >28 <DialogHeader class="flex-shrink-0">29 <DialogTitle id="long-dialog-title">Terms of Service</DialogTitle>30 <DialogDescription id="long-dialog-description">31 Please read the following terms carefully.32 </DialogDescription>33 </DialogHeader>34 <div className="text-sm text-muted-foreground space-y-4 overflow-y-auto flex-1 min-h-0">35 <p>Lorem ipsum dolor sit amet...</p>36 {/* Multiple paragraphs - only this area scrolls */}37 </div>38 <DialogFooter class="flex-shrink-0">39 <DialogClose>Decline</DialogClose>40 <DialogClose>Accept</DialogClose>41 </DialogFooter>42 </DialogContent>43 </Dialog>44 )45}#Accessibility
- ESC key to close - Press Escape to close the dialog
- Click outside to close - Click the overlay to close
- Scroll lock - Body scroll is disabled when dialog is open
- Focus trap - Tab/Shift+Tab cycles within the dialog
- ARIA attributes - role="dialog", aria-modal="true", aria-labelledby, aria-describedby
- Portal rendering - Dialog is mounted to document.body via createPortal
#API Reference
Dialog
| Prop | Type | Default | Description |
|---|---|---|---|
| open | boolean | false | Whether the dialog is open. |
| onOpenChange | (open: boolean) => void | - | Event handler called when the open state should change. |
DialogTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
| disabled | boolean | false | Whether the trigger is disabled. |
| asChild | boolean | false | Render child element as trigger instead of built-in button. Required when the child is itself interactive (e.g. a <Button>) — otherwise the nested button gets auto-closed by the parser and the dialog silently never opens. |
DialogOverlay
| Prop | Type | Default | Description |
|---|
DialogContent
| Prop | Type | Default | Description |
|---|---|---|---|
| ariaLabelledby | string | - | ID of the element that labels the dialog (typically DialogTitle). |
| ariaDescribedby | string | - | ID of the element that describes the dialog (typically DialogDescription). |
DialogTitle
| Prop | Type | Default | Description |
|---|---|---|---|
| id | string | - | ID for aria-labelledby reference. |
DialogDescription
| Prop | Type | Default | Description |
|---|---|---|---|
| id | string | - | ID for aria-describedby reference. |
DialogClose
| Prop | Type | Default | Description |
|---|