Popover
StableA small panel that opens beside a button, for menus, pickers and short explanations.
Also known as popup, flyout, dropdown panel
Install with npm install @tattva/ui (setup), or copy the code from the kit in Downloads.
Basic panel
<Popover label="About credits"
trigger={({ toggle, triggerProps }) => <Button variant="secondary" size="sm" onClick={toggle} {...triggerProps}>About credits</Button>}>
<p className="p-2 text-body leading-5">Cost depends on duration.</p>
</Popover>Give the button its screen reader details and open the panel on click.
When to use it
A small panel that opens beside a button, for menus, pickers and short explanations. Focus moves in when it opens and back when it closes, so keyboard users keep their place.
Made of: Button · Panel
Use it for
- A menu of actions behind a button.
- A list of options to pick from, such as a style or a size.
- A short explanation or a few settings that open from a button.
Not for
- Something people must answer before going on. Use a dialog.
- Actions on text the person has selected: use SelectionToolbar.
Do and avoid
Do
- Give the button its screen reader details, so it says whether the panel is open.
- Give the panel a name that says what it holds.
- Use a menu for a list of actions and a list box for a list of options, so arrow keys work.
- Keep panels narrower than the screen. A panel wider than the screen still runs off the right edge.
- Open upward near the bottom of a screen. Automatic flipping covers cases you did not plan for.
Avoid
- Do not use it for something people must answer. Use a dialog.
- Do not put one popover inside another.
- Do not use an icon-only button without a name.
- Do not rely on pointing to open it.
- Do not use a menu or list box for content that is not a list of items. Use the default panel.
On a phone
- Menus open as a bottom sheet with a backdrop on a narrow touch screen, kept within thumb reach and padded for the bottom safe area.
- Other roles, such as a dialog or listbox, stay anchored to the trigger unless you set phoneSheet; they are at least 224px wide and move to stay inside the screen edges.
- It closes when a finger touches outside it, and the sheet scrolls inside itself at up to 80% of the screen height.
- Give an anchored panel inside a scrolling or clipped parent the portal prop so it is not cut off.
Properties
| Prop | Type | Default | Description |
|---|---|---|---|
| triggerrequired | (api: PopoverTriggerApi) => ReactNode | none | Render function. Receives open, toggle and triggerProps with aria-expanded, aria-haspopup and aria-controls. |
| childrenrequired | ReactNode | ((api: { close: () => void }) => ReactNode) | none | Panel content. A function receives close. |
| labelrequired | string | none | Accessible name for the panel. |
| align | "start" | "end" | "start" | Which edge of the trigger the panel lines up with. |
| side | "bottom" | "top" | "bottom" | Whether the panel opens below or above the trigger. |
| role | "dialog" | "menu" | "listbox" | "dialog" | Role on the panel, also used for aria-haspopup on the trigger. |
| open | boolean | none | Controlled mode. Omit to let the popover manage itself. |
| onOpenChange | (open: boolean) => void | none | Called when the panel opens or closes. |
| autoFocus | boolean | true | Moves focus into the panel when it opens. Menu and listbox panels focus the checked radio or selected option, or else the first enabled item. Dialog panels focus the first focusable element, or the panel itself if there is none. Set false when the opener manages focus. |
| flip | boolean | true | After the panel mounts it is measured. If the preferred side overflows the viewport and the other side has more room, the panel opens there. It is also kept 8px inside the left and right viewport edges. |
| portal | boolean | false | Draws the panel on the page itself (fixed position) so a scrolling or clipped parent can never cut it off. Use it for menus inside lists and side bars. The panel keeps its place as the page scrolls or resizes. |
| phoneSheet | boolean | true for menus | On a phone (a narrow touch screen) the panel opens as a bottom sheet with a backdrop, within thumb reach. On by default when role is menu. Set false to keep it floating. |
| className | string | none | Classes for the wrapper. |
| panelClassName | string | none | Classes for the panel, for example to set a width. |
"Show code under the preview gives the code for the example on the stage."
In Figma
In Figma, one Popover switches all of this from its properties panel, across 4 variants. Each property sets the React prop beside it, so design and code use the same names.
| Figma property | React | Values |
|---|---|---|
| State | Hover, Pressed and Focus come from the browser. | Default, Hover, Pressed, Focus |
| Label | label | Any text |
States
States this part has beyond its default look. Build each one your screen can reach. States set by a property are in the Code tab.
- closing
- After open turns false the panel stays for --dur-fast while it fades out, and it cannot be pressed. Under reduced motion it goes at once.
Tokens
Change one of these and every part that uses it follows.
--surface-raised--border--shadow-lg--dur-fast--radius-overlay
Built in
- When the panel opens, focus moves into it: to the chosen or first item in a menu or list, or to the first control in other panels.
- Escape closes the panel and returns focus to the button.
- In menus and lists, the up and down arrows move between items and wrap around. Home and End jump to the ends, and unavailable items are skipped.
- In a menu or list, Tab closes it and moves on from the button.
- The panel has a name, and it flips to the other side when it would run off the screen.
- Closing is a quick plain fade in --dur-fast, faster than opening. The panel cannot be pressed while it fades. When people turn off motion in their system settings, it goes at once.
What you need to do
- Spread triggerProps onto your button, so screen readers hear whether the panel is open.
- Give the button a name, even when it shows only an icon.
- Set role to "menu" for actions or "listbox" for options, and give each item the matching item role, so the arrow keys work.
Built toward WCAG 2.2 AA and checked automatically; no independent audit yet. Read the accessibility guide