Popover
Enhance the native HTML Popover API with Floating UI positioning — the bare primitive Menu and every other floating panel in this library are built from.
PopoverDirective wraps the native HTML Popover API (the popover attribute) with intelligent
positioning from Floating UI. It responds to two selectors — [popover] and [dropdown] — which are the same
directive; [dropdown] only changes one default. Reach for it directly when the floating content is not a
list of actions; when it is, Menu is built on exactly this directive and adds the ARIA menu
semantics on top.
Concept — the native Popover API
An element with a popover attribute renders in the browser's top layer — above everything else, no
z-index required — and is shown/hidden by the browser itself: native focus handling, Escape-to-close and
click-outside dismissal all come from the platform. PopoverDirective adds what the platform does not:
computed placement relative to a trigger, collision-aware repositioning, and a couple of small ergonomics the
raw attribute leaves to you.
Minimal example
import { Component } from "@angular/core";
import { DropdownDirective } from "@sinequa/galactik";
@Component({
selector: "sample-component",
imports: [DropdownDirective],
template: `
<button popovertarget="my-popover">Open popover</button>
<div popover id="my-popover" class="w-64 p-3">
<p>This is popover content.</p>
</div>
`,
})
export class SampleComponent {}The trigger/target contract is entirely native HTML: any <button> with popovertarget="<id>" auto-invokes
the element carrying [popover]/[dropdown] with a matching, page-unique id — PopoverDirective only adds
the positioning once it opens.
How it works
The element starts visibility: hidden until the first position is computed, avoiding a flash at the wrong
spot. autoUpdate (repositioning on scroll, resize, or a DOM mutation near the trigger) only runs while the
popover is open — zero overhead while closed. [dropdown] is the exact same directive as [popover]; it only
changes what closeOnClick defaults to (see Options).
Recipes
Dropdown — closes on item click
Use [dropdown] for menus or lists where picking an item should always close the panel. PopoverDirective is
a bare positioning primitive with no "menu item" look of its own — factor the item classes into your own
reusable component, or reach for Menu when the content is an actions list with keyboard
navigation.
import { Component } from "@angular/core";
import { DropdownDirective } from "@sinequa/galactik";
@Component({
selector: "sample-component",
imports: [DropdownDirective],
template: `
<button popovertarget="my-dropdown">Actions</button>
<div dropdown id="my-dropdown" placement="bottom-start" class="min-w-48">
<button class="flex w-full items-center rounded-xs px-3 py-2 text-left text-sm hover:bg-(--bg-primary-lighter)">Edit</button>
<button class="flex w-full items-center rounded-xs px-3 py-2 text-left text-sm hover:bg-(--bg-primary-lighter)">Duplicate</button>
</div>
`,
})
export class SampleComponent {}Opt a [dropdown] out of its default click-to-close with an explicit [config]:
[config]="{ closeOnClick: false }".
Locking the position, for a trigger that moves
By default flip()/shift() reposition the popover if the viewport runs out of room. [lockPosition]="true"
disables both, forcing it to stay at the exact configured placement — useful when a consuming component
already accounts for available space itself and a repositioning popover would fight that logic.
import { Component } from "@angular/core";
import { DropdownDirective } from "@sinequa/galactik";
@Component({
selector: "sample-component",
imports: [DropdownDirective],
template: `
<button popovertarget="pinned">More filters</button>
<div popover id="pinned" placement="bottom-start" [lockPosition]="true" class="w-72 p-3">
<p>Always opens bottom-start, regardless of available viewport space.</p>
</div>
`,
})
export class SampleComponent {}Hiding the popover when its trigger scrolls away
A popover lives in the browser's top layer, so no ancestor clips it: when its trigger scrolls out of the list or
container it sits in, the popover keeps floating over nothing. [hideWhenTriggerHidden]="true" hides it
(visibility: hidden, it stays open) while the trigger is out of sight, and shows it again when the trigger comes back.
Programmatic control and reactive state
Grab the directive instance with exportAs="popover" (or "dropdown") to drive it imperatively, and read
state() to react to it.
import { Component } from "@angular/core";
import { DropdownDirective } from "@sinequa/galactik";
@Component({
selector: "sample-component",
imports: [DropdownDirective],
template: `
<button popovertarget="state-demo" #ref="popover">Toggle</button>
<div popover id="state-demo" #statePopover="popover">
<p>Popover content</p>
<button (click)="statePopover.close()">Close</button>
</div>
@if (statePopover.state() === 'open') {
<p>Popover is currently open!</p>
}
`,
})
export class SampleComponent {}Options
Prop
Type
Read through a template reference (#ref="popover" or "dropdown"): width (Signal<number>, the trigger's
offsetWidth, handy for width-matched panels) and state (Signal<"open" | "closed">). Methods: close(),
show(), toggle(). No outputs — react to state() instead.
Pitfalls
What's next
Menu
An accessible dropdown or contextual menu built on @angular/aria — roving focus, typeahead, unlimited nested submenus, and checkable items with no interactive control inside a menuitem.
Floating panel
injectFloatingPanel keeps a popup — a combobox's list, a submenu — under or beside an anchor with Floating UI, as wide as the anchor on request and in the top layer above a dialog.