@rozie-ui/popover-lit
v0.2.3
Published
Idiomatic Lit popover/tooltip wrapping @floating-ui/dom — one Rozie source compiled to Lit.
Downloads
778
Maintainers
Readme
@rozie-ui/popover-lit
Idiomatic lit Popover — a headless floating primitive for tooltips and popovers, wrapping @floating-ui/dom for collision-aware positioning (offset / flip / shift / arrow) with live autoUpdate tracking. You bring the anchor (the anchor slot) and the floating content (the default slot); Popover owns placement, the open/close gesture (trigger: click / hover / focus), dismissal (Escape + click-outside), the WAI-ARIA wiring (tooltip vs dialog), and a two-way open model — compiled from one Rozie source. Every visual value is a CSS custom property, so it re-skins to any design system. This package is generated; do not edit src/ by hand.
Install
npm i @rozie-ui/popover-lit @floating-ui/domPeer dependencies: lit + @lit-labs/preact-signals + @preact/signals-core + @floating-ui/dom. Install them alongside this package.
Required peers — beyond the framework peer above, this package requires these non-optional peers to actually render:
@floating-ui/dom^1.7.2— required by@rozie-ui/popover-lit
Install the whole chain in one line:
npm i @rozie-ui/popover-lit @floating-ui/domAlso installed: @rozie/runtime-lit — Rozie's small, tree-shaken runtime helper package (controllable state, keyboard navigation, event modifiers, and safe interpolation). It arrives as a regular dependency, so npm pulls it for you. Your bundler keeps only the helpers this component actually uses — typically a few hundred bytes to a few KB, minified and gzipped. What's in it and what it costs.
Usage
import '@rozie-ui/popover-lit';
import '@floating-ui/dom'; // peer engine
// <rozie-popover> is a custom element. Bind `open`/`placement`/`trigger`/`offset`/
// `arrow` as properties; listen for `change` for the new open boolean, or
// `open-change` to drive the two-way model. Project the anchor into the `anchor`
// slot and the content into the default slot.
const el = document.querySelector('rozie-popover');
el.trigger = 'click';
el.placement = 'bottom';
el.offset = 8;
el.arrow = true;
el.addEventListener('open-change', (e) => {
el.open = e.detail;
});
el.addEventListener('change', (e) => {
console.log('open:', e.detail);
});Theming
Every visual value is a --rozie-popover-* CSS custom property (background, border, radius, shadow, padding, z-index, max-width, arrow size) — override any of them at any ancestor scope to match your design system.
Props
| Name | Type | Default | Two-way (model) | Required | Description |
| --- | --- | --- | :---: | :---: | --- |
| open | Boolean | false | ✓ | | Whether the floating content is open. The sole model: true prop — two-way bind it (r-model:open / v-model:open / bind:open / [(open)]) and Popover writes the new state back whenever the trigger or a dismissal toggles it. Left unbound it falls back to an uncontrolled default. |
| placement | String | "bottom" | | | Floating UI placement of the content relative to the anchor — one of top/right/bottom/left, each optionally suffixed -start/-end (e.g. bottom-start). With disableFlip off, the content may flip to the opposite side when it would overflow the viewport. Reconciled at runtime. |
| trigger | String | "click" | | | How the anchor opens the content: 'click' toggles on click, 'hover' opens on pointer-enter and closes on pointer-leave (tooltip-style), 'focus' opens on focus and closes on blur, or 'manual' for a composing component that drives open itself — every built-in gesture handler no-ops and the anchor omits aria-haspopup/aria-expanded (only a real gesture trigger claims the popup). Drives both the gesture handlers and the ARIA role ('hover'/'focus' → tooltip, 'click' → popover dialog, 'manual' → no anchor ARIA claim). |
| offset | Number | 8 | | | Distance in pixels between the anchor and the floating content (the Floating UI offset middleware). Reconciled at runtime. |
| disableFlip | Boolean | false | | | Disable the Floating UI flip middleware. By default the content flips to the opposite side of the anchor when it would overflow the viewport; set this to keep it pinned to placement regardless. |
| disableShift | Boolean | false | | | Disable the Floating UI shift middleware. By default the content shifts along its axis to stay within the viewport; set this to keep it strictly aligned to the anchor. |
| arrow | Boolean | false | | | Opt in to a positioned arrow element. When set, Popover renders an arrow <div> and runs the Floating UI arrow middleware against it so it points at the anchor. Style it via the --rozie-popover-* arrow CSS custom properties. |
| disabled | Boolean | false | | | Disable the control entirely: the trigger no longer opens the content and any open content is suppressed. |
| modal | Boolean | false | | | Opt in to modal dialog semantics for a click popover. Off by default: a click popover is a non-modal, click-outside-dismissable layer, so its panel is rendered role-neutral (the slot content owns its own ARIA role — e.g. a role="menu") and carries NO aria-modal. Set modal for a genuinely modal dialog popover: the panel then gets role="dialog" + aria-modal="true". Note: Popover ships no focus trap (it stays a minimal headless primitive); if you set modal, provide your own focus containment so the aria-modal claim holds. Ignored for hover/focus triggers (always tooltip-flavored). |
| strategy | String | "absolute" | | | Floating UI positioning strategy — 'absolute' (default) or 'fixed'. Use 'fixed' to escape a scrollable/overflow-clipping ancestor (e.g. a sticky table header). Reconciled at runtime. |
| bare | Boolean | false | | | Suppress the floating panel's own chrome (background, border, border-radius, box-shadow, padding) so a composing component can supply its own instead. Off by default — the panel keeps its standard --rozie-popover-* chrome tokens. |
| disablePositioning | Boolean | false | | | Render the floating panel in normal document flow instead of computing a floating position — no computePosition call and no autoUpdate tracking is ever started. For a composing component that already controls the panel's layout (e.g. an inline consumer) rather than a genuinely floating popover. |
| keepMounted | Boolean | false | | | Render the floating panel hidden instead of unmounting it while closed, so a composing component whose panel content owns scroll state (e.g. a virtualizer) keeps its DOM across a close/open cycle. A one-shot position computation runs once at mount so the hidden panel already carries correct coordinates before the first open. |
| matchWidth | Boolean | false | | | Match the floating panel's width exactly to the anchor's width, via the Floating UI size middleware. Writes the panel's width style only — never touches height. |
| disableDismiss | Boolean | false | | | Suppress Popover's own Escape-key and click-outside dismissal listeners while true. For a composing component that drives open itself and needs to temporarily veto Popover's independent dismissal — e.g. while a host sub-surface anchored to (but not nested inside) the composed control legitimately holds focus. Off by default; existing trigger="manual" consumers relying on real click-outside dismissal are unaffected unless they opt in. |
Events
| Event | Description |
| --- | --- |
| change | Fired whenever the open state changes — a click/hover/focus trigger gesture, an Escape or click-outside dismissal, or a programmatic show/hide/toggle. Payload is the new open boolean. The two-way open model is updated alongside it. |
Imperative handle
Beyond props, the component exposes imperative methods (declared once in the Rozie source via $expose). Grab a handle with the native ref mechanism and call them directly:
| Method | Description |
| --- | --- |
| show | Open the floating content (no-op when disabled). Emits change and updates the open model. |
| hide | Close the floating content. Emits change and updates the open model. |
| toggle | Flip the open state (no-op when disabled). Emits change and updates the open model. |
| reposition | Recompute the floating position immediately (the Floating UI computePosition pass). Useful after content size changes that autoUpdate does not observe. |
// The custom element IS the handle — exposed methods are public element methods.
const el = document.querySelector('rozie-popover');
el.show();
el.hide();
el.toggle();
el.reposition();Slots
| Slot | Params | | --- | --- | | anchor | open, toggle, show, hide | | (default) | |
