useGdAnchored
One answer for every panel that hangs off a trigger. The panel is promoted to the top layer, so no ancestor's overflow can clip it, and it flips to the other side when the preferred one does not fit.
Anatomy
Code
const trigger = ref<HTMLElement>()
const panel = ref<HTMLElement>()
const open = ref(false)
useGdAnchored({ trigger, panel, open, placement: () => 'bottom' })Why the top layer
The usual arrangement — position: absolute inside a position: relative wrapper — is what every design system starts with and every design system outgrows, for two reasons.
- It gets clipped. An absolutely positioned box is clipped by the nearest ancestor whose overflow is not
visible, and this package has eighteen of those. GdAccordion is literally one — it clips its own children's corners — so a menu in an accordion row was cut off at the row's edge. A z-index cannot save it: z-index does not cross a stacking context, and clipping does not obey it anyway. - It cannot flip.
placement="bottom"meant "below, always", so a menu near the bottom of the viewport opened into nothing — and reaching its items meant scrolling, which moved the trigger, which moved the menu. - The fix is the platform's.
popover="manual"plusshowPopover()paints above the entire document, outside every ancestor's overflow and every stacking context.
API
GdAnchorOptions
Props
triggerrequiredRef<HTMLElement | undefined>—The element the panel hangs off — usually the component's root wrapper.
panelrequiredRef<HTMLElement | undefined>—The floating panel itself.
openrequiredRef<boolean>—The open state to follow.
placementrequired() => GdPlacement—Preferred side. Flipped to its opposite when that side does not fit.
align() => "start" | "end" | "center"—Which edge the panel lines up with along the cross axis.
gapnumber8The distance between trigger and panel, in px — `--gd-space-4`.
matchWidth() => boolean—Size the panel to the trigger. A select menu wants this; a tooltip does not.
Returns
Props
place() => void—Re-measure and reposition now. Called for you on open, scroll and resize; you need it only after changing the panel's own size.