Component

Floating layers

@getdom/studio/headless/floating.js

The shared positioning engine behind DOM Studio popovers, dropdowns, comboboxes, autocomplete lists, tooltips, and menu surfaces.

Why

The hard parts are already handled

Floating UI is where ordinary JavaScript tends to get fragile: nested scroll containers, clipped parents, transformed ancestors, mobile visual viewports, tiny windows, long menus, arrow positioning, focus return, and light-dismiss all meet in one place. DOM Studio centralizes those mechanics in floating.js and layers the native Popover API on top through <dom-popover> and friends.

  • Escapes parent overflow, transforms, and z-index traps through the native Popover top layer.
  • Keeps panels inside the visual viewport with collision padding, side flipping, and coordinate shifting.
  • Tracks scroll parents, viewport resize, visual viewport movement, and element resize with one update per animation frame.
  • Publishes placement state through data attributes so CSS can adapt transitions, arrows, and origin.
  • Exposes available width and height CSS variables for long menus, command palettes, calendars, and suggestion lists.
  • Shares one positioning contract across popover, dropdown, autocomplete, combobox, tooltip, and custom menus.

Use first

Prefer the popover panel

Reach for <dom-popover>, <dom-dropdown>, <dom-combobox>, or <dom-autocomplete> when the UI is trigger-driven. Those controls combine floating.js positioning with native top-layer rendering, light-dismiss, keyboard behavior, ARIA wiring, scroll locking, and open/close events.

<dom-popover placement="bottom-end" offset="8" collision-padding="12" flip>
	<button slot="trigger" type="button">Filters</button>
	<form slot="panel" class="dom-popover-panel dom-floating-transition">
		<!-- any content: menu, form, picker, command list -->
	</form>
</dom-popover>

<script type="module">
	import '@getdom/studio/headless/popover.js';
</script>

Demo

Anchored top-layer panel

This is the recommended route for app UI: native popover top layer plus DOM Studio floating placement.

popover.htmlhtml
<div class="relative flex min-h-56 w-full items-center justify-center overflow-hidden rounded-xl border border-dashed border-border bg-secondary/30 p-8">
	<div class="absolute left-6 top-5 text-xs text-muted-fg">Overflow and transforms on parents do not clip this panel.</div>
	<dom-popover placement="bottom-end" offset="10" collision-padding="16" flip lock-scroll>
		<button
			slot="trigger"
			type="button"
			class="inline-flex h-10 items-center rounded-full bg-fg px-4 text-sm font-medium text-background shadow-sm hover:bg-fg/90"
		>
			Open anchored panel
		</button>
		<div slot="panel" class="dom-floating-transition w-72 rounded-xl border border-border skin-floating p-4 text-sm text-fg shadow-2xl">
			<p class="font-semibold">Top-layer popover</p>
			<p class="mt-2 text-muted-fg">
				The panel is measured, flipped, shifted, and kept aligned while scrolling or resizing.
			</p>
			<div class="mt-3 max-h-[min(9rem,var(--dom-floating-available-height))] overflow-auto rounded-lg bg-secondary/50 p-3 text-xs leading-5">
				<p>Available height comes from <code>--dom-floating-available-height</code>.</p>
				<p class="mt-2">Use it to constrain long menus without guessing viewport size.</p>
			</div>
		</div>
	</dom-popover>
</div>

<script type="module">
	import '@getdom/studio/headless/popover.js';
</script>

Low level

Use floating.js directly

Use the direct helper when you are building a custom surface that is not one of the headless elements yet, or when you need position metadata without the Popover API. computeFloatingPosition returns coordinates and collision data. applyFloatingPosition writes the standard DOM Studio inline styles, CSS variables, and data-* placement attributes. autoUpdateFloating keeps the result fresh while layout changes.

import {
	applyFloatingPosition,
	autoUpdateFloating,
} from '@getdom/studio/headless/floating.js';

const trigger = document.querySelector('#filter-button');
const panel = document.querySelector('#filter-panel');

function update() {
	applyFloatingPosition(trigger, panel, {
		placement: 'bottom-end',
		offset: 8,
		padding: 12,
		mode: 'viewport',
	});
}

const stop = autoUpdateFloating(trigger, panel, update);
update();

// Call stop() when the panel is removed or no longer needs to track layout.

Contract

CSS hooks

Attributes

data-placement, data-side, data-align, data-floating-mode, data-flipped, data-collision-*, and data-overflow-* let CSS react to the resolved placement.

Variables

--dom-floating-available-width, --dom-floating-available-height, --dom-popover-arrow-x, and --dom-popover-arrow-y power scroll limits and placement-aware arrows.