Popover
A floating panel anchored to a trigger, with rich projected content rather than the plain text bkTooltip takes. There are 12 placements, four sides x three alignments, and the panel keeps itself on screen: it moves to the opposite side when the requested one has no room, and slides along the edge otherwise. The arrow always tracks the trigger, never the panel, so it keeps pointing at what opened it however far the panel had to move.
Preview
A working preview of Popover in the Brickclay style. Try it.
Interactive playground
Requested: bottom · Resolved: bottom
placement
trigger
A fixed CSS length, e.g. 320px or 50%, for when the content's natural width shouldn't drive layout. Leave empty for the default: sized to content, capped by maxWidth (280px).
Event log
Interact with the popover to see emitted events.
Examples
All 12 placements
A side (top) centres the panel on the trigger. A side plus an edge (top-left) lines that edge of the panel up with the same edge of the trigger instead. The edge names the axis the alignment runs along, so it is always concrete: above and below align horizontally (top-left, bottom-right), while left and right align vertically (left-top, right-bottom). There is no left-left.
Click
Stays open until you click outside or press Escape. Use it for anything the user has to interact with, a hover panel disappears the moment they reach for a button in it.
Hover
Opens on the way in, closes on the way out. The close is delayed and the panel cancels it, so the pointer can cross the gap and land on the content without it vanishing.
No arrow
Drop the arrow with [showArrow]="false" when the panel is wide enough that which element opened it is obvious anyway.
Disabled
[disabled]="true" stops the popover opening at all. Style the trigger itself, the popover doesn't reach into projected content.
Fixed width
[width]="'320px'" pins the panel to an exact width instead of letting the content (capped by maxWidth) decide, useful for a form or a fixed-column list that shouldn't reflow between opens as its content changes.
Driving it from outside
#ref="bkPopover" exposes open(), close(), toggle(), isOpen() and activePlacement(), so something other than the trigger can drive the panel.
API reference
Inputs <bk-popover>
| Name | Type | Default | Possible values | Description |
|---|---|---|---|---|
placement | PopoverPlacement | 'bottom' | the 12 listed below | Where to put the panel. A preference, not a guarantee, read activePlacement() for where it landed. |
trigger | string | 'click' | 'click', 'hover' | How the panel opens. Use click for anything interactive. |
title | string | '' | any text | Optional heading. Empty hides the header row entirely. |
closable | boolean | false | true, false | Show the × in the header. Does nothing without a title. |
disabled | boolean | false | true, false | Stops the popover opening. Style the trigger yourself. |
showArrow | boolean | true | true, false | Render the arrow. The gap stays either way. |
offset | number | 8 | px | Gap between trigger and panel. The arrow spans most of it, so going below ~8 leaves the tip overlapping the trigger. |
flip | boolean | true | true, false | Allow moving to the opposite side when the requested one has no room. Sliding along the edge is not covered by this and always happens. |
closeOnClickOutside | boolean | true | true, false | Click trigger only. Off means only Escape or code can close it. |
closeOnEscape | boolean | true | true, false | Close when Escape is pressed anywhere on the document. |
openDelay | number | 80 | ms | Hover only. Stops panels firing off as the pointer sweeps past. |
closeDelay | number | 150 | ms | Hover only. This is what makes the panel reachable, at 0 it closes while the pointer is still crossing the gap. |
maxWidth | string | '280px' | any CSS length | Caps the panel width. The content still decides the actual width up to this. |
width | string | null | null | any CSS length, or null | Pins the panel to an exact width instead of sizing to content. Unset by default, maxWidth alone still applies in that case. |
panelClass | string | '' | any class names | Extra classes on the panel. |
appendToBody | boolean | false | true, false | Deprecated, no-op. The panel positions via Angular CDK Overlay now, which always portals into the shared overlay container, the escape from a transformed ancestor or clipping parent this input used to opt into by hand is unconditional today. Safe to remove; kept only so existing bindings don't break. |
Outputs
| Name | Payload | Fires when |
|---|---|---|
opened | void | The panel opens, before it has been measured and positioned. |
closed | void | The panel closes, however it was closed. |
openChange | boolean | Either of the above. Convenient when you only track one flag. |
Public members #ref="bkPopover"
| Member | Returns | Description |
|---|---|---|
open() | void | Opens the panel. No-op while disabled. |
close() | void | Closes the panel. |
toggle() | void | Opens or closes, whichever applies. |
isOpen() | Signal<boolean> | Whether the panel is currently rendered. |
activePlacement() | Signal<PopoverPlacement> | Where the panel actually is once positioned, falls back to the requested placement while closed. |
Content slots
| Slot | Description |
|---|---|
[bkPopoverTrigger] | The element the panel anchors to. A bare attribute on whatever you want to be the trigger, a bk-button, an icon, a span. |
default | Everything else becomes the panel's body. |
PopoverPlacement
| Side | Centred | Aligned to first edge | Aligned to last edge | Alignment runs along |
|---|---|---|---|---|
Above | top | top-left | top-right | x, left and right edges |
Right | right | right-top | right-bottom | y, top and bottom edges |
Below | bottom | bottom-left | bottom-right | x, left and right edges |
Left | left | left-top | left-bottom | y, top and bottom edges |
Notes
- Flip vs. shift. Two different rules keep the panel on screen. Flip moves it to the opposite side, and only when it actually fits there, flipping into a second bad fit just trades one clipped edge for another. Shift slides it along the edge it's on, and is not optional. Only flip changes
activePlacement(). - The arrow follows the trigger, not the panel. Its offset is measured from the trigger's centre, so a panel that shifted 60px to stay on screen still has an arrow pointing at what opened it. It stops short of the rounded corners, where it would grow out of the curve rather than a flat edge.
- One at a time. Opening a popover closes any other one that's open. Two at once is nearly always a missed close rather than an intent.
- Choose the trigger by content, not by weight. Anything the user must click, type into, or read at their own pace needs trigger="click". Hover panels are for glanceable detail.