Popup
Popup is a utility that lets you declaratively anchor “popup” containers to another element.
This component’s name is inspired by <popup>
. It uses Floating UI under the hood to provide a well‐tested, lightweight and fully declarative positioning utility for tooltips, dropdowns and more.
Popup doesn’t provide any styles—just positioning! The popup’s preferred placement, distance and skidding (offset) can be configured using attributes. An arrow that points to the anchor can be shown and customised to your liking. Additional positioning options are available and described in more detail below.
Popup is a low‐level utility built specifically for positioning elements. Do not mistake it for a tooltip or similar because it does not facilitate an accessible experience ! Almost every correct usage of it will involve building other components. It should rarely, if ever, occur directly in your HTML.
A popup’s anchor should not be styled with display: contents
since the coordinates will not be eligible for calculation. However, if the anchor is a <slot>
element, the Popup component will use the first assigned element as the anchor. This behaviour allows other components to pass anchors through more easily via composition.
<div class="popup-overview">
<pc-popup placement="top" active>
<span slot="anchor"></span>
<div class="box"></div>
</pc-popup>
<div class="popup-overview-options">
<pc-select
class="popup-overview-select"
label="Placement"
name="placement"
value="top"
>
<pc-option value="top">top</pc-option>
<pc-option value="top-start">top-start</pc-option>
<pc-option value="top-end">top-end</pc-option>
<pc-option value="bottom">bottom</pc-option>
<pc-option value="bottom-start">bottom-start</pc-option>
<pc-option value="bottom-end">bottom-end</pc-option>
<pc-option value="right">right</pc-option>
<pc-option value="right-start">right-start</pc-option>
<pc-option value="right-end">right-end</pc-option>
<pc-option value="left">left</pc-option>
<pc-option value="left-start">left-start</pc-option>
<pc-option value="left-end">left-end</pc-option>
</pc-select>
<pc-input
label="Distance"
type="number"
name="distance"
value="0"
></pc-input>
<pc-input
label="Skidding"
type="number"
name="skidding"
value="0"
></pc-input>
</div>
<div class="popup-overview-options">
<pc-switch name="active" checked>Active</pc-switch>
<pc-switch name="arrow">Arrow</pc-switch>
</div>
</div>
<script>
const container = document.querySelector(".popup-overview");
const popup = container.querySelector("pc-popup");
const select = container.querySelector('pc-select[name="placement"]');
const distance = container.querySelector('pc-input[name="distance"]');
const skidding = container.querySelector('pc-input[name="skidding"]');
const active = container.querySelector('pc-switch[name="active"]');
const arrow = container.querySelector('pc-switch[name="arrow"]');
select.addEventListener("pc-change", () => (popup.placement = select.value));
distance.addEventListener("pc-input", () => (popup.distance = distance.value));
skidding.addEventListener("pc-input", () => (popup.skidding = skidding.value));
active.addEventListener("pc-change", () => (popup.active = active.checked));
arrow.addEventListener("pc-change", () => (popup.arrow = arrow.checked));
</script>
<style>
.popup-overview pc-popup {
--arrow-color: var(--pc-color-primary-400);
}
.popup-overview span[slot="anchor"] {
display: inline-block;
width: 150px;
height: 150px;
border: 2px dashed var(--pc-color-neutral-400);
border-radius: var(--pc-border-radius-l);
margin: var(--pc-spacing-xxxxl);
}
.popup-overview .box {
width: 100px;
height: 55px;
background-color: var(--pc-color-primary-400);
border-radius: var(--pc-border-radius-m);
}
.popup-overview-options {
display: flex;
align-items: center;
justify-content: flex-start;
flex-wrap: wrap;
gap: var(--pc-spacing-s);
}
.popup-overview-options pc-select {
width: 200px;
}
.popup-overview-options pc-input {
width: 100px;
}
.popup-overview-options + .popup-overview-options {
margin-top: var(--pc-spacing-s);
}
</style>
Demos
Activation
Popups are inactive and hidden until the active
attribute is applied. Removing the attribute will tear down all positioning logic and event listeners, meaning you can have many idle popups on the page without affecting performance.
External anchors
By default, anchors are slotted into the popup using the anchor
slot. If your anchor needs to live outside of the popup, you can pass the anchor’s id
to the anchor
attribute. Alternatively, you can pass an element reference to the anchor
property to achieve the same effect without using an id
.
Placement
Use the placement
attribute to tell the popup the preferred placement of the popup. The actual position will vary to ensure the panel remains in the viewport if you’re using positioning features like flip
or shift
.
Since placement is preferred when using flip
, you can observe the popup’s current placement when it’s active by looking at the data-current-placement
attribute. This attribute will update as the popup flips to find available space and will be removed when the popup is deactivated.
Distance
Use the distance
attribute to change the distance between the popup and its anchor. A positive value will move the popup further away and a negative value will move it closer to the anchor.
Skidding
The skidding
attribute is similar to distance
, but instead allows you to offset the popup along the anchor’s axis. Both positive and negative values are allowed.
Arrows
Add an arrow to your popup with the arrow
attribute. It’s usually a good idea to set a distance
to make room for the arrow. To adjust the arrow’s colour and size, use the --arrow-color
and --arrow-size
custom properties, respectively. You can also target the arrow
part to add additional styles such as shadows and borders.
By default, the arrow will be aligned as close to the centre of the anchor as possible, considering available space and arrow-padding
. You can the arrow-placement
attribute to force the arrow to align to the start, centre or end of the popup instead.
This page takes a long time to complete, please be patient!
Properties
Name | Description | Reflects | Default |
---|---|---|---|
popup | HTMLElement | - | |
active | boolean | false | |
anchor | Element | string | VirtualElement | undefined | - | |
arrow | boolean | false | |
arrowPadding arrow-padding | number | 10 | |
arrowPlacement arrow-placement | "start"
| "end"
| "center"
| "anchor" | "anchor" | |
autoSize auto-size | "horizontal"
| "vertical"
| "both" | undefined | - | |
autoSizeBoundary | Element | Element[] | undefined | - | |
autoSizePadding auto-size-padding | number | 0 | |
distance | number | 0 | |
flip | boolean | false | |
flipBoundary | Element | Element[] | undefined | - | |
flipFallbackPlacements flip-fallback-placements | string | "" | |
flipFallbackStrategy flip-fallback-strategy | "best-fit"
| "initial" | "best-fit" | |
flipPadding flip-padding | number | 0 | |
hoverBridge hover-bridge | boolean | false | |
placement | "top"
| "top-start"
| "top-end"
| "bottom"
| "bottom-start"
| "bottom-end"
| "right"
| "right-start"
| "right-end"
| "left"
| "left-start"
| "left-end" | "top" | |
shift | boolean | false | |
shiftBoundary | Element | Element[] | undefined | - | |
shiftPadding shift-padding | number | 0 | |
skidding | number | 0 | |
strategy | "absolute" | "fixed" | "absolute" | |
sync | "width" | "height" | "both" | undefined | - | |
updateComplete | A read‐only promise that resolves when the component has finished updating. | - |
Learn more about attributes and properties.
Methods
Name | Description | Arguments |
---|---|---|
reposition() | - |
Learn more about methods.
Importing
If you’re using the autoloader or the standard loader, you can ignore this section. If you’re cherry picking, you can use any of the following snippets to import this component.
To import this component from the CDN with a script tag, copy this snippet and paste it in your HTML.
<script type="module" src="https://cdn.jsdelivr.net/npm/placer-toolkit@0.5.1/dist/components/popup/popup.js"></script>
To import this component from the CDN using a JavaScript import, copy this snippet and paste it in your JavaScript:
import "https://cdn.jsdelivr.net/npm/placer-toolkit@0.5.1/dist/components/popup/popup.js";
To import this component with a bundler using a JavaScript import, copy this snippet and paste it in your JavaScript:
import "placer-toolkit/dist/components/popup/popup.js";