Sections
Popovers are small content containers that provide a contextual overlay. They can be used as in-context feature explanations, dropdowns, or tooltips.
Classes
Section titled ClassesClass | Applies to | Description |
---|---|---|
.s-popover |
N/A | Base parent container for popovers |
.s-popover__tooltip |
.s-popover |
Removes minimum size constraints to support shorter tooltip text |
.is-visible |
.s-popover |
This class toggles the popover visibility |
.s-popover--close |
Child of .s-popover |
Used to dismiss a popover |
.s-popover--arrow |
Child of .s-popover |
When combined with JavaScript data attributes, this arrow element will be positioned automatically |
.s-popover--content |
Child of .s-popover |
Wrapper around the popover content to apply appropriate overflow styles |
Interactive popovers
Section titled Interactive popoversStacks provides a Stimulus controller that allows you to interactively display a popover from a source element. Positioning and arrow 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
Section titled Interactive AttributesAttribute | Applied to | Description |
---|---|---|
id="{POPOVER_ID}" |
.s-popover |
A unique id that the popover’s toggling element can target. Matches the value of [aria-controls] on the toggling element. |
data-controller="s-popover" |
Controller element | Wires up the element to the popover controller. This may be a toggling element or a wrapper element. |
data-s-popover-reference-selector="[css selector]" |
Controller element | Optional Designates the element to use as the popover reference. If left unset, the value defaults to the controller element. |
aria-controls="{POPOVER_ID}" |
Reference element | Associates the element to the desired popover element. |
data-action="s-popover#toggle" |
Toggling element | Wires up the element to toggle the visibility of a generic popover. |
data-s-popover-toggle-class="[class list]" |
Controller element | Adds an optional space-delineated list of classes to be toggled on the originating element when the popover is shown or hidden. |
data-s-popover-placement="[placement]" |
Controller element | Dictates where to place the popover in relation to the reference element. By default, the placement value is bottom . Accepted placements are auto , top , right , bottom , left . Each placement can take an additional -start and -end variation. |
data-s-popover-auto-show="[true|false]" |
Controller element | Optional If true , the popover will appear immediately when the Stacks controller is first connected. This should be used in place of .is-visible for displaying popovers on load as it will prevent the popover from appearing before it has been correctly positioned. |
data-s-popover-hide-on-outside-click="[always|never|if-in-viewport|after-dismissal]" |
Controller element | Optional
If left unset, defaults to |
Interactive Events
Section titled Interactive EventsEvent | Element | Description |
---|---|---|
s-popover:show |
Controller element | Default preventable Fires immediately before showing and positioning the popover. This fires before the popover is first displayed to the user, and can be used to create or initialize the popover element. Calling .preventDefault() cancels the display of the popover. |
s-popover:shown |
Controller element | Fires immediately after showing the popover. |
s-popover:hide |
Controller element | Default preventable Fires immediately before hiding the popover. Calling .preventDefault() prevents the removal of the popover. |
s-popover:hidden |
Controller element | Fires immediately after hiding the popover. |
event.detail | Applicable events | Description |
---|---|---|
dispatcher |
s-popover:* |
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. |
Examples
Section titled ExamplesDefault interactivity
Section titled Default interactivityTo 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--arrow"></div>
<div class="s-popover--content">
…
</div>
</div>
Dismissible
Section titled DismissibleIn 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__filled 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">
<div class="s-popover--arrow"></div>
<button class="s-popover--close s-btn s-btn__muted" aria-label="Close"
data-action="s-popover#toggle">
@Svg.ClearSm
</button>
<div class="s-popover--content">
…
</div>
</div>
</div>
Dismissible persistent popover presented with a close button
JavaScript interaction
Section titled JavaScript interactionThere 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. Ut vitae nulla eu risus iaculis semper sit amet vitae tortor. Sed convallis lacus quis libero placerat finibus. Phasellus pulvinar vel nunc eu tempor. Sed pharetra magna a felis egestas placerat. Sed imperdiet dui a sem fermentum, eget consectetur elit feugiat. Phasellus non condimentum orci. Nam id molestie elit, ut gravida metus. Vivamus vel nunc risus. Maecenas posuere sit amet tellus vel laoreet. Nulla lacus mauris, rhoncus eu laoreet quis, mattis vehicula nisl. Integer efficitur quam et nisi luctus scelerisque. Mauris efficitur lectus ac malesuada congue.
JavaScript configuration (popovers)
Section titled 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;
}
});
Hover tooltips
Section titled Hover tooltipsWhen 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.
Hover attributes
Section titled Hover attributesAttribute | Applied to | Description |
---|---|---|
id="{POPOVER_ID}" |
.s-popover |
A unique id that the popover’s toggling element can target. Matches the value of [aria-describedby] on the toggling element. |
data-controller="s-tooltip" |
Controller element | Wires up the element to the tooltip controller. |
data-s-tooltip-reference-selector="[css selector]" |
Controller element | Optional Designates the element to use as the tooltip reference. If left unset, the value defaults to the controller element. |
aria-describedby="{POPOVER_ID}" |
Reference element | Associates the element to the desired popover element. |
title="{TITLE}" |
Controller element | If aria-describedby is not present or valid, and the title attribute exists, the title will be removed from the element and be used to create a popover immediately after the element. All content will be escaped and inserted as text. |
data-s-tooltip-html-title="{TITLE}" |
Controller element | Acts the exact same as the title attribute, but inserts the raw text directly as html. If both this and the title attribute exist on the element, this attribute will be used. |
data-s-tooltip-placement="[placement]" |
Controller element | Dictates where to place the popover in relation to the reference element. By default, the placement value is bottom . Accepted placements are auto , top , right , bottom , left . Each placement can take an additional -start and -end variation. |
Hover events
Section titled Hover eventsEvent | Element | Description |
---|---|---|
s-tooltip:show |
Controller element | Default preventable Fires immediately before showing and positioning the tooltip. This fires before the tooltip is first displayed to the user, and can be used to create or initialize the tooltip element. Calling .preventDefault() cancels the display of the popover. |
s-tooltip:shown |
Controller element | Fires immediately after showing the tooltip. |
s-tooltip:hide |
Controller element | Default preventable Fires immediately before hiding the tooltip. Calling .preventDefault() prevents the removal of the tooltip. |
s-tooltip:hidden |
Controller element | Fires immediately after hiding the tooltip. |
event.detail | Applicable events | Description |
---|---|---|
dispatcher |
s-tooltip:* |
Contains the Element that initiated the event. For instance, the element hovered over to show, etc. |
Hover examples
Section titled Hover examplesIf 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
Section titled Title attributeIn 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)
Section titled 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
Section titled Rich tooltipsWhen 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"
data-s-tooltip-placement="top-start">
…
</button>
<div class="s-popover s-popover__tooltip"
id="tooltip-example"
role="tooltip"
aria-hidden="true">
<div class="s-popover--arrow"></div>
<div class="s-popover--content">
…
</div>
</div>
Tooltips and interactive popovers
Section titled Tooltips and interactive popoversHover tooltips can be used alongside interactive popovers. Tooltips will no 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--arrow"></div>
<div class="s-popover--content">
…
</div>
</div>
Manual placement
Section titled Manual placementManual classes
Section titled Manual classesClass | Applies to | Description |
---|---|---|
.s-popover--arrow__tc |
Child of .s-popover |
Popover arrow appears top center |
.s-popover--arrow__tl |
Child of .s-popover |
Popover arrow appears top left |
.s-popover--arrow__tr |
Child of .s-popover |
Popover arrow appears top right |
.s-popover--arrow__bc |
Child of .s-popover |
Popover arrow appears bottom center |
.s-popover--arrow__bl |
Child of .s-popover |
Popover arrow appears bottom left |
.s-popover--arrow__br |
Child of .s-popover |
Popover arrow appears bottom right |
.s-popover--arrow__rc |
Child of .s-popover |
Popover arrow appears right center |
.s-popover--arrow__rt |
Child of .s-popover |
Popover arrow appears right top |
.s-popover--arrow__rb |
Child of .s-popover |
Popover arrow appears right bottom |
.s-popover--arrow__lc |
Child of .s-popover |
Popover arrow appears left center |
.s-popover--arrow__lt |
Child of .s-popover |
Popover arrow appears left top |
.s-popover--arrow__lb |
Child of .s-popover |
Popover arrow appears left bottom |
Manual examples
Section titled Manual examplesPopovers 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
.
Manual positioning for arrows is also provided. Arrow direction (top, right, bottom, left) will appear first, followed by the secondary (center, top, right, bottom, left). For example, a popover with an arrow child of .s-popover--arrow__tc
will appear on the top of the .s-popover
and centered horizontally. Though there are sensible defaults applied to the width of popovers, you may need to adjust manually.
By default, popovers are hidden and positioned absolutely. Adding the class .is-visible
will show the popover.
If you want to set the background color to something other than the default, simply add your desired bg-*
class to the s-popover
element and a matching fc-*
class to the s-popover--arrow
element.
<div class="s-popover">
<div class="s-popover--arrow s-popover--arrow__tc"></div>
<div class="s-popover--content">
…
</div>
</div>
.s-popover--arrow__tc
Popover arrow appears top center
.s-popover--arrow__tl
Popover arrow appears top left
.s-popover--arrow__tr
Popover arrow appears top right
.s-popover--arrow__bc
Popover arrow appears bottom center
.s-popover--arrow__bl
Popover arrow appears bottom left
.s-popover--arrow__br
Popover arrow appears bottom right
.s-popover--arrow__rc
Popover arrow appears right center
.s-popover--arrow__rt
Popover arrow appears right top
.s-popover--arrow__rb
Popover arrow appears right bottom
.s-popover--arrow__lc
Popover arrow appears left center
.s-popover--arrow__lt
Popover arrow appears left top
.s-popover--arrow__lb
Popover arrow appears left bottom