Popover

A small floating panel anchored to a trigger. Holds a title, a short body, and any interactive content the trigger surfaces.

Basic

Author the popover as real markup with an id. Point the trigger at it with data-stisla-popover-trigger="<id>". Click toggles, Escape closes, and a pointer down outside dismisses.

API key

Used to authenticate requests to the workspace from your scripts and CI.
<button type="button" class="btn btn--primary"
        data-stisla-popover-trigger="popover-basic"
        aria-haspopup="dialog" aria-expanded="false" aria-controls="popover-basic">
  Show popover
</button>

<div class="popover" id="popover-basic" data-stisla-popover
     aria-labelledby="popover-basic-title">
  <h3 class="popover__title" id="popover-basic-title">API key</h3>
  <div class="popover__body">Used to authenticate requests to the workspace from your scripts and CI.</div>
</div>

Anatomy

An optional .popover__close chip sits in the top-right corner. Wire it with data-stisla-popover-dismiss so the delegated handler closes the popover without per-instance JS.

Release notes

v3.0 ships the popover, dropdown, and tooltip components on the Floating UI foundation.
<button type="button" class="btn btn--outline btn--neutral"
        data-stisla-popover-trigger="popover-anatomy"
        aria-haspopup="dialog" aria-expanded="false" aria-controls="popover-anatomy">
  With explicit close
</button>

<div class="popover" id="popover-anatomy" data-stisla-popover
     aria-labelledby="popover-anatomy-title">
  <h3 class="popover__title" id="popover-anatomy-title">Release notes</h3>
  <div class="popover__body">v3.0 ships the popover, dropdown, and tooltip components on the Floating UI foundation.</div>
  <button type="button" class="popover__close" data-stisla-popover-dismiss aria-label="Dismiss">
    <i data-lucide="x"></i>
  </button>
</div>

Placements

data-stisla-popover-placement picks the resting side. Floating UI flips automatically when the chosen side would overflow the viewport.

Top

Anchors above the trigger.

Right

Anchors to the right of the trigger.

Bottom

Anchors below the trigger.

Left

Anchors to the left of the trigger.
<div class="d-flex flex-wrap gap-2">
  <button type="button" class="btn btn--outline btn--neutral"
          data-stisla-popover-trigger="popover-top"
          aria-haspopup="dialog" aria-expanded="false" aria-controls="popover-top">Top</button>
  <button type="button" class="btn btn--outline btn--neutral"
          data-stisla-popover-trigger="popover-right"
          aria-haspopup="dialog" aria-expanded="false" aria-controls="popover-right">Right</button>
  <button type="button" class="btn btn--outline btn--neutral"
          data-stisla-popover-trigger="popover-bottom"
          aria-haspopup="dialog" aria-expanded="false" aria-controls="popover-bottom">Bottom</button>
  <button type="button" class="btn btn--outline btn--neutral"
          data-stisla-popover-trigger="popover-left"
          aria-haspopup="dialog" aria-expanded="false" aria-controls="popover-left">Left</button>
</div>

<div class="popover" id="popover-top" data-stisla-popover data-stisla-popover-placement="top"
     aria-labelledby="popover-top-title">
  <h3 class="popover__title" id="popover-top-title">Top</h3>
  <div class="popover__body">Anchors above the trigger.</div>
</div>
<div class="popover" id="popover-right" data-stisla-popover data-stisla-popover-placement="right"
     aria-labelledby="popover-right-title">
  <h3 class="popover__title" id="popover-right-title">Right</h3>
  <div class="popover__body">Anchors to the right of the trigger.</div>
</div>
<div class="popover" id="popover-bottom" data-stisla-popover data-stisla-popover-placement="bottom"
     aria-labelledby="popover-bottom-title">
  <h3 class="popover__title" id="popover-bottom-title">Bottom</h3>
  <div class="popover__body">Anchors below the trigger.</div>
</div>
<div class="popover" id="popover-left" data-stisla-popover data-stisla-popover-placement="left"
     aria-labelledby="popover-left-title">
  <h3 class="popover__title" id="popover-left-title">Left</h3>
  <div class="popover__body">Anchors to the left of the trigger.</div>
</div>

Hover trigger

Add data-stisla-popover-trigger-mode="hover focus" to open on hover and keyboard focus. The 100ms close delay bridges the cursor from the trigger into the popover so it stays open while you read it.

Read-only

Members with the viewer role can browse but not edit shared boards.
<button type="button" class="btn btn--outline btn--neutral"
        data-stisla-popover-trigger="popover-hover"
        aria-haspopup="dialog" aria-expanded="false" aria-controls="popover-hover">
  Hover or focus me
