Popover
A floating panel anchored to a trigger that holds richer content than a tooltip, such as a short explanation, a link, or a small set of controls. Use for supplementary content the user opens on purpose. Don't use for essential information, confirmations or menus.
Description
What it does
Popover opens a small floating panel next to the element that triggered it. The panel can hold text, links and simple controls, and closes when the user dismisses it.
Where it appears
Help content next to a form field, a filter panel opened from a table toolbar, a short preview of a record, and the panel behind pickers such as a calendar.
Why it exists
Some content is too long or too interactive for a tooltip but doesn't justify a modal or a new page. Popover keeps the user in context, and a single shared panel keeps every floating surface looking and behaving the same.
Anatomy
| Part | Required? | Notes |
|---|---|---|
| Trigger | Required | The button or link that opens the popover. Must be focusable, and carries aria-expanded and aria-controls. |
| Panel | Required | The floating surface that holds the content. Has a border, rounded corners and a soft shadow so it reads as sitting above the page. |
| Content | Required | Whatever the popover is for. Keep it short; if it needs scrolling or several steps, use a Modal or a page. |
| Arrow | Optional | A small pointer from the panel toward the trigger. Helps show which element opened the panel, especially when it moves to another side to stay on screen. |
Variants
Top
The panel opens above the trigger
Default when there is room above and the content below the trigger should stay visible.
Bottom
The panel opens below the trigger
Use near the top of the page or a scrolling area, where a panel above would be cut off.
Start (left)
The panel opens to the left of the trigger
Use for triggers at the right edge of a panel or column.
End (right)
The panel opens to the right of the trigger
Use for triggers at the left edge of a sidebar.
With arrow
Adds a pointer toward the trigger
Use when several triggers sit close together and it must be clear which one opened the panel. Leave it off for panels attached directly under a field.
States
| State | Behaviour |
|---|---|
| Closed | Default. The panel isn't in the page layout. The trigger shows aria-expanded="false". |
| Open | The panel appears next to the trigger. Focus moves into it when it holds interactive content, or stays on the trigger when it holds read-only text. |
| Dismissed | The user presses Escape, activates the trigger again, or moves focus or clicks outside. The panel closes and focus returns to the trigger. |
Usage guidelines
When to use
- Giving help that needs more than one line, such as an explanation with a link to guidance.
- Showing a small group of controls in context, such as a filter or a date picker panel.
- Previewing a record without leaving the current page.
When not to use
- For a short label on an icon button — use Tooltip.
- For anything the user must read or answer to continue — use Modal or inline text. Popover content is easy to miss.
- For a list of actions or options — use a menu or Select.
- For required information or error messages. These must always be visible without opening anything.
Do / Don't
Do
Open a popover from a button or link the user chooses to activate, and label that trigger clearly.
Don't
Don't open a popover on page load or without a user action. It interrupts people and screen readers may not announce it.
Do
Keep the content to a few lines or a handful of controls, with a clear way to dismiss it.
Don't
Don't stack popovers or open a Modal from inside one. If the flow needs that, redesign it.
Layout & Spacing
The panel is as wide as its content, up to the width the page sets. It repositions itself to stay inside the viewport.
| Element | Spec |
|---|---|
| Background | Raised surface colour. --orbit-popover-background |
| Border | 1px, subtle border colour. --orbit-popover-border-width, --orbit-popover-border-color |
| Corner radius | 8px. --orbit-popover-radius |
| Padding | 24px on all sides. --orbit-popover-padding |
| Gap to the trigger | 8px. --orbit-popover-offset |
| Arrow | 8px square, overlapping the panel edge by half its size (worked out in code, because tokens can't be negative). --orbit-popover-arrow-size |
| Shadow | Medium elevation, the same as Menu. --orbit-popover-shadow-* |
Component tokens
These are framework-neutral CSS variables. In both Light and Dark the colours follow the Semantic surface and border tokens.
| Token | What it sets |
|---|---|
--orbit-popover-background |
Panel fill (the raised surface). |
--orbit-popover-border-color, -border-width |
Panel outline. |
--orbit-popover-radius |
Corner radius of the panel. |
--orbit-popover-padding |
Space between the panel edge and its content. |
--orbit-popover-offset |
Gap between the trigger and the panel. |
--orbit-popover-arrow-size |
Width and height of the arrow square. |
| `--orbit-popover-shadow-{offset-x | offset-y |
| Element | Token |
|---|---|
| Panel fill | --orbit-popover-background |
| Panel outline | --orbit-popover-border-color |
| Shadow colour | --orbit-popover-shadow-color |
Engineering notes
- In the React Aria trial, the panel is
Popover, with the trigger as aDialogTriggerand the content in aDialog. Apply the--orbit-popover-*variables to thePopoverthrough a class name. Treat this mapping as a proposal until Engineering confirms it in the trial. - Use the
offsetsetting for the trigger gap rather than adding margin to the panel, so the positioning logic stays correct when the panel flips to another side. - The arrow is half outside the panel. Compute its overlap from
--orbit-popover-arrow-size(for examplecalc(var(--orbit-popover-arrow-size) / -2)) instead of hard-coding it. - Pickers whose panel is a list of options (Combobox, Select) use the menu surface tokens so they look identical to each other. Use the popover tokens for content panels.
Keyboard interaction
| Key | Action |
|---|---|
| Enter or Space | On the trigger, opens the popover. Activating the trigger again closes it. |
| Tab | Moves focus through the interactive content inside an open popover. Focus is not trapped; tabbing past the last item moves on to the next element in the page. |
| Escape | Closes the popover and returns focus to the trigger. |
Why it matters
People who use a keyboard or a screen reader can only reach a popover through its trigger, so the trigger must announce whether the panel is open and where it is. If focus doesn't move into interactive content, or doesn't return to the trigger on close, these users lose their place.
ARIA
| Role or attribute | When to use | Example |
|---|---|---|
aria-expanded | On the trigger. true while the popover is open, false when closed. | <button aria-expanded="true" aria-controls="filter-panel">Filters</button> |
aria-controls | On the trigger. Points to the id of the popover panel. | <button aria-controls="filter-panel">Filters</button> |
role="dialog" | On the panel when it holds interactive content, with aria-label or aria-labelledby naming it. Not modal: content behind it stays available. | <div id="filter-panel" role="dialog" aria-label="Filters"> |
Things to avoid
- Don't open a popover on hover alone. If it must open on hover, it also has to open on keyboard focus, stay open while the pointer moves onto it, and be dismissible without moving the pointer (WCAG 1.4.13).
- Don't trap focus in a popover. If it needs a focus trap, it should be a Modal.
- Don't rely on the arrow or the shadow to show where the panel ends. The border and background already do that, and must keep their contrast.