The page navigation is complete. You may now navigate the page content as you wish.
Skip to main content

Popovers

Content that floats above the page, anchored to the thing that opened it: the popover attribute, the top layer, and how our elements position it.

How a popover works

A popover is content that floats above the page, anchored to the thing that opened it: a menu, a hint, a small panel. Two separate problems have to be solved, and the browser only solves the first.

What the browser gives you: the popover attribute

Put popover on an element and popovertarget on a button, and you have a working popover with no script at all:

Every running service counts once, whichever region it is in. Stopped services do not count.
<button type="button" class="i80-button i80-button--color-secondary i80-button--size-medium" popovertarget="quota-note">
  <span class="i80-button__text">What counts towards the quota?</span>
</button>

<div id="quota-note" popover class="i80-typography-body-200 i80-foreground-primary"
     style="max-width: 20rem; padding: 16px; background: var(--i80-color-surface-primary); border: 1px solid var(--i80-color-border-primary); border-radius: 6px; box-shadow: var(--i80-elevation-high-box-shadow)">
  Every running service counts once, whichever region it is in. Stopped services do not count.
</div>

What you get for free:

  • The popover is promoted to the top layer, so it is never clipped by an overflow: hidden ancestor and never lands behind something with a higher z-index. Stacking contexts stop being your problem.
  • "Light dismiss": a click outside it or Esc closes it.
  • Opening one popover="auto" closes any other, so two menus cannot be open at once.
  • showPopover(), hidePopover() and togglePopover() from script, and :popover-open in CSS.

popover="manual" opts out of light dismiss and out of closing its siblings — that is what a tooltip wants, because a tooltip is not something you dismiss.

What the browser does not give you: position

The popover attribute says nothing about where the content appears. Placing it against its anchor, flipping it when there is no room below, and nudging it sideways at the edge of the window is the second problem, and we use Floating UI for it — the same library behind Dropdown and Tooltip.

So before you build anything: the two elements that already combine both are almost certainly what you want.

A menu, on click

Dropdown is a button and a list in a native popover. Esc, a click outside and the arrow keys all work, and the list flips and shifts to stay on screen:

Plan Edit Duplicate Delete
<i80-dropdown text="Actions" color="secondary">
  <i80-dropdown-title>Plan</i80-dropdown-title>
  <i80-dropdown-item href="#edit" icon="edit">Edit</i80-dropdown-item>
  <i80-dropdown-item icon="duplicate">Duplicate</i80-dropdown-item>
  <i80-dropdown-separator></i80-dropdown-separator>
  <i80-dropdown-item color="critical" icon="trash">Delete</i80-dropdown-item>
</i80-dropdown>

A hint, on hover and focus

Tooltip opens when the pointer enters the trigger and when the trigger takes focus, and closes when the pointer leaves, when focus leaves, and on Esc. Hover alone is not enough — a hint that only appears on hover is unreachable from the keyboard — so both are wired together:

Duplicate Visibility
<i80-tooltip text="Copies the plan with all its tasks">
  <i80-button icon="duplicate" icon-only color="secondary">Duplicate</i80-button>
</i80-tooltip>

<i80-tooltip text="Only people on the team can see it">Visibility</i80-tooltip>

Where it opens

Both elements take the preferred side, and both give it up when there is no room: the content flips to the opposite side, then shifts along the edge, rather than disappearing off the window.

Top Bottom Left Bottom start Newest first Oldest first Newest first Oldest first
<i80-tooltip text="Above, the default" placement="top">Top</i80-tooltip>
<i80-tooltip text="Below the trigger" placement="bottom">Bottom</i80-tooltip>
<i80-tooltip text="To the left" placement="left">Left</i80-tooltip>
<i80-tooltip text="Lined up with the start edge" placement="bottom-start">Bottom start</i80-tooltip>

<i80-dropdown text="Opens down and right" color="secondary" size="small" position="bottom-right">
  <i80-dropdown-item>Newest first</i80-dropdown-item>
  <i80-dropdown-item>Oldest first</i80-dropdown-item>
</i80-dropdown>

<i80-dropdown text="Opens up and left" color="secondary" size="small" position="top-left">
  <i80-dropdown-item>Newest first</i80-dropdown-item>
  <i80-dropdown-item>Oldest first</i80-dropdown-item>
</i80-dropdown>

<i80-tooltip> uses placement, with the twelve Floating UI values (top, top-start, top-end, and the same for bottom, left and right). <i80-dropdown> uses position, with the four corners. Both are a preference, not a promise — at the edge of the window, staying visible wins.

Building your own

If you need an anchored overlay that is neither a menu nor a hint:

  1. Put popover on the panel and popovertarget on the button, so the browser handles the layer, Esc and the click outside.
  2. Position it with Floating UI — flip to swap sides, shift to slide along the edge, offset for the gap, arrow for an arrow — or with CSS anchor positioning where the browsers you support have it.
  3. If the toggle shows and hides a panel rather than floating one, you want aria-expanded instead — see Show and hide.

Accessibility alert

The toggle must be a real <button>. popovertarget only works on a button, and anything else leaves the popover unreachable from the keyboard.

Code tip

  • The Popover API, in detail: MDN / Popover API
  • Positioning, in detail: Floating UI
  • How we combine the two: elements/src/components/dropdown.js and elements/src/components/tooltip.js

Attributes

The two elements that give you a positioned popover:

Element Attributes
<i80-dropdown> text, color, size, icon, position (bottom-right · bottom-left · top-right · top-left), width, height, match-toggle-width, inline, open. Methods show(), hide(). Events i80-open, i80-close.
<i80-tooltip> text, placement (top · bottom · left · right, each also -start and -end).

The popover attribute

This is the browser's own API, not ours — see MDN.

Member Notes
popover On the panel. auto (the default) closes on Esc and on a click outside, and closes other auto popovers when it opens. manual does neither.
popovertarget On a <button>: the id of the popover it toggles. Nothing else works here.
popovertargetaction toggle (the default), show or hide.
showPopover() / hidePopover() / togglePopover() From script.
:popover-open The CSS selector for an open popover.
toggle event Fired on the popover with oldState and newState.

The panel goes in the top layer, so it is not clipped by overflow: hidden and not affected by z-index anywhere on the page. Position it with Floating UI or CSS anchor positioning — the attribute itself places nothing.

See Web Components for loading i80.js.

0.1.0

Første i80-version (fork af upstream, se core/UPSTREAM.md).


Related