</button>

<div class="popover" id="popover-hover" data-stisla-popover
     data-stisla-popover-trigger-mode="hover focus"
     aria-labelledby="popover-hover-title">
  <h3 class="popover__title" id="popover-hover-title">Read-only</h3>
  <div class="popover__body">Members with the viewer role can browse but not edit shared boards.</div>
</div>

Rich content

Author the body with whatever markup the popover needs. Paragraphs, links, buttons, and form bits all work. There's no string-content opt-in.

Invite by email

<button type="button" class="btn btn--primary"
        data-stisla-popover-trigger="popover-rich"
        aria-haspopup="dialog" aria-expanded="false" aria-controls="popover-rich">
  Invite teammate
</button>

<div class="popover" id="popover-rich" data-stisla-popover data-stisla-popover-placement="bottom-start"
     aria-labelledby="popover-rich-title" style="min-width: 17rem;">
  <h3 class="popover__title" id="popover-rich-title">Invite by email</h3>
  <div class="popover__body text-foreground">
    <div class="d-flex flex-column gap-2">
      <input type="email" class="input" placeholder="name@example.com" aria-label="Email address">
      <div class="d-flex gap-2 justify-content-end">
        <button type="button" class="btn btn--sm btn--ghost btn--neutral" data-stisla-popover-dismiss>Cancel</button>
        <button type="button" class="btn btn--sm btn--primary">Send invite</button>
      </div>
    </div>
  </div>
</div>

Imperative

Mark a popover with an id and reach it via Stisla.get(document.getElementById('<id>')). Call Stisla.Popover.getOrCreate(el).open() when you need to drive it from script without a click trigger.

Programmatic

Opened by a script, anchored to the marked trigger.
<div class="d-flex flex-wrap gap-2 align-items-center">
  <button type="button" class="btn btn--neutral" data-demo-popover-open="popover-imperative">
    Open via JS
  </button>
  <button type="button" class="btn btn--outline btn--neutral"
          data-stisla-popover-trigger="popover-imperative"
          aria-haspopup="dialog" aria-expanded="false" aria-controls="popover-imperative">
    Anchor
  </button>
</div>

<div class="popover" id="popover-imperative" data-stisla-popover
     aria-labelledby="popover-imperative-title">
  <h3 class="popover__title" id="popover-imperative-title">Programmatic</h3>
  <div class="popover__body">Opened by a script, anchored to the marked trigger.</div>
</div>

Customization

Every variable that retunes .popover is below. Override on a single popover, on a parent scope, or on :root. The surface uses the --st-surface tier so the popover sits in the same elevation family as dialog and drawer.

Geometry

VariableDefaultUse
--popover-max-width17.25remWrap point for body text. Long content wraps; rich content can set its own min-width.
--popover-min-width13remFloor so single-line action labels don't collapse the panel.
--popover-padding1remSingle frame inset. Title, body, and close chip share it.
--popover-radiusvar(--st-radius-lg)Frame radius. Matches dialog and card large-frame tier.
--popover-z-index1070Stack level. Sits above an open dropdown, below a toast.

Surface

VariableDefaultUse
--popover-bgvar(--st-surface)Panel background.
--popover-colorvar(--st-foreground)Default text color inside the panel.
--popover-border-colorvar(--st-border)Frame outline. Solid fill, no transparency.
--popover-border-width1pxFrame thickness. Arrow border thickness tracks this.
--popover-shadowvar(--st-shadow)Elevation off the page.

Title

VariableDefaultUse
--popover-title-colorvar(--st-foreground)Heading color.
--popover-title-font-weight600Heading weight.
--popover-title-font-size0.9375remSlightly larger than body for hierarchy.
--popover-title-margin-bottom0.5remGap between title and body. Sized so a bare-text body and an input body both read cleanly under the title.

Body

VariableDefaultUse
--popover-body-colorvar(--st-muted-foreground)Default body color. Override it to --st-foreground inside rich-content popovers.
--popover-body-font-size0.875remBody text size.
--popover-body-line-height1.5Wrapping rhythm.

Close chip

VariableDefaultUse
--popover-close-size1.5remSquare footprint of the optional dismiss chip.
--popover-close-colorvar(--st-muted-foreground)Resting icon color.
--popover-close-color-hovervar(--st-foreground)Hover icon color.
--popover-close-bg-hovervar(--st-accent)Hover background fill.

Arrow and motion

VariableDefaultUse
--popover-arrow-size12pxSquare that's rotated 45° for the diamond caret.
--popover-transition-duration0.18sFade and translate duration. Zeroed under prefers-reduced-motion.