Skip to main content Design tokens Components Style utilities
Skip to table of contents

    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>
    Code

    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

    NameDescriptionReflectsDefault
    popup HTMLElement-
    active booleanfalse
    anchor Element | string | VirtualElement | undefined-
    arrow booleanfalse
    arrowPadding
    arrow-padding
    number10
    arrowPlacement
    arrow-placement
    "start" | "end" | "center" | "anchor""anchor"
    autoSize
    auto-size
    "horizontal" | "vertical" | "both" | undefined-
    autoSizeBoundary Element | Element[] | undefined-
    autoSizePadding
    auto-size-padding
    number0
    distance number0
    flip booleanfalse
    flipBoundary Element | Element[] | undefined-
    flipFallbackPlacements
    flip-fallback-placements
    string""
    flipFallbackStrategy
    flip-fallback-strategy
    "best-fit" | "initial""best-fit"
    flipPadding
    flip-padding
    number0
    hoverBridge
    hover-bridge
    booleanfalse
    placement "top" | "top-start" | "top-end" | "bottom" | "bottom-start" | "bottom-end" | "right" | "right-start" | "right-end" | "left" | "left-start" | "left-end""top"
    shift booleanfalse
    shiftBoundary Element | Element[] | undefined-
    shiftPadding
    shift-padding
    number0
    skidding number0
    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

    NameDescriptionArguments
    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.

    ScriptImportBundler

    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";