Popover
Display additional information in a popover on click or hover
<sa-button variant="ButtonVariant.Outline" popovertarget="--popover-intro">
<sa-icon name="sliders-horizontal" class="text-muted-foreground"/>
Configure View
</sa-button>
<sa-popover id="--popover-intro">
<div class="flex flex-col gap-y-3">
<sa-field-set>
<sa-field-legend variant="FieldLegendVariant.Label">Sort By</sa-field-legend>
<sa-field-group data-slot="radio-group">
<sa-field orientation="FieldOrientation.Horizontal">
<sa-input type="radio" name="sort-by" value="departure" id="sort-departure" checked/>
<sa-field-label for="sort-departure" class="font-normal">
Departure Date
</sa-field-label>
</sa-field>
<sa-field orientation="FieldOrientation.Horizontal">
<sa-input type="radio" name="sort-by" value="posted" id="sort-posted"/>
<sa-field-label for="sort-posted" class="font-normal">
Date Posted
</sa-field-label>
</sa-field>
</sa-field-group>
</sa-field-set>
<sa-separator orientation="SeparatorOrientation.Horizontal"/>
<sa-field-set>
<sa-field-legend variant="FieldLegendVariant.Label">View As</sa-field-legend>
<sa-field-group data-slot="radio-group">
<sa-field orientation="FieldOrientation.Horizontal">
<sa-input type="radio" name="view-as" value="departure" id="view-list" checked/>
<sa-field-label for="view-list" class="font-normal">
List
</sa-field-label>
</sa-field>
<sa-field orientation="FieldOrientation.Horizontal">
<sa-input type="radio" name="view-as" value="posted" id="view-gallery"/>
<sa-field-label for="view-gallery" class="font-normal">
Gallery
</sa-field-label>
</sa-field>
</sa-field-group>
</sa-field-set>
</div>
</sa-popover>Usage
<sa-popover> uses the HTML Popover API and renders a div element with a popover attribute. The popover can be opened by using the popovertarget attribute on a <sa-button> element.
<sa-button popovertarget="--my-popover">Element that triggers popover</sa-button>
<sa-popover id="--my-popover">
<!-- add the popover content here -->
</sa-popover>Examples
With button group
Create a split button that opens a popover using <sa-button-group>.
<sa-button-group>
<sa-button variant="ButtonVariant.Outline">
<sa-icon name="bot"/>
Trip Assistant
</sa-button>
<sa-button variant="ButtonVariant.Outline" size="ButtonSize.Icon" popovertarget="--popover-button-group" aria-label="More options">
<sa-icon name="chevron-down"/>
</sa-button>
</sa-button-group>
<sa-popover id="--popover-button-group" position="PositionArea.BottomSpanLeft">
<sa-stack gap="StackGap.Small">
<sa-popover-header>
<sa-popover-title>Ask the trip assistant</sa-popover-title>
<sa-popover-description>
Describe what you need in plain language.
</sa-popover-description>
</sa-popover-header>
<sa-field>
<sa-field-label for="--popopver-button-group-task" class="sr-only">
Request
</sa-field-label>
<sa-textarea
id="--popopver-button-group-task"
placeholder="I need to..."
class="resize-none"/>
<sa-field-description>
The assistant will draft an itinerary for you to review.
</sa-field-description>
</sa-field>
</sa-stack>
</sa-popover>Hover
You can trigger the popover on hover using an Interest Invokers.
For more information on interest invokers, please see our Tooltip documentation.
<p>While most tourists stick to the bustling markets of Marrakech, our resident history expert,
<a href="#" interestfor="--popover-hover" class="underline decoration-dotted decoration-2 underline-offset-2">Ibn Battuta</a>,
suggests heading north to the ancient streets of Tangier to truly capture the spirit of the Maghreb
</p>
<sa-popover id="--popover-hover">
<div class="flex flex-col gap-3">
<sa-avatar src="https://api.dicebear.com/9.x/adventurer-neutral/svg?seed=Brooklynn"/>
<div>
<p class="font-medium">Ibn Battuta</p>
<div class="flex items-center gap-2">
<div>@@ibnbattuta</div>
<sa-badge>Follows you</sa-badge>
</div>
</div>
<div class="flex items-center gap-4">
<p><span class="font-medium">655</span> following</p>
<p><span class="font-medium">100.3k</span> followers</p>
</div>
<div class="flex gap-2">
<sa-button variant="ButtonVariant.Secondary" class="flex-1">
<sa-icon name="check"/>
Following
</sa-button>
<sa-button class="flex-1">
<sa-icon name="mail"/>
Message
</sa-button>
</div>
</div>
</sa-popover>Position
Control the position of the popover relative to the trigger element using the position attribute. The default value is PositionArea.Bottom.
You can visualize the various values by using the Chrome team's Anchor Position Tool.
<div class="grid grid-cols-2 gap-4">
<sa-button variant="ButtonVariant.Outline" popovertarget="--popover-position-top-span-right" class="w-full">
Top Span Right
</sa-button>
<sa-button variant="ButtonVariant.Outline" popovertarget="--popover-position-right-center" class="w-full">
Right Center
</sa-button>
<sa-button variant="ButtonVariant.Outline" popovertarget="--popover-position-left-span-top" class="w-full">
Left Span Top
</sa-button>
<sa-button variant="ButtonVariant.Outline" popovertarget="--popover-position-bottom-span-left" class="w-full">
Bottom Span Left
</sa-button>
</div>
<sa-popover position="PositionArea.TopSpanRight" id="--popover-position-top-span-right" class="w-[230px]">
<sa-stack>
<sa-skeleton class="h-4 w-[200px]"/>
<sa-skeleton class="h-4 w-[200px]"/>
</sa-stack>
</sa-popover>
<sa-popover position="PositionArea.RightCenter" id="--popover-position-right-center" class="w-[230px]">
<sa-stack>
<sa-skeleton class="h-4 w-[200px]"/>
<sa-skeleton class="h-4 w-[200px]"/>
</sa-stack>
</sa-popover>
<sa-popover position="PositionArea.LeftSpanTop" id="--popover-position-left-span-top" class="w-[230px]">
<sa-stack>
<sa-skeleton class="h-4 w-[200px]"/>
<sa-skeleton class="h-4 w-[200px]"/>
</sa-stack>
</sa-popover>
<sa-popover position="PositionArea.BottomSpanLeft" id="--popover-position-bottom-span-left" class="w-[230px]">
<sa-stack>
<sa-skeleton class="h-4 w-[200px]"/>
<sa-skeleton class="h-4 w-[200px]"/>
</sa-stack>
</sa-popover>Offset
Control the offset of the popover from the trigger element using one of Tailwind's margin utilities.
<sa-button variant="ButtonVariant.Outline" popovertarget="--popover-offset">
Offset
</sa-button>
<sa-popover id="--popover-offset" class="mt-5">
<sa-stack>
<sa-skeleton class="h-4 w-[250px]"/>
<sa-skeleton class="h-4 w-[250px]"/>
</sa-stack>
</sa-popover>Manual dismiss
By default, <sa-popover> is light dismissable - meaning you can hide the popover by clicking outside of it or pressing the Esc key. You can change this behaviour by setting the popover attribute to manual which will require it to be explicitly closed using popovertargetaction="hide" or JavaScript.
<sa-button variant="ButtonVariant.Outline" popovertarget="--popover-manual-dismiss" popovertargetaction="show">
Open Popover
</sa-button>
<sa-button variant="ButtonVariant.Outline" popovertarget="--popover-manual-dismiss"
popovertargetaction="toggle">
Toggle Popover
</sa-button>
<sa-popover id="--popover-manual-dismiss" popover="manual">
<sa-button variant="ButtonVariant.Outline" popovertarget="--popover-manual-dismiss"
popovertargetaction="hide">
Close Popover
</sa-button>
</sa-popover>JavaScript API
Since <sa-popover> uses the Popover API, you can use the showPopover(), hidePopover() and togglePopover() methods to control visibility of the popover.
When opening a popover from JavaScript, pass the source element to showPopover() / togglePopover() so the popover is positioned relative to the element that opened it.
<sa-stack align="StackAlign.Center">
<sa-group>
<sa-button variant="ButtonVariant.Outline" id="--popover-js-api-button-open">
Open
</sa-button>
<sa-button variant="ButtonVariant.Outline" id="--popover-js-api-button-close">
Close
</sa-button>
<sa-button variant="ButtonVariant.Outline" id="--popover-js-api-button-toggle">
Toggle
</sa-button>
</sa-group>
<sa-avatar src="/avatars/avatar-1.jpg" id="--popover-js-api-avatar"/>
</sa-stack>
<sa-popover id="--popover-js-api" popover="manual">
<sa-stack>
<sa-skeleton class="h-4 w-[250px]"/>
<sa-skeleton class="h-4 w-[250px]"/>
</sa-stack>
</sa-popover>
<script>
const apiPopover = document.getElementById('--popover-js-api');
const apiPopoverAvatar = document.getElementById('--popover-js-api-avatar');
const apiPopoverButtonOpen = document.getElementById('--popover-js-api-button-open');
const apiPopoverButtonClose = document.getElementById('--popover-js-api-button-close');
const apiTooltipButtonToggle = document.getElementById('--popover-js-api-button-toggle');
apiPopoverButtonOpen.addEventListener('click', () => {
apiPopover.showPopover({
source: apiPopoverAvatar
});
});
apiPopoverButtonClose.addEventListener('click', () => {
apiPopover.hidePopover();
});
apiTooltipButtonToggle.addEventListener('click', () => {
apiPopover.togglePopover({
source: apiPopoverAvatar
});
});
</script>JavaScript events
The Popover API fires the beforetoggle and toggle events on <sa-popover> before it is shown/hidden and after it is show/hidden respectively. You can prevent opening the popover by calling event.preventDefault() in the beforetoggle event handler.
<sa-stack gap="StackGap.Small" class="min-w-md">
<sa-button popovertarget="--popover-js-events" variant="ButtonVariant.Outline">
Toggle Popover
</sa-button>
<sa-input type="checkbox" id="--popover-prevent-checkbox" label="Prevent popover toggle"/>
<label class="text-sm font-bold">Events:</label>
<div class="font-mono w-full h-40 overflow-y-auto rounded-md border border-border bg-muted p-3 text-foreground" id="--popover-js-events-output">
</div>
</sa-stack>
<sa-popover id="--popover-js-events" class="mt-5">
<sa-stack>
<sa-skeleton class="h-4 w-[250px]"/>
<sa-skeleton class="h-4 w-[250px]"/>
</sa-stack>
</sa-popover>
<script>
let counter = 0;
const eventsPopover = document.getElementById('--popover-js-events');
const eventsPopoverEventOutput = document.getElementById('--popover-js-events-output');
const eventsPopoverPreventCheckbox = document.getElementById('--popover-prevent-checkbox');
eventsPopover.addEventListener("beforetoggle", (e) => {
if (eventsPopoverPreventCheckbox.checked === true) {
eventsPopoverEventOutput.innerText = `${++counter}: Toggle prevented!\n` + eventsPopoverEventOutput.innerText;
e.preventDefault();
return;
}
eventsPopoverEventOutput.innerText = `${++counter}: Before toggle: ${e.oldState} -> ${e.newState}\n` + eventsPopoverEventOutput.innerText;
});
eventsPopover.addEventListener("toggle", (e) => {
eventsPopoverEventOutput.innerText = `${++counter}: Toggle: ${e.oldState} -> ${e.newState}\n` + eventsPopoverEventOutput.innerText;
});
</script>Accessibility
<sa-popover> uses the native Popover API. Opening it from a button with popovertarget gives you light dismiss, so clicking outside the popover or pressing Escape closes it, and the browser exposes the open state on the trigger button. Focus stays on the trigger when the popover opens unless an element inside it has autofocus.
Give the popover an accessible name when it contains more than a short message: put an id on <sa-popover-title> and reference it from aria-labelledby on <sa-popover>. Interactive content inside the popover is reached with Tab as usual.
API Reference
<sa-popover>
Renders a <div> element with a popover attribute (which you can override by supplying your own popover attribute, e.g. popover="manual"), opened by a button whose popovertarget attribute references the popover's id.
Prop
Type
<sa-popover-header>
Renders a <div> element that groups the popover's title and description.
<sa-popover-title>
Renders an <h2> element.
<sa-popover-description>
Renders a <p> element.