Components/Overlay

Popover

Popovers are small panels anchored to the control that opens them, holding content or controls the page has no room to keep on show. The panel floats over what is underneath without taking it, so the reader can dismiss it and carry straight on with whatever they were doing.

import { Popover } from '@growth-protocol-ai/alpha';

On this page

side
align
width
state
Popover.tsxtsx
1import { Popover } from '@growth-protocol-ai/alpha';
2
3<Popover
4 width="auto"
5 side="bottom"
6 align="center"
7 open={false}
8 onOpenChange={setOpen}
9 title="Filters"
10 trigger={<Button variant="outlined">Filters</Button>}
11>
12 <Checkbox label="Only mine" />
13 <Checkbox label="Archived" />
14</Popover>

When to use

Reach for a popover when something is worth a panel but not worth the page: a set of filters, a handful of controls, a summary of the row a reader is pointing at. It opens from the control that asks for it and closes the moment attention moves, so the page underneath keeps its place and there is nothing to navigate back from. What most often goes wrong is scope — a panel that grows a second step, or one holding a decision that must not be abandoned halfway, has outgrown the surface rather than found a use for it.

When not to use

A list of choices that sets a value is a Dropdown, which already owns the keyboard path through it and the way a selection reads back on the trigger. A single line clarifying a control, with nothing in it to click, is a Tooltip. And anything that has to be dealt with before the reader can get on with anything else is a Modal, because a popover deliberately leaves the page live and lets a stray click throw the panel away — which is exactly wrong for a question with consequences. The test for that last one is whether losing what is in the panel would matter.

side4 values

Side is where the panel sits, and it is a preference rather than a guarantee: a trigger with no room below flips to above, and the panel slides along its side rather than off the edge of the window. Narrow the window with one open and it will do exactly that. Every specimen here is closed until you press it, which is the component answering for itself how much of the page it is willing to cover.

top
right
bottom
left
sideWhen to use
topAbove the trigger. For a control near the foot of the page, where below is off the edge and the flip would happen anyway.
rightTo the right. For a trigger in a left rail or the first cell of a row, where the panel would otherwise cover the list it came from.
bottomBelow the trigger. The default, and the one to leave alone — a panel under the control that opened it is where a reader looks for it.
leftTo the left. For a control in a right rail, for the same reason.

align3 values

Alignment is where the panel sits along its side, and it carries more weight here than it does for a hint: a panel is usually wider than the control that opened it, so centring one on a narrow button hangs it over whatever is either side. Collision handling still has the last word — a panel with no room to align the way it was asked slides until it fits.

start
center
end
alignWhen to use
startFlush with the start of the trigger. The answer whenever the panel is wider than what opened it, and for anything in the first column of a row.
centerCentred on the trigger. The default, and right while the two are a similar width.
endFlush with the end of the trigger. The mirror of start, for a control at the right-hand end of its row.

width2 values

Width says whether the panel is a surface of its own or an extension of the control beneath it. It is the one axis here Alpha decides rather than the geometry, and the decision is about what the panel is for rather than how much is in it.

auto
trigger
widthWhen to use
autoSized to its content, between a floor and a cap, so a panel with two controls in it is not as wide as the button that opened it.
triggerExactly the trigger’s width, the way a dropdown panel is. For a panel that reads as part of the control — a filter set under a full-width button, or a picker under a field.