Popover

A non-modal dialog, that displays rich content in a portal, triggered by a clickable element.

# Features

  • Trigger and popover are automatically associated semantically using ARIA for improved accessibility.
  • Popover can be closed by interacting outside of it or pressing the Escape key.
  • Focus is moved and contained within the popover while it is open, and returned to the trigger element when it is closed.
  • Customization options for side, alignment, offsets, and collision handling.

# Dependencies

This module depends on focus-trap and @floating-ui/dom.

# Anatomy

Typically, a popover consists of:

  • Trigger: The element that triggers the popover.
  • Popover: The overlay, which is positioned relative to the trigger, and may contain
    • Close Button: An optional element to close the popover.
    • Arrow: An optional element that points to the trigger.
<script>
	import { createPopover } from '@grail-ui/svelte';

	const { usePopoverTrigger, triggerAttrs, usePopover, popoverAttrs, closeButtonAttrs, open } = createPopover();
</script>

<button use:usePopoverTrigger {...$triggerAttrs}>Click</button>

{#if $open}
	<section use:usePopover {...$popoverAttrs}>
		<div>
			Content
		</div>
		<button {...$closeButtonAttrs}>Close</button>
	</section>
{/if}

# Examples

# Changing placement

To change the placement of the popover relatively to the trigger, set the positioning property.

createPopover({
positioning: {
placement: "top-start",
gutter: 10
},
})

# Custom portal container

If you want to customize the element that your popover portals into, use the portal and pass the actual element or a CSS selector.

createPopover({
positioning: {
placement: "top-start",
},
portal: '#custom',
})

# Accessibility

# Keyboard Interactions

Key description
Esc
Closes the popover and moves focus to trigger element.
Tab
Moves focus to the next focusable element.
Shift + Tab
Moves focus to the previous focusable element.

# API

# PopoverConfig

Property Description Default
open?
boolean
The open state of the popover when it is initially rendered.
false
positioning?
Partial<PositioningOptions>
Where to place the floating element relative to its reference element.
portal?
string | HTMLElement | null
Where to "portal" the floating element outside it's initial DOM position. It can be a HTMLElement or a CSS selector that points to an already existing element.
"body"
onOpenChange?
(value?: boolean) => void | undefined
Event handler called when the open state of the tooltip changes.

# PopoverReturn

Property Description
usePopoverTrigger
Action<HTMLElement, void>
Action for the trigger element.
triggerAttrs
Readable<Record<string, string | undefined>>
HTML attributes for the trigger element.
usePopover
Action<HTMLElement, void>
Action for the popover element.
popoverAttrs
Readable<Record<string, string>>
HTML attributes for the popover element.
closeButtonAttrs
Readable<Record<string, string>>
HTML attributes for the popover element.
arrowAttrs
Readable<Record<string, string>>
HTML attributes for the popover arrow element.
open
Writable<boolean>
The controlled open state of the popover.
referenceEl
Writable<HTMLElement | undefined>
The element to which the popover should be attached. Defaults to the usePopoverTrigger.

# PositioningOptions

Property Description Default
strategy?
absolute | fixed
The strategy to use for positioning.
"absolute"
placement?
top | top-start | top-end | right | right-start | right-end | bottom | bottom-start | bottom-end | left | left-start | left-end
The initial placement of the floating element.
"top"
offset?
Object
The offset of the floating element.
gutter?
number
The main axis offset or gap between the reference and floating elements.
5
overflowPadding?
number
The virtual padding around the viewport edges to check for overflow.
8
flip?
boolean
Whether to flip the placement.
true
overlap?
boolean
Whether the floating element can overlap the reference element.
false
sameWidth?
boolean
Whether to make the floating element same width as the reference element.
false
fitViewport?
boolean
Whether the floating element should fit the viewport.
false
boundary?
@floating-ui/dom.Boundary
The overflow boundary of the reference element.