Skip to content
Sveltier
Mode
Primitives

Positioning

Place a popover, menu or tooltip next to its trigger, keep it there on scroll and resize, and flip it when there is no room.

Anchored elements

anchored() places the element on a side of its anchor, aligned along it, with a small gap. Start and end follow the writing direction, so the same code works in right-to-left layouts.

Side
Align

Open the popover, then change its side and alignment. Near an edge of the window it flips to the other side.

AccountMenu.svelte
<script>
  import { anchored, dismissable } from 'sveltier';
  let open = $state(false);
  let trigger = $state();
</script>

<button bind:this={trigger} aria-expanded={open} onclick={() => (open = !open)}>Account</button>

{#if open}
  <!-- A manual popover sits in the top layer; anchored() places it, dismissable() closes it. -->
  <div
    popover="manual"
    {@attach (element) => element.showPopover()}
    {@attach anchored({ anchor: trigger, side: 'bottom', align: 'end' })}
    {@attach dismissable({ onDismiss: () => (open = false), inside: [trigger] })}
  >
    …
  </div>
{/if}
NameTypeDefaultDescription
anchorElement | null | undefined–The element to place against. Nothing happens until it exists.
side'top' | 'bottom' | 'start' | 'end''bottom'Side of the anchor. Start and end follow the writing direction.
align'start' | 'center' | 'end''start'Alignment along that side, following the writing direction.
offsetnumber5Gap between the anchor and the element, in px.
flipbooleantrueMove to the opposite side when there is not enough room.

How it works

Where the browser has CSS anchor positioning, the browser does all the work: the element moves with its anchor and flips without a line of script running. Elsewhere a small script loads on demand and places the element the same way, so browsers with native support never download it.

Put the element in the top layer with popover, or position it with position: fixed: it is placed against the viewport, so no ancestor can clip it.