@data-slot/popover
v0.2.6
Published
Headless popover component for vanilla JavaScript. Accessible, unstyled, tiny.
Maintainers
Readme
@data-slot/popover
Headless popover component for vanilla JavaScript. Accessible, unstyled, tiny.
Installation
npm install @data-slot/popoverQuick Start
<div data-slot="popover">
<button data-slot="popover-trigger">Open Popover</button>
<div data-slot="popover-content" hidden>
<p>Popover content here</p>
<button data-slot="popover-close">Close</button>
</div>
</div>
<script type="module">
import { create } from "@data-slot/popover";
const controllers = create();
</script>API
create(scope?)
Auto-discover and bind all popover instances in a scope (defaults to document).
import { create } from "@data-slot/popover";
const controllers = create(); // Returns PopoverController[]createPopover(root, options?)
Create a controller for a specific element.
import { createPopover } from "@data-slot/popover";
const popover = createPopover(element, {
defaultOpen: false,
position: "bottom",
closeOnClickOutside: true,
closeOnEscape: true,
onOpenChange: (open) => console.log(open),
});Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| defaultOpen | boolean | false | Initial open state |
| position | "top" \| "bottom" \| "left" \| "right" | "bottom" | Position relative to trigger |
| closeOnClickOutside | boolean | true | Close when clicking outside |
| closeOnEscape | boolean | true | Close when pressing Escape |
| onOpenChange | (open: boolean) => void | undefined | Callback when open state changes |
Controller
| Method/Property | Description |
|-----------------|-------------|
| open() | Open the popover |
| close() | Close the popover |
| toggle() | Toggle the popover |
| isOpen | Current open state (readonly boolean) |
| destroy() | Cleanup all event listeners |
Markup Structure
<div data-slot="popover">
<button data-slot="popover-trigger">Trigger</button>
<div data-slot="popover-content">
Content
<button data-slot="popover-close">Close</button>
</div>
</div>Required Slots
popover-trigger- Button to toggle popoverpopover-content- The popover panel
Optional Slots
popover-close- Button to close the popover
Data Attributes
Options can also be set via data attributes on the root element. JS options take precedence over data attributes.
| Attribute | Type | Default | Description |
|-----------|------|---------|-------------|
| data-default-open | boolean | false | Initial open state |
| data-close-on-click-outside | boolean | true | Close when clicking outside |
| data-close-on-escape | boolean | true | Close when pressing Escape |
Boolean attributes: present or "true" = true, "false" = false, absent = default.
Position is set via data-position on the content element:
<div data-slot="popover-content" data-position="top"><!-- Popover that stays open when clicking outside -->
<div data-slot="popover" data-close-on-click-outside="false">
...
</div>Styling
Use data-state and data-position attributes:
/* Hidden state */
[data-slot="popover-content"][hidden] {
display: none;
}
/* Positioning */
[data-slot="popover"] {
position: relative;
}
[data-slot="popover-content"] {
position: absolute;
}
[data-slot="popover-content"][data-position="top"] {
bottom: 100%;
left: 50%;
transform: translateX(-50%);
}
[data-slot="popover-content"][data-position="bottom"] {
top: 100%;
left: 50%;
transform: translateX(-50%);
}
[data-slot="popover-content"][data-position="left"] {
right: 100%;
top: 50%;
transform: translateY(-50%);
}
[data-slot="popover-content"][data-position="right"] {
left: 100%;
top: 50%;
transform: translateY(-50%);
}With Tailwind:
<div data-slot="popover" class="relative">
<button data-slot="popover-trigger">Open</button>
<div
data-slot="popover-content"
class="absolute top-full left-1/2 -translate-x-1/2 mt-2 bg-white shadow-lg rounded-lg p-4 hidden data-[state=open]:block"
>
Content
</div>
</div>Accessibility
The component automatically handles:
aria-haspopup="dialog"on triggeraria-controlslinking trigger to contentaria-expandedstate on trigger- Unique ID generation for content
Keyboard Navigation
| Key | Action |
|-----|--------|
| Enter / Space | Toggle popover (on trigger) |
| Escape | Close popover and return focus to trigger |
Events
Listen for changes via custom events:
element.addEventListener("popover:change", (e) => {
console.log("Popover open:", e.detail.open);
});License
MIT
