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.
<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.
Centered horizontally.
Centered vertically.
<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.
The close button reuses the same data-tf-toggle="popover" trigger contract — clicking any trigger for an open popover closes it.
<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
| Requirement | TicFrame guidance |
|---|---|
| Relationship | TicFrame sets aria-haspopup="dialog" and tracks aria-expanded on the trigger automatically. |
| Role and name | Add role="dialog" and aria-labelledby on the popover element yourself — TicFrame doesn't assume the content structure. |
| Focus | Opening 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. |
| Keyboard | Escape closes it from anywhere on the page, not only while focus is inside. |
| Icon-only dismiss | Give an icon-only close button its own aria-label. |
API Reference
Classes
| Class | Purpose |
|---|---|
tf-popover | Required surface. |
tf-popover-header | Optional title styling. |
tf-popover-top, -bottom, -left, -right | Applied automatically based on data-tf-popover-placement — don't add these by hand. |
Attributes
| Attribute | Where | Description |
|---|---|---|
data-tf-toggle="popover" | Trigger | Marks 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-target | Trigger | Selector for the popover to toggle. |
data-tf-popover-placement | Trigger | "top" | "bottom" | "left" | "right" | "start" | "end". Omit for the original unopinionated default. |
Events
| Event | When it fires | Target |
|---|---|---|
tf:popover:open | After the popover opens. | Popover |
tf:popover:close | After the popover closes. | Popover |
Troubleshooting
| Symptom | Fix |
|---|---|
| Popover reappears immediately after closing it | A 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 apply | data-tf-popover-placement must be on the trigger, not the popover element. |
| Popover overlaps its own trigger | Give 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 page | Use Modal — Popover deliberately never adds a backdrop or scroll lock. |