Skip to content

TicFrame v5.7.0-alpha.1 is available - read the changelog.

TicFrame

Components

Popover

A popover is a click-triggered overlay anchored to a trigger element, for rich content — headings, links, buttons, forms — that a plain-text Tooltip can't hold. Unlike Modal, it doesn't block the rest of the page.

How It Works

A trigger with data-tf-toggle="popover" and data-tf-target toggles any element open on click. TicFrame positions it, tracks aria-expanded on the trigger, and closes it on Escape or an outside click.

Placement

Without data-tf-popover-placement, a popover anchors by its own top-left corner just below the trigger's start edge (the original, unopinionated default). Set the attribute to top, bottom, left, right, start, or end for a centered, edge-anchored placement instead.

Not modal

No backdrop, no scroll lock, no focus trap. The rest of the page stays usable while a popover is open — use Modal when you need to block interaction.

Dismissal

Escape or a click anywhere outside both the popover and its trigger closes it.

RTL

start/end placements resolve to the correct physical side automatically — the same resolver Tooltip uses.

Live Examples

Default placement

No data-tf-popover-placement — anchored below the trigger's start edge.

HTML
<button type="button" class="tf-btn tf-btn-primary" data-tf-toggle="popover" data-tf-target="#popover-default">Open popover</button>
<div id="popover-default" class="tf-popover" role="dialog" aria-labelledby="popover-default-title">
  <div class="tf-popover-header" id="popover-default-title">Quick actions</div>
  <p>Rich content, real links and buttons -- not just text.</p>
  <a href="/components/tooltip">See Tooltip instead</a>
</div>

Explicit placements

Centered and edge-anchored, via data-tf-popover-placement.

HTML
<button type="button" class="tf-btn" data-tf-toggle="popover" data-tf-target="#popover-top" data-tf-popover-placement="top">Top</button>
<div id="popover-top" class="tf-popover tf-w-64" role="dialog" aria-labelledby="popover-top-title">...</div>

<button type="button" class="tf-btn" data-tf-toggle="popover" data-tf-target="#popover-right" data-tf-popover-placement="right">Right</button>
<div id="popover-right" class="tf-popover tf-w-64" role="dialog" aria-labelledby="popover-right-title">...</div>

With a dismiss button

A popover doesn't require one — Escape and outside click already work — but a visible close control helps touch users who have neither.

HTML
<div id="popover-dismiss" class="tf-popover tf-w-64" role="dialog" aria-labelledby="popover-dismiss-title">
  <div class="tf-flex tf-items-center tf-justify-between">
    <span id="popover-dismiss-title">Share this page</span>
    <button type="button" data-tf-toggle="popover" data-tf-target="#popover-dismiss" aria-label="Close">
      <i class="ti ti-028069" aria-hidden="true"></i>
    </button>
  </div>
  ...
</div>

Accessibility

RequirementTicFrame guidance
RelationshipTicFrame sets aria-haspopup="dialog" and tracks aria-expanded on the trigger automatically.
Role and nameAdd role="dialog" and aria-labelledby on the popover element yourself — TicFrame doesn't assume the content structure.
FocusOpening a popover does not move focus into it (unlike Modal/Offcanvas) — it's meant to be glanced at without losing your place. If it contains a form or a sequence the user must complete, use Modal instead.
KeyboardEscape closes it from anywhere on the page, not only while focus is inside.
Icon-only dismissGive an icon-only close button its own aria-label.

API Reference

Classes

ClassPurpose
tf-popoverRequired surface.
tf-popover-headerOptional title styling.
tf-popover-top, -bottom, -left, -rightApplied automatically based on data-tf-popover-placement — don't add these by hand.

Attributes

AttributeWhereDescription
data-tf-toggle="popover"TriggerMarks the element as a popover trigger. Multiple triggers (e.g. an open button and an in-panel close button) can target the same popover.
data-tf-targetTriggerSelector for the popover to toggle.
data-tf-popover-placementTrigger"top" | "bottom" | "left" | "right" | "start" | "end". Omit for the original unopinionated default.

Events

EventWhen it firesTarget
tf:popover:openAfter the popover opens.Popover
tf:popover:closeAfter the popover closes.Popover

Troubleshooting

SymptomFix
Popover reappears immediately after closing itA click on the trigger itself both fires the outside-click close handler and the trigger's own open toggle in the same event; TicFrame's own trigger already accounts for this, but a second, separate close control that isn't the registered trigger needs data-tf-toggle="popover" pointed at the same target to close it correctly.
Placement class doesn't applydata-tf-popover-placement must be on the trigger, not the popover element.
Popover overlaps its own triggerGive the popover a defined width (e.g. tf-w-64) — an unconstrained popover can grow wide enough to cover a nearby trigger at certain placements.
Need to block the rest of the pageUse Modal — Popover deliberately never adds a backdrop or scroll lock.