CSS Anchor Positioning made simple for Svelte 5
🎯 Live Demo | 📦 npm | 🐙 GitHub
AnchorPop is a lightweight library that brings the power of native CSS Anchor Positioning to Svelte applications. Create popovers, tooltips, dropdowns, and context menus that intelligently position themselves and automatically adjust when space is constrained.
This library requires browser support for CSS Anchor Positioning. Currently supported in:
npm install anchorpop
<script>
import { AnchorPop, AnchorArrow, AnchorPopConfig } from 'anchorpop';
const config = new AnchorPopConfig({
id: 'my-popover',
placement: 'outside',
position_area: 'y-end',
align_self: 'start',
justify_self: 'anchor-center',
offset: 16
});
</script>
<button
popovertarget={config.id}
style="anchor-name: {config.from_anchor}"
>
Click me
</button>
<AnchorPop {...config}>
Your content here
</AnchorPop>
<AnchorArrow {...config} />
AnchorPop supports four semantic placement modes:
outside – Never overlaps the anchor element. Perfect for tooltips, dropdowns, and context menus.inside – Positioned within anchor bounds. Useful for overlay controls within containers.overlay – Covers the anchor and extends beyond. Perfect for select-style menus.border – Centered on anchor edges/corners. Great for badges and decorations.The AnchorPopConfig class encapsulates all popover state and configuration:
const config = new AnchorPopConfig({
id: 'my-popover', // Unique identifier
placement: 'outside', // Placement mode
position_area: 'y-end', // CSS position-area value
align_self: 'start', // Block axis alignment
justify_self: 'anchor-center', // Inline axis alignment
offset: 16, // Distance from anchor (px)
arrow_color: '#000000', // Arrow color
arrow_stroke_width: 2, // Arrow thickness (px)
flip_axis: 'all' // Which axes can flip ('x', 'y', 'all', 'none')
});
When the popover doesn't fit in its primary position, AnchorPop automatically tries fallback positions:
// Enable fallbacks on all axes
const config = new AnchorPopConfig({
flip_axis: 'all' // default
});
// Restrict to vertical flipping only
const config = new AnchorPopConfig({
flip_axis: 'y'
});
// Disable fallbacks
const config = new AnchorPopConfig({
flip_axis: 'none'
});
Visual arrows connect the popover to its anchor. You can pass any clip-path compatible vector to shape for the arrow heads:
const config = new AnchorPopConfig({
arrow_color: '#000000',
arrow_stroke_width: 2,
arrow_heads: {
start: { show: true, shape: 'polygon(0% 0%, 100% 50%, 0% 100%)' },
end: { show: true, shape: 'polygon(0% 0%, 100% 50%, 0% 100%)' }
}
});
Arrows are only shown for
outsideplacement mode.
const tooltip = AnchorPopConfig.tooltip_bottom({ offset: 8 });
const dropdown = AnchorPopConfig.dropdown({ offset: 4 });
const sidebar = AnchorPopConfig.sidebar({
side: 'right',
offset: 0
});
<AnchorPop>The main popover component. Renders content in the browser's top layer using the Popover API.
Props:
id – Unique identifier (required)from_anchor – Anchor name of the trigger elementto_anchor – Anchor name for this popoverplacement – Placement modeposition_area – CSS position-area valuealign_self – Block axis alignmentjustify_self – Inline axis alignmentoffset – Distance from anchor in pixelsflip_axis – Which axes can flipfallback_chain – Computed fallback positionsmode – Popover mode ('auto' or 'manual')backdrop – Show backdrop overlay<AnchorArrow>Visual arrow connecting the popover to the anchor. Uses pure CSS positioning with automatic updates.
Props:
id – Unique identifier (required)from_anchor – Anchor name of the sourceto_anchor – Anchor name of the targetplacement – Placement modeposition_area – CSS position-area valuealign_self – Block axis alignmentjustify_self – Inline axis alignmentfallback_chain – Computed fallback positionsarrow_color – Arrow colorarrow_stroke_width – Arrow thicknessarrow_heads – Arrow head configurationAnchorPopConfigConfiguration class with reactive state using Svelte 5 runes.
Methods:
new AnchorPopConfig(options) – Create a new configurationAnchorPopConfig.tooltip_top(options) – Tooltip preset (top)AnchorPopConfig.tooltip_bottom(options) – Tooltip preset (bottom)AnchorPopConfig.dropdown(options) – Dropdown menu presetAnchorPopConfig.sidebar(options) – Sidebar presetSafari has a limitation where CSS anchor positions don't update when the anchor element is moved using transform. To ensure compatibility:
❌ Don't use transforms for positioning:
<button style="transform: translate(100px, 50px)">
✅ Use layout positioning instead:
<button style="position: relative; left: 100px; top: 50px">
This affects draggable triggers or any dynamically positioned anchors.
Override CSS variables to customize appearance:
:global(.popover) {
--popover-background: oklch(100% 0 0 / 0.95);
--popover-border-color: oklch(90% 0 0);
--popover-border-width: 1px;
--popover-border-radius: 8px;
--popover-shadow: 0 4px 16px oklch(0% 0 0 / 0.1);
--popover-transition-duration: 80ms;
--popover-animation-distance: 8px;
}
Use mode="manual" for programmatic control:
<script>
let popover_ref;
function open_popover() {
popover_ref?.showPopover();
}
function close_popover() {
popover_ref?.hidePopover();
}
</script>
<AnchorPop bind:ref={popover_ref} mode="manual" {...config}>
Content
</AnchorPop>
AnchorPop uses several advanced CSS techniques:
For implementation details, see the component source code.
See CONTRIBUTING.md for development guidelines.
MIT © Johannes Mutter
Built with Svelte 5 and inspired by the CSS Anchor Positioning specification.