0
# Overlays and Modals
1
2
Overlay components including dialogs, popovers, tooltips, and modals with proper focus management, positioning, and accessibility. All overlays handle escape key, click outside, and focus restoration.
3
4
## Capabilities
5
6
### Dialog
7
8
Provides modal dialog behavior with focus containment and accessibility.
9
10
```typescript { .api }
11
/**
12
* Provides dialog behavior and accessibility
13
* @param props - Dialog configuration
14
* @param ref - Ref to the dialog element
15
* @returns Dialog props
16
*/
17
function useDialog(props: AriaDialogProps, ref: RefObject<Element>): DialogAria;
18
19
interface AriaDialogProps {
20
/** Role of the dialog */
21
role?: 'dialog' | 'alertdialog';
22
/** ID of the dialog */
23
id?: string;
24
/** Accessible name for the dialog */
25
'aria-label'?: string;
26
/** ID of element that labels the dialog */
27
'aria-labelledby'?: string;
28
/** ID of element that describes the dialog */
29
'aria-describedby'?: string;
30
}
31
32
interface DialogAria {
33
/** Props for the dialog element */
34
dialogProps: DOMAttributes<Element>;
35
/** Props for the dialog title element */
36
titleProps: DOMAttributes<Element>;
37
}
38
```
39
40
### Disclosure
41
42
Provides disclosure behavior for expandable/collapsible content sections.
43
44
```typescript { .api }
45
/**
46
* Provides disclosure behavior and accessibility
47
* @param props - Disclosure configuration
48
* @param ref - Ref to the disclosure element
49
* @returns Disclosure props and state
50
*/
51
function useDisclosure(props: AriaDisclosureProps, ref: RefObject<Element>): DisclosureAria;
52
53
interface AriaDisclosureProps {
54
/** Whether the disclosure is expanded */
55
isExpanded?: boolean;
56
/** Default expanded state (uncontrolled) */
57
defaultExpanded?: boolean;
58
/** Handler called when expansion changes */
59
onExpandedChange?: (isExpanded: boolean) => void;
60
/** Whether the disclosure is disabled */
61
isDisabled?: boolean;
62
}
63
64
interface DisclosureAria {
65
/** Props for the disclosure button */
66
buttonProps: ButtonHTMLAttributes<HTMLButtonElement>;
67
/** Props for the disclosure panel */
68
panelProps: DOMAttributes<Element>;
69
/** Whether the disclosure is expanded */
70
isExpanded: boolean;
71
}
72
```
73
74
### Modal
75
76
Provides modal overlay behavior with backdrop and focus management.
77
78
```typescript { .api }
79
/**
80
* Provides modal behavior and accessibility
81
* @param options - Modal configuration options
82
* @returns Modal props and state
83
*/
84
function useModal(options?: AriaModalOptions): ModalAria;
85
86
/**
87
* Provides modal overlay behavior
88
* @param props - Modal overlay configuration
89
* @param state - Modal state
90
* @param ref - Ref to the overlay element
91
* @returns Modal overlay props
92
*/
93
function useModalOverlay(props: AriaModalOverlayProps, state: OverlayTriggerState, ref: RefObject<Element>): ModalOverlayAria;
94
95
/**
96
* Provides modal provider context
97
* @param props - Modal provider configuration
98
* @returns Modal provider props and state
99
*/
100
function useModalProvider(props: ModalProviderProps): ModalProviderAria;
101
102
interface AriaModalOptions {
103
/** Whether the modal is open */
104
isOpen?: boolean;
105
/** Handler called when the modal should close */
106
onClose?: () => void;
107
/** Whether the modal is keyboard modal */
108
isKeyboardDismissDisabled?: boolean;
109
/** Whether to disable outside clicks */
110
isDismissable?: boolean;
111
}
112
113
interface ModalAria {
114
/** Props for the modal element */
115
modalProps: DOMAttributes<Element>;
116
/** Props for the underlay element */
117
underlayProps: DOMAttributes<Element>;
118
}
119
120
interface AriaModalOverlayProps {
121
/** Whether the overlay is open */
122
isOpen?: boolean;
123
/** Handler called when the overlay should close */
124
onClose?: () => void;
125
/** Whether the overlay is dismissable */
126
isDismissable?: boolean;
127
/** Whether keyboard dismissal is disabled */
128
isKeyboardDismissDisabled?: boolean;
129
/** Whether to prevent scrolling */
130
shouldCloseOnBlur?: boolean;
131
}
132
133
interface ModalOverlayAria {
134
/** Props for the modal overlay element */
135
modalProps: DOMAttributes<Element>;
136
/** Props for the underlay element */
137
underlayProps: DOMAttributes<Element>;
138
}
139
```
140
141
### Popover
142
143
Provides popover behavior with positioning and trigger management.
144
145
```typescript { .api }
146
/**
147
* Provides popover behavior and accessibility
148
* @param props - Popover configuration
149
* @param state - Popover state
150
* @param ref - Ref to the popover element
151
* @returns Popover props
152
*/
153
function usePopover(props: AriaPopoverProps, state: PopoverState, ref: RefObject<Element>): PopoverAria;
154
155
interface AriaPopoverProps {
156
/** Type of popover */
157
triggerRef: RefObject<Element>;
158
/** Whether the popover is non-modal */
159
isNonModal?: boolean;
160
/** Trigger source for the popover */
161
triggerSource?: string;
162
/** Whether keyboard dismissal is disabled */
163
isKeyboardDismissDisabled?: boolean;
164
/** Whether the popover should close on blur */
165
shouldCloseOnBlur?: boolean;
166
/** Whether to flip placement when space is limited */
167
shouldFlip?: boolean;
168
/** Boundary element for flipping */
169
boundaryElement?: Element;
170
/** Scroll ref for repositioning */
171
scrollRef?: RefObject<Element>;
172
/** Whether the popover should update position */
173
shouldUpdatePosition?: boolean;
174
/** Arrow boundary offset */
175
arrowBoundaryOffset?: number;
176
/** Placement of the popover */
177
placement?: Placement;
178
/** Container padding */
179
containerPadding?: number;
180
/** Offset from trigger */
181
offset?: number;
182
/** Cross offset */
183
crossOffset?: number;
184
/** Whether the popover should close on interact outside */
185
shouldCloseOnInteractOutside?: (element: Element) => boolean;
186
/** Max height of the popover */
187
maxHeight?: number;
188
/** Arrow size */
189
arrowSize?: number;
190
/** Arrow boundary offset */
191
arrowBoundaryOffset?: number;
192
}
193
194
interface PopoverAria {
195
/** Props for the popover element */
196
popoverProps: DOMAttributes<Element>;
197
/** Props for the arrow element */
198
arrowProps: DOMAttributes<Element>;
199
/** Placement that was actually used */
200
placement: PlacementAxis;
201
/** Arrow props */
202
arrowProps: DOMAttributes<Element>;
203
/** Props for the underlay element */
204
underlayProps: DOMAttributes<Element>;
205
}
206
```
207
208
### Overlay
209
210
Provides base overlay behavior and positioning.
211
212
```typescript { .api }
213
/**
214
* Provides overlay behavior and accessibility
215
* @param props - Overlay configuration
216
* @param ref - Ref to the overlay element
217
* @returns Overlay props
218
*/
219
function useOverlay(props: AriaOverlayProps, ref: RefObject<Element>): OverlayAria;
220
221
/**
222
* Provides overlay positioning behavior
223
* @param props - Position configuration
224
* @param ref - Ref to the overlay element
225
* @returns Position props and placement
226
*/
227
function useOverlayPosition(props: AriaPositionProps, ref: RefObject<Element>): PositionAria;
228
229
/**
230
* Provides overlay trigger behavior
231
* @param props - Overlay trigger configuration
232
* @param state - Overlay trigger state
233
* @param ref - Ref to the trigger element
234
* @returns Overlay trigger props
235
*/
236
function useOverlayTrigger(props: OverlayTriggerProps, state: OverlayTriggerState, ref?: RefObject<Element>): OverlayTriggerAria;
237
238
interface AriaOverlayProps {
239
/** Whether the overlay is open */
240
isOpen?: boolean;
241
/** Handler called when the overlay should close */
242
onClose?: () => void;
243
/** Whether the overlay should close on blur */
244
shouldCloseOnBlur?: boolean;
245
/** Whether the overlay is dismissable */
246
isDismissable?: boolean;
247
/** Whether keyboard dismissal is disabled */
248
isKeyboardDismissDisabled?: boolean;
249
/** Whether to prevent scrolling */
250
shouldCloseOnInteractOutside?: (element: Element) => boolean;
251
}
252
253
interface OverlayAria {
254
/** Props for the overlay element */
255
overlayProps: DOMAttributes<Element>;
256
/** Props for the underlay element */
257
underlayProps: DOMAttributes<Element>;
258
}
259
260
interface AriaPositionProps extends PositionProps {
261
/** Element to position relative to */
262
targetRef: RefObject<Element>;
263
/** Element to position */
264
overlayRef: RefObject<Element>;
265
/** Scroll refs for repositioning */
266
scrollRef?: RefObject<Element>;
267
/** Placement preference */
268
placement?: Placement;
269
/** Container padding */
270
containerPadding?: number;
271
/** Offset from target */
272
offset?: number;
273
/** Cross-axis offset */
274
crossOffset?: number;
275
/** Whether to flip when space is limited */
276
shouldFlip?: boolean;
277
/** Boundary element */
278
boundaryElement?: Element;
279
/** Whether position should update */
280
shouldUpdatePosition?: boolean;
281
/** Whether the overlay is open */
282
isOpen?: boolean;
283
/** Handler called when the overlay should close */
284
onClose?: () => void;
285
/** Max height */
286
maxHeight?: number;
287
/** Arrow size for popover arrows */
288
arrowSize?: number;
289
/** Arrow boundary offset */
290
arrowBoundaryOffset?: number;
291
}
292
293
interface PositionAria {
294
/** Props for the overlay element */
295
overlayProps: DOMAttributes<Element>;
296
/** Props for the arrow element */
297
arrowProps: DOMAttributes<Element>;
298
/** Placement that was used */
299
placement: PlacementAxis;
300
/** Update position manually */
301
updatePosition(): void;
302
}
303
304
interface OverlayTriggerProps {
305
/** Type of overlay */
306
type: 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid';
307
/** Whether the overlay is open */
308
isOpen?: boolean;
309
/** Default open state (uncontrolled) */
310
defaultOpen?: boolean;
311
/** Handler called when open state changes */
312
onOpenChange?: (isOpen: boolean) => void;
313
}
314
315
interface OverlayTriggerAria {
316
/** Props for the trigger element */
317
triggerProps: ButtonHTMLAttributes<HTMLButtonElement>;
318
/** Props for the overlay element */
319
overlayProps: DOMAttributes<Element>;
320
}
321
```
322
323
### Tooltip
324
325
Provides tooltip behavior with hover and focus triggers.
326
327
```typescript { .api }
328
/**
329
* Provides tooltip behavior and accessibility
330
* @param props - Tooltip configuration
331
* @param state - Tooltip trigger state
332
* @returns Tooltip props
333
*/
334
function useTooltip(props: AriaTooltipProps, state: TooltipTriggerState): TooltipAria;
335
336
/**
337
* Provides tooltip trigger behavior
338
* @param props - Tooltip trigger configuration
339
* @param state - Tooltip trigger state
340
* @param ref - Ref to the trigger element
341
* @returns Tooltip trigger props
342
*/
343
function useTooltipTrigger(props: TooltipTriggerProps, state: TooltipTriggerState, ref?: RefObject<Element>): TooltipTriggerAria;
344
345
interface AriaTooltipProps {
346
/** Whether the tooltip is open */
347
isOpen?: boolean;
348
/** ID for the tooltip */
349
id?: string;
350
}
351
352
interface TooltipAria {
353
/** Props for the tooltip element */
354
tooltipProps: DOMAttributes<Element>;
355
}
356
357
interface TooltipTriggerProps {
358
/** Whether the tooltip is disabled */
359
isDisabled?: boolean;
360
/** Delay before showing tooltip */
361
delay?: number;
362
/** Close delay */
363
closeDelay?: number;
364
/** Trigger events */
365
trigger?: 'focus' | 'focus+hover';
366
/** Whether the tooltip is open */
367
isOpen?: boolean;
368
/** Default open state (uncontrolled) */
369
defaultOpen?: boolean;
370
/** Handler called when open state changes */
371
onOpenChange?: (isOpen: boolean) => void;
372
}
373
374
interface TooltipTriggerAria {
375
/** Props for the trigger element */
376
triggerProps: DOMAttributes<Element>;
377
/** Props for the tooltip element */
378
tooltipProps: DOMAttributes<Element>;
379
}
380
```
381
382
### Toast
383
384
Provides toast notification behavior with timeout and positioning.
385
386
```typescript { .api }
387
/**
388
* Provides toast behavior and accessibility
389
* @param props - Toast configuration
390
* @param state - Toast state
391
* @param ref - Ref to the toast element
392
* @returns Toast props
393
*/
394
function useToast(props: AriaToastProps, state: ToastState, ref: RefObject<Element>): ToastAria;
395
396
/**
397
* Provides toast region behavior for managing multiple toasts
398
* @param props - Toast region configuration
399
* @param state - Toast region state
400
* @param ref - Ref to the toast region element
401
* @returns Toast region props
402
*/
403
function useToastRegion(props: AriaToastRegionProps, state: ToastRegionState, ref: RefObject<Element>): ToastRegionAria;
404
405
interface AriaToastProps {
406
/** Toast priority level */
407
priority?: 'low' | 'normal' | 'high';
408
/** Whether the toast should auto-dismiss */
409
shouldCloseOnAction?: boolean;
410
/** Handler called when the toast is closed */
411
onClose?: () => void;
412
}
413
414
interface ToastAria {
415
/** Props for the toast element */
416
toastProps: DOMAttributes<Element>;
417
/** Props for the title element */
418
titleProps: DOMAttributes<Element>;
419
/** Props for the description element */
420
descriptionProps: DOMAttributes<Element>;
421
/** Props for the close button */
422
closeButtonProps: ButtonHTMLAttributes<HTMLButtonElement>;
423
}
424
425
interface AriaToastRegionProps {
426
/** Accessible label for the toast region */
427
'aria-label'?: string;
428
/** ID of element that labels the toast region */
429
'aria-labelledby'?: string;
430
}
431
432
interface ToastRegionAria {
433
/** Props for the toast region element */
434
regionProps: DOMAttributes<Element>;
435
}
436
```
437
438
### Prevent Scroll
439
440
Provides scroll prevention behavior for modal overlays.
441
442
```typescript { .api }
443
/**
444
* Provides scroll prevention behavior
445
* @param options - Scroll prevention options
446
* @returns Scroll prevention state
447
*/
448
function usePreventScroll(options?: { isDisabled?: boolean }): void;
449
```
450
451
### Overlay Components
452
453
```typescript { .api }
454
/**
455
* Button for dismissing overlays
456
*/
457
function DismissButton(props: DismissButtonProps): JSX.Element;
458
459
/**
460
* Provider for modal context
461
*/
462
function ModalProvider(props: { children: ReactNode }): JSX.Element;
463
464
/**
465
* Base overlay component
466
*/
467
function Overlay(props: OverlayProps): JSX.Element;
468
469
/**
470
* Container for overlay portals
471
*/
472
function OverlayContainer(props: OverlayContainerProps): JSX.Element;
473
474
/**
475
* Provider for overlay context
476
*/
477
function OverlayProvider(props: { children: ReactNode }): JSX.Element;
478
479
/**
480
* Portal provider (marked as unsafe)
481
*/
482
function UNSAFE_PortalProvider(props: { children: ReactNode; getContainer?: () => Element }): JSX.Element;
483
484
interface DismissButtonProps {
485
/** Handler called when button is pressed */
486
onDismiss?: () => void;
487
}
488
489
interface OverlayProps {
490
/** Children to render in overlay */
491
children: ReactNode;
492
/** Whether the overlay is open */
493
isOpen?: boolean;
494
/** Container to render overlay in */
495
container?: Element;
496
/** Handler called when overlay should close */
497
onClose?: () => void;
498
}
499
500
interface OverlayContainerProps {
501
/** Container element to render portals in */
502
portalContainer?: Element;
503
/** Children to render */
504
children: ReactNode;
505
}
506
```
507
508
## Types
509
510
```typescript { .api }
511
type Placement =
512
| 'bottom'
513
| 'bottom left'
514
| 'bottom right'
515
| 'bottom start'
516
| 'bottom end'
517
| 'top'
518
| 'top left'
519
| 'top right'
520
| 'top start'
521
| 'top end'
522
| 'left'
523
| 'left top'
524
| 'left bottom'
525
| 'start'
526
| 'start top'
527
| 'start bottom'
528
| 'right'
529
| 'right top'
530
| 'right bottom'
531
| 'end'
532
| 'end top'
533
| 'end bottom';
534
535
type PlacementAxis = 'top' | 'bottom' | 'left' | 'right' | 'center';
536
537
interface OverlayTriggerState {
538
/** Whether the overlay is open */
539
isOpen: boolean;
540
/** Set the open state */
541
setOpen(isOpen: boolean): void;
542
/** Open the overlay */
543
open(): void;
544
/** Close the overlay */
545
close(): void;
546
/** Toggle the overlay */
547
toggle(): void;
548
}
549
550
interface PopoverState extends OverlayTriggerState {
551
/** Trigger source */
552
triggerSource?: string;
553
/** Set trigger source */
554
setTriggerSource(source: string): void;
555
}
556
557
interface TooltipTriggerState extends OverlayTriggerState {
558
/** Whether the tooltip is immediately open */
559
isWarmupFinished: boolean;
560
/** Warm up the tooltip globally */
561
warmUp(): void;
562
/** Cool down the tooltip globally */
563
coolDown(): void;
564
}
565
566
interface ToastState {
567
/** Whether the toast is visible */
568
isVisible: boolean;
569
/** Whether the toast is paused */
570
isPaused: boolean;
571
/** Remaining time before auto-dismiss */
572
remainingTime: number;
573
/** Pause the toast */
574
pause(): void;
575
/** Resume the toast */
576
resume(): void;
577
/** Close the toast */
578
close(): void;
579
}
580
581
interface ToastRegionState {
582
/** List of toasts in the region */
583
toasts: Toast[];
584
/** Add a toast */
585
add(toast: Toast): void;
586
/** Remove a toast */
587
remove(key: string): void;
588
/** Pause all toasts */
589
pauseAll(): void;
590
/** Resume all toasts */
591
resumeAll(): void;
592
}
593
594
interface Toast {
595
/** Unique key for the toast */
596
key: string;
597
/** Toast content */
598
content: ReactNode;
599
/** Toast priority */
600
priority?: 'low' | 'normal' | 'high';
601
/** Auto-dismiss timeout */
602
timeout?: number;
603
/** Handler called when closed */
604
onClose?: () => void;
605
}
606
607
interface PositionProps {
608
/** Container padding */
609
containerPadding?: number;
610
/** Offset from target */
611
offset?: number;
612
/** Cross-axis offset */
613
crossOffset?: number;
614
/** Whether to flip placement */
615
shouldFlip?: boolean;
616
/** Boundary element */
617
boundaryElement?: Element;
618
/** Max height */
619
maxHeight?: number;
620
}
621
```