Popovers
Popovers are small content containers that provide a contextual overlay. They can be used as in-context feature explanations, dropdowns, or tooltips.
Classes
| Class | Description | Parent | Modifies |
|---|---|---|---|
.s-popover | Base parent container for popovers. | N/A | N/A |
.s-popover--close | Used to dismiss a popover. | .s-popover | N/A |
.s-popover--content | Wrapper around the popover content to apply appropriate overflow styles. | .s-popover | N/A |
.s-popover__tooltip | Removes minimum size constraints to support shorter tooltip text. | N/A | .s-popover |
.is-visible | This class toggles the popover visibility. | N/A | .s-popover |
Interactive popovers
Stacks provides a Stimulus controller that allows you to interactively display a popover from a source element. Positioning direction are managed for you by Popper.js, a powerful popover positioning library we’ve added as a dependency. These popovers are automatically hidden when user click outside the popover or tap the Esc key.
Interactive Attributes
| Attribute | Description | Applied to |
|---|---|---|
id="{POPOVER_ID}" | A unique id that the popover's toggling element can target. Matches the value of `[aria-controls]` on the toggling element. | .s-popover |
data-controller="s-popover" | Wires up the element to the popover controller. This may be a toggling element or a wrapper element. | Controller element |
data-s-popover-reference-selector="[selector]" | (optional) Designates the element to use as the popover reference. If left unset, defaults to the controller element. | Controller element |
aria-controls="{POPOVER_ID}" | Associates the element to the desired popover element. | Reference element |
data-action="s-popover#toggle" | Wires up the element to toggle the visibility of a generic popover. | Toggling element |
data-s-popover-toggle-class="[class list]" | Adds an optional space-delineated list of classes to be toggled on the originating element when the popover is shown or hidden. | Controller element |
data-s-popover-placement="[placement]" | Dictates where to place the popover in relation to the reference element. Defaults to `bottom`. Accepted placements are `auto`, `top`, `right`, `bottom`, `left`, each with optional `-start` and `-end` variations. | Controller element |
data-s-popover-auto-show="[true|false]" | (optional) If `true`, the popover will appear immediately when the Stacks controller is first connected. Use this instead of `.is-visible` to prevent the popover from appearing before it has been correctly positioned. | Controller element |
data-s-popover-hide-on-outside-click="[value]" | (optional) Default: `always`. Values: `always` — hides on outside clicks; `if-in-viewport` — only hides if the popover is in the viewport; `never` — does not hide on outside clicks; `after-dismissal` — does not hide on outside clicks unless it has been dismissed at least once. | Controller element |
Interactive Events
| Event | Description |
|---|---|
s-popover:show | Fires immediately before showing and positioning the popover. Can be used to create or initialize the popover element. Calling `.preventDefault()` cancels the display. |
s-popover:shown | Fires immediately after showing the popover. |
s-popover:hide | Fires immediately before hiding the popover. Calling `.preventDefault()` prevents the removal of the popover. |
s-popover:hidden | Fires immediately after hiding the popover. |
Dispatched Events
| Event | Description | Element |
|---|---|---|
dispatcher | Contains the `Element` that initiated the event. For instance, the button clicked to show, the element clicked outside the popover that caused it to hide, etc. | s-popover:* |
Examples
There's no data associated with your account yet. Please check back tomorrow.
We know you hate spam, and we do too. That's why we make it easy for you to update your email preferences or unsubscribe at anytime.
We never share your email address with third parties for marketing purposes.
Default interactivity
To enable interactive popovers, you will need to add the above attributes to the popover’s originating button. Custom positioning can be specified using the data-s-popover-placement. In the following example, we’ve chosen bottom-start. No positioning classes need to be added to your markup, only the data attributes.
To promote being able to tab to an open popover, it’s best to place the popover immediately after the toggling button in the markup as siblings.
<button class="s-btn s-btn__dropdown" role="button"
aria-controls="popover-example"
aria-expanded="false"
data-controller="s-popover"
data-action="s-popover#toggle"
data-s-popover-placement="bottom-start"
data-s-popover-toggle-class="is-selected">
…
</button>
<div class="s-popover"
id="popover-example"
role="menu">
<div class="s-popover--content">
…
</div>
</div> Dismissible
In the case of new feature callouts, it may be appropriate to include an explicit dismiss button. You can add one using the styling provided by .s-popover--close.
In order for to close the popover with an explicit close button, you’ll need to add the controller to a parent as illustrated in the following example code:
<div class="…"
data-controller="s-popover"
data-s-popover-reference-selector="#reference-element">
<button id="reference-element" class="s-btn s-btn__dropdown"
aria-controls="popover-example"
aria-expanded="true"
data-action="s-popover#toggle">
…
</button>
<div id="popover-example" class="s-popover is-visible" role="menu">
<button class="s-popover--close s-btn s-btn__tonal" aria-label="Close"
data-action="s-popover#toggle">
<svg aria-hidden="true">…</svg>
</button>
<div class="s-popover--content">
…
</div>
</div>
</div> Dismissible persistent popover presented with a close button
JavaScript interaction
There may be cases where you need to show or hide a popover via JavaScript. For example, if you need to show a popover at a specific time or if you need to hide a popover from an event outside of the controller, Stacks provides convenience methods to achieve this.
Stacks.application.register("section", class extends Stacks.StacksController {
static targets = ["help"];
showHelp(event) {
Stacks.showPopover(this.helpTarget);
event.stopPropagation();
}
hideHelp(event) {
Stacks.hidePopover(this.helpTarget);
}
}); Lorem ipsum
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et metus molestie nulla luctus sodales ac luctus justo. Aenean iaculis ac ante sit amet aliquam. Duis dolor velit, imperdiet sed mauris eu, sollicitudin egestas nisl.
JavaScript configuration (popovers)
Situations may also arise where popovers need to be attached to an element after the document is rendered. For example, a button could have a contextual menu that is too expensive to serve up on every page load.
Popovers can be attached to an element after the fact using Stacks.attachPopover.
This method takes three parameters, the element to attach the popover to, the popover either as an element or an HTML string, and optional options for displaying the popover.
Stacks.application.register("actions", class extends Stacks.StacksController {
var loaded = false;
async load() {
if (this.loaded) { return; }
Stacks.attachPopover(this.element,
await fetch(`/posts/{postId}/actions`),
{ autoShow: true, toggleOnClick: true });
this.loaded = true;
}
});Tooltips
When a popover is intended only for display as an on-hover tooltip and contains no interactive text, the s-tooltip controller can be used in place of s-popover. This is a separate controller that can be used alongside s-popover on a single target element.
Tooltip hover attributes
| Attribute | Description | Applied to |
|---|---|---|
id="{POPOVER_ID}" | A unique id that the popover's toggling element can target. Matches the value of `[aria-describedby]` on the toggling element. | .s-popover |
data-controller="s-tooltip" | Wires up the element to the tooltip controller. | Controller element |
data-s-tooltip-reference-selector="[selector]" | (optional) Designates the element to use as the tooltip reference. If left unset, defaults to the controller element. | Controller element |
aria-describedby="{POPOVER_ID}" | Associates the element to the desired popover element. | Reference element |
title="{TITLE}" | If `aria-describedby` is not present or valid, and the title attribute exists, the title will be removed from the element and used to create a popover immediately after the element. All content will be escaped and inserted as text. | Controller element |
data-s-tooltip-html-title="{TITLE}" | Acts the same as the `title` attribute, but inserts the raw text directly as HTML. If both exist on the element, this attribute takes precedence. | Controller element |
data-s-tooltip-placement="[placement]" | Dictates where to place the tooltip in relation to the reference element. Defaults to `bottom`. Accepted placements are `auto`, `top`, `right`, `bottom`, `left`, each with optional `-start` and `-end` variations. | Controller element |
Tooltip hover events
| Event | Description | Element |
|---|---|---|
s-tooltip:show | Fires immediately before showing and positioning the tooltip. Calling `.preventDefault()` cancels the display. | Controller element |
s-tooltip:shown | Fires immediately after showing the tooltip. | Controller element |
s-tooltip:hide | Fires immediately before hiding the tooltip. Calling `.preventDefault()` prevents the removal of the tooltip. | Controller element |
s-tooltip:hidden | Fires immediately after hiding the tooltip. | Controller element |
Tooltip dispatched events
| Event | Description | Element |
|---|---|---|
dispatcher | Contains the `Element` that initiated the event. For instance, the element hovered over to show, etc. | s-tooltip:* |
Tooltip examples
If the user doesn’t need to interact with the contents of the popover, it may be appropriate to only show it on hover. This will make popovers feel like a tooltip. To do so, we provide an alternative controller, s-tooltip, that shows the tooltip only on hover.
Title attribute
In the simple case where no markup is needed in the tooltip, the popover element can be omitted and automatically generated using the title attribute.
<button class="s-btn" role="button"
title="…"
data-controller="s-tooltip"
data-s-tooltip-placement="bottom-start">
…
</button> JavaScript configuration (tooltips)
In cases where the tooltip needs to display simple text or HTML, the popover can be configured using JavaScript. Plain text tooltips will render characters like <, >, and & as is. HTML tooltips will render the HTML as expected.
Stacks.setTooltipText(el,
"Plain text tooltip",
{
placement: "top-start"
});
Stacks.setTooltipHtml(el,
"Tooltip <i>with</i> HTML",
{
placement: "top-end"
});Rich tooltips
When a rich tooltip is required, a popover element can be configured in much the same way as an s-popover controller, with the most notable difference being the use of aria-describedby instead of aria-controls.
<button
class="s-btn"
role="button"
aria-describedby="tooltip-example"
aria-expanded="false"
data-controller="s-tooltip">
…
</button>
<div
class="s-popover s-popover__tooltip"
id="tooltip-example"
role="tooltip">
<div class="s-popover--content">
…
</div>
</div> There's no data associated with your account yet. Please visit our help page for more information.
Tooltips and interactive popovers
Hover tooltips can be used alongside interactive popovers. Tooltips will not appear when the interactive popover is visible.
<button class="s-btn s-btn__dropdown" role="button"
aria-controls="popover-example"
aria-expanded="false"
data-controller="s-popover s-tooltip"
data-action="s-popover#toggle"
data-s-popover-placement="bottom-start"
data-s-popover-toggle-class="is-selected"
title="…"
data-s-tooltip-placement="top-start">
…
</button>
<div class="s-popover"
id="popover-example"
role="menu">
<div class="s-popover--content">
…
</div>
</div> Manual placement
Our Stimulus popover controller handles the positioning of popovers for you. Popovers can be positioned manually for various legacy reasons, but we recommend using the s-popover controller's placement property instead.
Manual examples
Popovers can also be positioned manually if you aren’t using the built-in JavaScript interactivity. Practically, this might look like adding something like t8 l8 to .s-popover.
By default, popovers are hidden and positioned absolutely. Adding the class .is-visible will show the popover.
<div class="s-popover">
<div class="s-popover--content">
…
</div>
</div> Example popover with manual placement