"use client";
import { Button, Card, Floating } from "@vortexlabs/vortex";
export const FloatingExample = () => (
<Floating>
<Floating.Trigger>
{(attributes) => <Button attributes={attributes}>Open panel</Button>}
</Floating.Trigger>
<Floating.Content attributes={{ "aria-label": "Example panel" }}>
<Card>Panel content</Card>
</Floating.Content>
</Floating>
);Features
- Positions content beside an element or viewport coordinates.
- Opens on click, hover, or focus.
- Adjusts placement when space is limited.
- Supports controlled visibility and custom surfaces.
Anatomy
<Floating>
<Floating.Trigger>
{(attributes) => <Button attributes={attributes}>Open panel</Button>}
</Floating.Trigger>
<Floating.Content />
</Floating>Position
Set position to choose a side and alignment; start and end follow text direction. Use fallbackPositions to choose alternatives when space is limited.
"use client";
import { Button, Card, Floating, Stack } from "@vortexlabs/vortex";
export const FloatingPositionExample = () => (
<Stack direction="row" gap={2} wrap>
{(["top-center", "bottom-center", "start-center", "end-center"] as const).map((position) => (
<Floating key={position} position={position}>
<Floating.Trigger>
{(attributes) => <Button attributes={attributes}>{position}</Button>}
</Floating.Trigger>
<Floating.Content>
<Card>{position}</Card>
</Floating.Content>
</Floating>
))}
</Stack>
);Triggers
Set triggerType to open on click, hover, or focus. Use trapFocusMode={false} to let focus leave.
"use client";
import { Button, Card, Floating, Stack } from "@vortexlabs/vortex";
export const FloatingTriggersExample = () => (
<Stack direction="row" gap={2} wrap>
{(["click", "hover", "focus"] as const).map((triggerType) => (
<Floating
key={triggerType}
triggerType={triggerType}
trapFocusMode={false}
>
<Floating.Trigger>
{(attributes) => <Button attributes={attributes}>{triggerType}</Button>}
</Floating.Trigger>
<Floating.Content>
<Card>18.4 °C · Updated a moment ago</Card>
</Floating.Content>
</Floating>
))}
</Stack>
);Controlled Visibility
Pass active and update it in onOpen and onClose. Use defaultActive instead for an uncontrolled panel that starts open.
"use client";
import { Button, Card, Floating, Stack, Text } from "@vortexlabs/vortex";
import React from "react";
export const FloatingControlledExample = () => {
const [active, setActive] = React.useState(false);
return (
<Floating
active={active}
onOpen={() => setActive(true)}
onClose={() => setActive(false)}
>
<Floating.Trigger>
{(attributes) => <Button attributes={attributes}>Inspect station</Button>}
</Floating.Trigger>
<Floating.Content attributes={{ "aria-label": "Station inspector" }}>
<Card>
<Stack gap={3}>
<Text>North pier · 18.4 °C</Text>
<Button size="sm" onClick={() => setActive(false)}>Close inspector</Button>
</Stack>
</Card>
</Floating.Content>
</Floating>
);
};Separate Anchor
Use positionRef to anchor content to another element. instanceRef exposes open(), close(), and updatePosition() for manual control.
Station A
"use client";
import { Button, Card, Floating, type FloatingInstance, Stack, Text } from "@vortexlabs/vortex";
import React from "react";
export const FloatingAnchorExample = () => {
const positionRef = React.useRef<HTMLDivElement>(null);
const instanceRef = React.useRef<FloatingInstance>(null);
return (
<Stack gap={4} align="start">
<Stack padding={4} borderColor="default" borderRadius="sm" attributes={{ ref: positionRef }}>
Station A
</Stack>
<Floating positionRef={positionRef} instanceRef={instanceRef} position="bottom-start">
<Floating.Trigger>
{(attributes) => <Button attributes={attributes}>Inspect Station A</Button>}
</Floating.Trigger>
<Floating.Content attributes={{ "aria-label": "Station A inspector" }}>
<Card>
<Stack gap={3}>
<Text>18.4 °C · Updated a moment ago</Text>
<Button size="sm" onClick={() => instanceRef.current?.close()}>Close</Button>
</Stack>
</Card>
</Floating.Content>
</Floating>
</Stack>
);
};Accessibility
Key
Description
TabShiftTab
Cycles through panel controls in the default dialog mode.
EnterSpace
Toggles a click-triggered panel from its button.
Escape
Requests dismissal; update
active in controlled mode.Set
aria-label in Floating.Content.attributes if the trigger does not name the dialog.