Floating Panel
Floating window with stage controls.
Basic
Panel
Congue molestie ipsum gravida a. Sed ac eros luctus, cursus turpis non, pellentesque elit. Pellentesque sagittis fermentum.
<.floating_panel class="floating-panel">
<:trigger class="button button--ghost button--sm">
<span data-closed>Open panel</span>
<span data-open>Close panel</span>
</:trigger>
<:title>Panel</:title>
<:minimize_trigger>
<.heroicon name="hero-arrow-down-left" class="icon" />
</:minimize_trigger>
<:maximize_trigger>
<.heroicon name="hero-arrows-pointing-out" class="icon" />
</:maximize_trigger>
<:default_trigger>
<.heroicon name="hero-rectangle-stack" class="icon" />
</:default_trigger>
<:close_trigger>
<.heroicon name="hero-x-mark" class="icon" />
</:close_trigger>
<:content>
<p>
Congue molestie ipsum gravida a. Sed ac eros luctus, cursus turpis
non, pellentesque elit. Pellentesque sagittis fermentum.
</p>
</:content>
</.floating_panel>
No visible trigger
Auxiliary panel
Opened from external buttons; the Zag trigger stays in the tab order but is visually hidden.
<div class="flex flex-col gap-space">
<div class="flex flex-wrap gap-2">
<button type="button" class="button button--sm">
Open
</button>
<button type="button" class="button button--sm">
Close
</button>
</div>
<script>
(function () {
const openBtn = document.getElementById("floating-panel-anatomy-no-trigger-open");
const closeBtn = document.getElementById("floating-panel-anatomy-no-trigger-close");
const dispatch = (open) => {
document.getElementById("floating-panel-anatomy-no-trigger")?.dispatchEvent(
new CustomEvent("corex:floating-panel:set-open", {
detail: { open },
bubbles: false,
})
);
};
openBtn?.addEventListener("click", () => dispatch(true));
closeBtn?.addEventListener("click", () => dispatch(false));
})();
</script>
<.floating_panel class="floating-panel">
<:trigger class="sr-only">
<span data-closed>Open auxiliary panel</span>
<span data-open>Close auxiliary panel</span>
</:trigger>
<:title>Auxiliary panel</:title>
<:close_trigger>
<.heroicon name="hero-x-mark" class="icon" />
</:close_trigger>
<:content>
<p>Opened from external buttons; the Zag trigger stays in the tab order but is visually hidden.</p>
</:content>
</.floating_panel>
</div>
Positioning
Placement
Uses positioning={%Corex.Positioning{}}
so the hook passes getAnchorPosition
with placement and gutter (flip keeps it in view).
<div class="inline-block rounded-md border border-border p-space">
<.floating_panel
class="floating-panel"
positioning={%Corex.Positioning{placement: "top-start", gutter: 20, flip: true}}
>
<:trigger class="button button--ghost button--sm">
<span data-closed>Open anchored panel</span>
<span data-open>Close anchored panel</span>
</:trigger>
<:title>Placement</:title>
<:close_trigger>
<.heroicon name="hero-x-mark" class="icon" />
</:close_trigger>
<:content>
<p>
Uses <code class="text-sm">positioning={%Corex.Positioning{}}</code> so the hook passes
<code class="text-sm">getAnchorPosition</code> with placement and gutter (flip keeps it in view).
</p>
</:content>
</.floating_panel>
</div>
Size
Default size
size={%{width: 380, height: 220}}
maps to Zag defaultSize; optional
min_size
constrains resize.
<.floating_panel
class="floating-panel"
size={%{width: 380, height: 220}}
min_size={%{width: 280, height: 160}}
>
<:trigger class="button button--ghost button--sm">
<span data-closed>Open sized panel</span>
<span data-open>Close sized panel</span>
</:trigger>
<:title>Default size</:title>
<:close_trigger>
<.heroicon name="hero-x-mark" class="icon" />
</:close_trigger>
<:content>
<p>
<code class="text-sm">size={%{width: 380, height: 220}}</code> maps to Zag
<code class="text-sm">defaultSize</code>; optional <code class="text-sm">min_size</code> constrains resize.
</p>
</:content>
</.floating_panel>