A small raised panel that opens from a button and stays beside it, holding a title, a few lines or a field, and actions, without blocking the page.
Version history
Every save is kept for thirty days. Restore one to bring the page back as it was.
Options
| Choice | Values | Default | What it does |
|---|---|---|---|
alignsmbk-popover--align-<value> | startend | start | Which edge of the opener the panel lines up with. End keeps a popover at the right of a bar inside the window. |
sidesmbk-popover--side-<value> | belowaboveleftright | below | The side of the opener the panel prefers. With script it takes the opposite side when this one has no room, as at the foot of the window, and below when neither the left nor the right has room, as on a phone. |
pointersmbk-popover--pointer | On or off | Off | A small triangle on the panel's edge, pointing at the middle of what opened it. |
Examples
Usage
A title, a few lines, and an action under a divider.
Version history
Every save is kept for thirty days. Restore one to bring the page back as it was.
Full header
An icon, a description, a close button and a divider in the header; helper text at the foot.
Page settings
Changes apply to this page only.
Hide the page from search, or turn comments off while it is a draft.
With a field
The body holds a field, for a quick change without leaving the page.
Invite someone
With two actions
A button group at the foot, lined up at the end.
Review the change
Maya asked to rename the project to Atlas.
Helper text
A line of help at the foot instead of actions.
Keyboard shortcuts
Press the question mark anywhere to see every shortcut.
With opener
Press the button to open it under the button, or over it when there is no room below. Escape, a click outside or the close button closes it.
Version history
Every save is kept for thirty days. Restore one to bring the page back as it was.
Guidelines
Purpose
A small raised panel that opens from a button and stays beside it, for a little more than a tooltip can hold: a title, a few lines, a field, and one or two actions. The page behind it stays usable. It differs from the tooltip, which holds a few words and nothing to press; from the dropdown, which is a list of actions or links; and from the modal, which stops everything else until it is answered.
When to use
- A short explanation with a way to read more, opened from an info button.
- A quick change that belongs to one thing on the page: invite someone, rename, pick a setting.
- A short review with two answers, such as approve and decline.
- A card that previews what a name or a tag points at, such as a person or a place, with opening on hover turned on.
When not to use
- A few words naming a button. Use the tooltip.
- A list of actions or links. Use the dropdown.
- A task that must be finished or canceled before anything else. Use the modal.
- A whole form or long reading. Use a page or a drawer; one click outside closes a popover.
- Opening on hover, for a card that holds the only way to an action. The pointer leaving closes it, so leave the switch off and let a press open it.
Usage rules
Do
- Keep it short: a title, a few lines, one or two actions.
- Give it a close button when it holds a field or actions.
- Align a popover at the end of a bar to the end, so it opens inward.
- Prefer the side above for an opener at the foot of a page or a panel.
- Prefer the left or the right where a panel under the opener would cover what a reader is comparing it with, such as the next row or the next item in a graph.
- Give it a pointer when openers sit close together, so it is clear which one the panel belongs to.
- Turn on opening on hover for a card that previews what its opener names, and keep what it says to something a reader can take in where they rest.
- Name an opener with
openerIdonly where the popover cannot draw it, such as a table row's own button or words in a paragraph. A second button beside it would be two Tab stops with one job. - Put a popover with
openerIdsoon after its button on the page, such as in a cell of its row, so Tab goes on through the panel from where the reader was.
Don't
- Open it on hover alone. A press and the keyboard always open it too, and the opener stays a button.
- Put anything in it that the reader must not lose to a stray click outside.
- Use it for a site's navigation.
Version history
Every save is kept for thirty days.
Keep it to a few lines and one or two actions about the thing it opened from.
Version history
Every save is kept for thirty days, and every change by every person is listed with the time, the person, the part of the page and the words before and after, so you can compare any two versions side by side before you restore one.
A whole form or long reading in a popover is lost with one click outside; use a page, a drawer or a modal.
Anatomy
- The anchor part, present when the popover draws its own opener: it holds the opener and the layer part, and the layer holds the panel, which hangs from the anchor until it is placed.
- The opener, supplied by the consumer: a button, or a piece of text made a press with the link's
as="button". The popover tells it whether the panel is open and which panel it controls. An opener the popover does not draw is named by its id, and the popover draws no anchor part and leaves that button as it is. - The block, the raised panel at the popover width, in three sections spaced by the large stack step.
- An optional pointer part, a small triangle on the edge facing the opener that points at the opener's middle. With it, what the panel holds sits in the scroll part, which scrolls in the panel's place, since a panel that scrolled would cut the pointer off.
- The header part: the heading part (an optional icon part, the titles part with the title part and an optional description part) and an optional divider part under it. An optional close part, the small ghost icon button with its cross in the subtlest text color, sits centred on the title's first line, over the padding at the panel's end.
- The body part: a few lines in the description's look, or a field, or both.
- The footer part: a divider part, on unless turned off, then the actions at the end (one button, or a button group for several) or the helper part, a line of small text.
Behavior
Pressing the opener shows the panel beside it, on the side the side axis prefers: below unless it says above, left or right. It lines up with the opener's start or end edge, or its top or foot on the left and right. Pressing it again, pressing Escape, pressing the close button, clicking outside or moving focus outside closes it. Only what is open on top closes: with a dropdown open inside the popover, Escape or a click outside closes the dropdown first, and a popover inside a modal closes before the modal. The panel is lifted over the page while it shows, so no scrolling or clipped part around the opener cuts it off. Without room on the side it prefers, as at the foot of the window, it opens on the opposite side, and preferring the left or the right with room on neither, as on a phone, it opens below; near the window's edge it moves inward; it is never taller than the room on its side, and scrolls inside itself when its content is taller. It stays beside the opener as the page scrolls. An opener inside a part of the page that goes full screen keeps its panel shown over that part. The panel comes out of the opener: it drops across the gap, growing from its edge nearest the opener, and fades in with the enter purpose, rising and growing upward instead when it opened above and sliding out sideways when it opened at the left or the right, and goes back the same way, toward the opener, with the exit purpose; when the reader's system asks for less motion it fades in and out in place, without traveling or growing. Without script it hangs under the opener, over it on the above side, or beside it on the left and right sides, under it in a narrow space. A closed panel is hidden, not removed, so anything typed into a field inside it is still there when it opens again.
With pointer, a switch that is off unless a page turns it on, the panel points at the middle of its opener, on whichever side it opened, and keeps pointing there when it has moved inward at the window's edge. The pointer stays clear of the panel's corners, so at a very small opener it points a little past the middle. It stands a little further from the opener, by the pointer's reach.
With openOnHover, a switch that is off unless a page turns it on, the panel also opens once a mouse has rested on the opener for as long as a tooltip waits, so a pointer passing over opens nothing. It stays while the pointer moves from the opener into the panel, closes a moment after the pointer has left both, and stays open if the pointer comes back within that moment. Opened this way it leaves focus where it is, and Escape closes it without the pointer moving. Pressing the opener then keeps it open as a press would have opened it: focus moves in, and the pointer leaving no longer closes it; moving focus into the panel does the same. A press, the keyboard and touch open it just as they do without the switch.
Without an opener, the panel draws itself open in place, and opening, closing and placing it belong to whoever shows it; its close button calls the change handler.
The opener is given as trigger. It can be any element that renders one button and passes the id, aria attributes and click and key handlers it is given on to that button, as the dropdown's opener does; the design system's Button, Tag and a plain button all do. The full interface is written out at the top of the React wrapper.
A piece of text can be the opener too, such as a name that opens its card: give trigger a link with as="button". The words keep the look of the text around them and stay in their line, breaking with it. A panel cannot stand inside a paragraph, so a name in a p element is named with openerId and the popover goes after the paragraph.
An opener the popover cannot draw, such as the button a table row holds, is named instead: pass its id as openerId, in place of trigger. The popover then draws only its panel, where the page puts it, and the panel hangs on that button. The page holds open and sets it from the button's own press, which can simply turn it over: a press on that button is no press outside, so it closes what it opened and never opens it again. Focus moves in as the panel opens, and Escape and the close button give it back to that button, as with trigger. To hang the panel on something else, such as the name in a row whose button runs the width of the table, give that element's id as hangOnId; it takes no press and no focus. A copy of the button that takes presses for it counts as the button when it names the button's id in data-copy-of, as the table's held cell does by itself. Opening on hover takes a trigger, since the pointer rests on what the popover draws.
Accessibility
What it does for you
- The panel is a non-modal dialog, named by its header's title, or by its label when it has no header.
- An opener given as
triggersays it opens a dialog, whether it is open, and which panel it controls. - Opening moves focus into the panel, so a screen reader announces it: to its first field when it holds one, otherwise to the panel itself, named by its title, which draws no ring of its own. Tab goes on through the panel. A popover open as the page loads takes no focus.
- Escape, from the opener or anywhere in the panel, closes it and returns focus to the opener, as the close button does, an opener named with
openerIdincluded. A click outside closes it without moving focus. - Nothing in it is reachable only by hover: with opening on hover turned on, a press and the keyboard still open it.
- The pointer is a picture only, hidden from screen readers.
- A panel a resting pointer opened stays while the pointer is on it, and closes with Escape without the pointer moving.
- Every word it says by itself, such as the close button's name, keeps its English unless the page gives its own.
What you must do
- Give it a close button whenever it holds a field or actions, so touch users have a clear way out. It is named "Close".
- Give the popover a title in its header, or a label when it has no header, so the dialog has a name.
- Give the close button's name in the page's own language when the page is not in English.
- With
openerId, the popover writes nothing on the button it does not draw: give that buttonaria-haspopup="dialog", and say while the panel is open, as a table row'scurrentdoes.
How to test
- Press Enter on the opener: the panel opens and focus moves into it; a screen reader says its title. Press Tab: focus moves through the panel.
- Press Escape inside the panel: it closes and focus returns to the opener.
- With a screen reader, the opener is read as a button that opens a dialog, open or closed, and the panel as a dialog by its title.
- With text at 200% and the page 320 pixels wide, the panel stays inside the window and scrolls inside itself when its content is taller.
- In a forced-color mode, the focus rings on the opener and the close button stay visible, and so does the pointer.
- On a touch screen, the close button closes it.
- With opening on hover turned on, rest the pointer on the opener: the panel opens and focus stays where it was. Move the pointer onto the panel: it stays. Press Escape: it closes.
- With
openerId, press Enter on the button named: the panel opens and focus moves into it. Press Escape: it closes and focus is back on that button. Press the button twice with the pointer: the second press closes the panel and it stays closed.
Keyboard
| Key | What it does |
|---|---|
| EnterorSpace | On the opener, opens the panel and moves focus into it, or closes it. |
| Tab | With the panel open, moves from the opener into the panel, then through what it holds. |
| Escape | Closes the panel and returns focus to the opener. With a dropdown open inside it, closes the dropdown first. |
Source
The files the popover is made of. Its folder on GitHub (opens in a new tab) holds them with its wrapper and its document.
Stylesheet
/* smbk-popover — Popover. Figma "Popover" 4804:65705, over "PopoverHeader"
* 4804:65619, "PopoverBody" 4804:65655 and "PopoverFooter" 4803:65513.
* A panel on the overlay surface in three sections spaced by the large stack step: a header
* (an optional icon, the title, an optional description, an optional close
* button and an optional divider), a body of text or a field, and a footer
* (a divider, then buttons at the end or a line of helper text). The width is
* the popover width token, carried from Figma's frame, and never more than the
* room it is given.
* With an opener, the anchor holds the opener and the layer, and the layer the
* panel, which hangs under the opener (or over it, on the above side) as the
* dropdown's does, the same gap away, or beside it on the left and right sides.
* With script, the layer is lifted over the page while the panel shows and the
* wrapper places the panel on the side it prefers, or the other when that one has
* no room (floating.ts). With the pointer flag, what the panel holds sits in the
* scroll part and the pointer beside it, on the edge facing the opener. */
.smbk-popover {
box-sizing: border-box;
position: relative;
display: flex;
flex-direction: column;
gap: var(--smbk-space-stack-lg);
width: var(--smbk-size-popover-width);
max-width: 100%;
margin: 0;
padding: var(--smbk-space-inset-xl);
border: var(--smbk-border-width-hairline) solid var(--smbk-color-border-rim);
border-radius: var(--smbk-radius-container-sm);
background: var(--smbk-color-surface-overlay);
color: var(--smbk-color-text-default);
box-shadow: var(--smbk-elevation-overlay);
text-align: start;
/* Its words wrap wherever it opens: a table heading or a bar keeps its own on one line, and the panel would take that
and scroll sideways. */
white-space: normal;
}
/* The panel takes focus as it opens when it holds no field, as a container, not a control: no ring of its own. */
.smbk-popover:focus {
outline: none;
}
/* ---- header ---- */
.smbk-popover__header {
display: flex;
flex-direction: column;
gap: var(--smbk-space-stack-md);
}
.smbk-popover__heading {
display: flex;
align-items: flex-start;
gap: var(--smbk-space-inline-md);
}
/* The words keep clear of the close button, which hangs past the row's end (below). */
.smbk-popover__heading:has(.smbk-popover__close) {
padding-inline-end: calc(var(--smbk-size-control-sm) - (var(--smbk-space-inset-xl) - var(--smbk-space-inset-sm)) + var(--smbk-space-inline-md));
}
/* As tall as the title's line, so the icon sits on its middle. */
.smbk-popover__icon {
display: inline-flex;
flex-shrink: 0;
align-items: center;
height: calc(var(--smbk-typography-label-lg-font-size) * var(--smbk-typography-label-lg-line-height));
color: var(--smbk-color-text-subtle);
font-size: var(--smbk-size-icon-sm);
}
/* The title sits straight on its description: the line heights give the air. */
.smbk-popover__titles {
display: flex;
flex: 1 1 auto;
flex-direction: column;
gap: var(--smbk-space-text-label);
min-width: 0;
}
.smbk-popover__title {
display: block;
margin: 0;
color: var(--smbk-color-text-default);
font-family: var(--smbk-typography-label-lg-font-family);
font-size: var(--smbk-typography-label-lg-font-size);
font-weight: var(--smbk-typography-label-lg-font-weight);
line-height: var(--smbk-typography-label-lg-line-height);
letter-spacing: var(--smbk-typography-label-lg-letter-spacing);
}
.smbk-popover__description {
margin: 0;
color: var(--smbk-color-text-subtle);
font-family: var(--smbk-typography-body-sm-font-family);
font-size: var(--smbk-typography-body-sm-font-size);
font-weight: var(--smbk-typography-body-sm-font-weight);
line-height: var(--smbk-typography-body-sm-line-height);
letter-spacing: var(--smbk-typography-body-sm-letter-spacing);
}
/* The close button is centred on the title's first line, its end edge the small inset from the panel's
end edge, over the padding rather than in the row, as Figma places it. Its cross is the subtlest text
color, as Figma binds it. */
.smbk-popover .smbk-popover__close {
position: absolute;
top: calc(var(--smbk-space-inset-xl) + (var(--smbk-typography-label-lg-font-size) * var(--smbk-typography-label-lg-line-height) - var(--smbk-size-control-sm)) / 2);
inset-inline-end: var(--smbk-space-inset-sm);
color: var(--smbk-color-text-subtlest);
}
.smbk-popover__divider {
margin: 0;
}
/* ---- body: a few lines in the description's look, a field, or both ---- */
.smbk-popover__body {
display: flex;
flex-direction: column;
gap: var(--smbk-space-stack-md);
}
/* ---- footer: a divider, then actions at the end or helper text ---- */
.smbk-popover__footer {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: var(--smbk-space-stack-md);
}
.smbk-popover__footer > .smbk-popover__divider,
.smbk-popover__helper {
align-self: stretch;
}
.smbk-popover__helper {
margin: 0;
color: var(--smbk-color-text-subtle);
font-family: var(--smbk-typography-body-xs-font-family);
font-size: var(--smbk-typography-body-xs-font-size);
font-weight: var(--smbk-typography-body-xs-font-weight);
line-height: var(--smbk-typography-body-xs-line-height);
letter-spacing: var(--smbk-typography-body-xs-letter-spacing);
}
/* ---- the opener: the anchor holds the opener and the layer, the layer the panel hanging under the opener ----
The layer, its lift into the top layer, the closed panel and the panel's motion (as the dropdown's panel, it drops
in from its opener and fades, rises when it opened above, and does not travel under reduced motion) are the
floating panels' own, shared with the dropdown and the tooltip: floating/floating.css. */
.smbk-popover__anchor {
position: relative;
display: inline-flex;
}
/* Words as the opener stay in their line: a flex box would make them a box of their own, which does not break with
the words around it. An inline box ends at the words, so the padding takes in their line for the panel to hang
under; it moves nothing in the line. */
.smbk-popover__anchor:has(> .smbk-link--pressable) {
display: inline;
padding-block-end: var(--smbk-space-link-underline);
}
/* Until the wrapper lifts the layer, the panel hangs from the anchor, as in markup without the layer. Hanging from its
opener, the panel is never wider than the window less the extra-large inset on either side, so on the narrowest
phones it narrows rather than make the page scroll sideways. */
.smbk-popover__anchor > .smbk-popover,
.smbk-popover__layer > .smbk-popover {
position: absolute;
top: calc(100% + var(--smbk-space-stack-xs));
left: 0;
z-index: var(--smbk-layer-overlay);
max-width: calc(100vw - 2 * var(--smbk-space-inset-xl));
}
.smbk-popover__anchor > .smbk-popover--align-end,
.smbk-popover__layer > .smbk-popover--align-end {
right: 0;
left: auto;
}
/* Preferring the side above, it hangs over the opener until the wrapper places it. */
.smbk-popover__anchor > .smbk-popover--side-above,
.smbk-popover__layer > .smbk-popover--side-above {
top: auto;
bottom: calc(100% + var(--smbk-space-stack-xs));
}
/* Preferring the left or the right, it hangs beside the opener, its top lined up with the opener's, or its foot when
lined up with the end, until the wrapper places it. */
.smbk-popover__anchor > :is(.smbk-popover--side-left, .smbk-popover--side-right),
.smbk-popover__layer > :is(.smbk-popover--side-left, .smbk-popover--side-right) {
top: 0;
bottom: auto;
}
.smbk-popover__anchor > .smbk-popover--side-right,
.smbk-popover__layer > .smbk-popover--side-right {
right: auto;
left: calc(100% + var(--smbk-space-stack-xs));
}
.smbk-popover__anchor > .smbk-popover--side-left,
.smbk-popover__layer > .smbk-popover--side-left {
right: calc(100% + var(--smbk-space-stack-xs));
left: auto;
}
.smbk-popover__anchor > .smbk-popover--align-end:where(.smbk-popover--side-left, .smbk-popover--side-right),
.smbk-popover__layer > .smbk-popover--align-end:where(.smbk-popover--side-left, .smbk-popover--side-right) {
top: auto;
bottom: 0;
}
/* Placed in the lifted layer, it is never taller than the room on its side of the opener (the wrapper sets that
height), and a panel taller than that scrolls inside itself, with the system's scrollbar over it (scrollbar.ts), or
the browser's thin one in markup written by hand. */
.smbk-popover__layer:popover-open > .smbk-popover {
overflow-y: auto;
overscroll-behavior: contain;
scrollbar-width: thin;
scrollbar-color: var(--smbk-color-border-subtle) transparent;
}
/* ---- the pointer: a small triangle on the edge facing the opener ----
A panel that scrolled would cut it off, so with the pointer the panel keeps what sticks out past its edge and what it
holds scrolls in the scroll part, which takes the panel's padding and its gap. */
.smbk-popover--pointer {
--smbk-popover-pointer-base: calc(var(--smbk-size-icon-lg) - 2 * var(--smbk-border-width-hairline));
--smbk-popover-pointer-depth: calc(var(--smbk-popover-pointer-base) / 2);
gap: 0;
padding: 0;
}
.smbk-popover__scroll {
box-sizing: border-box;
position: relative;
display: flex;
flex: 1 1 auto;
flex-direction: column;
gap: var(--smbk-space-stack-lg);
min-height: 0;
padding: var(--smbk-space-inset-xl);
border-radius: inherit;
}
/* The keyboard reaches it while it scrolls (the wrapper makes it a region then): its ring is drawn inside its edge. */
.smbk-popover__scroll:focus-visible {
outline: var(--smbk-focus-ring-width) solid var(--smbk-color-border-focus);
outline-offset: calc(-1 * var(--smbk-focus-ring-width));
}
.smbk-popover__layer:popover-open > .smbk-popover--pointer {
overflow: visible;
}
.smbk-popover__layer:popover-open > .smbk-popover--pointer > .smbk-popover__scroll {
overflow-y: auto;
overscroll-behavior: contain;
scrollbar-width: thin;
scrollbar-color: var(--smbk-color-border-subtle) transparent;
}
/* Hanging from its opener, the panel stands the pointer's reach further off, as the wrapper places it. These weigh no
more than the other hanging rules, so a lifted panel still takes only the place the wrapper writes (floating.css). */
.smbk-popover__anchor > .smbk-popover--pointer:where(.smbk-popover--side-below),
.smbk-popover__layer > .smbk-popover--pointer:where(.smbk-popover--side-below) {
top: calc(100% + var(--smbk-space-stack-xs) + var(--smbk-popover-pointer-depth) - var(--smbk-border-width-hairline));
}
.smbk-popover__anchor > .smbk-popover--pointer:where(.smbk-popover--side-above),
.smbk-popover__layer > .smbk-popover--pointer:where(.smbk-popover--side-above) {
bottom: calc(100% + var(--smbk-space-stack-xs) + var(--smbk-popover-pointer-depth) - var(--smbk-border-width-hairline));
}
.smbk-popover__anchor > .smbk-popover--pointer:where(.smbk-popover--side-right),
.smbk-popover__layer > .smbk-popover--pointer:where(.smbk-popover--side-right) {
left: calc(100% + var(--smbk-space-stack-xs) + var(--smbk-popover-pointer-depth) - var(--smbk-border-width-hairline));
}
.smbk-popover__anchor > .smbk-popover--pointer:where(.smbk-popover--side-left),
.smbk-popover__layer > .smbk-popover--pointer:where(.smbk-popover--side-left) {
right: calc(100% + var(--smbk-space-stack-xs) + var(--smbk-popover-pointer-depth) - var(--smbk-border-width-hairline));
}
/* In a narrow space there is no room beside the opener: it hangs under it, as the wrapper places it. */
@container style(--smbk-layout: narrow) {
.smbk-popover__anchor > :is(.smbk-popover--side-left, .smbk-popover--side-right),
.smbk-popover__layer > :is(.smbk-popover--side-left, .smbk-popover--side-right) {
top: calc(100% + var(--smbk-space-stack-xs));
right: auto;
bottom: auto;
left: 0;
}
.smbk-popover__anchor > :is(.smbk-popover--side-left, .smbk-popover--side-right) > .smbk-popover__pointer,
.smbk-popover__layer:not(:popover-open) > :is(.smbk-popover--side-left, .smbk-popover--side-right):not([data-side]) > .smbk-popover__pointer {
display: none;
}
}
/* Its box is the triangle's, as deep as its shape, which the wrapper measures. It stands on the panel's inside edge, so
it covers the hairline there and the two read as one shape. Its own edge is the strong border: the panel's rim is
nearly the panel's color, and with no shadow the triangle would be lost on a light page. The fill is the same
triangle moved in by a hairline across its slopes, so the edge shows only along them. */
.smbk-popover__pointer {
position: absolute;
width: var(--smbk-popover-pointer-base);
height: var(--smbk-popover-pointer-depth);
background: var(--smbk-color-border-strong);
}
.smbk-popover__pointer::after {
content: "";
position: absolute;
inset: 0;
background: var(--smbk-color-surface-overlay);
clip-path: inherit;
}
/* The side it is on: the one the wrapper placed it on, or until then the one asked for. */
.smbk-popover--pointer:is([data-side="below"], .smbk-popover--side-below:not([data-side])) > .smbk-popover__pointer {
bottom: 100%;
clip-path: polygon(50% 0, 100% 100%, 0 100%);
}
.smbk-popover--pointer:is([data-side="below"], .smbk-popover--side-below:not([data-side])) > .smbk-popover__pointer::after {
translate: 0 calc(var(--smbk-border-width-hairline) * 1.4142);
}
.smbk-popover--pointer:is([data-side="above"], .smbk-popover--side-above:not([data-side])) > .smbk-popover__pointer {
top: 100%;
clip-path: polygon(50% 100%, 0 0, 100% 0);
}
.smbk-popover--pointer:is([data-side="above"], .smbk-popover--side-above:not([data-side])) > .smbk-popover__pointer::after {
translate: 0 calc(var(--smbk-border-width-hairline) * -1.4142);
}
.smbk-popover--pointer:is([data-side="right"], .smbk-popover--side-right:not([data-side])) > .smbk-popover__pointer {
right: 100%;
width: var(--smbk-popover-pointer-depth);
height: var(--smbk-popover-pointer-base);
clip-path: polygon(0 50%, 100% 0, 100% 100%);
}
.smbk-popover--pointer:is([data-side="right"], .smbk-popover--side-right:not([data-side])) > .smbk-popover__pointer::after {
translate: calc(var(--smbk-border-width-hairline) * 1.4142) 0;
}
.smbk-popover--pointer:is([data-side="left"], .smbk-popover--side-left:not([data-side])) > .smbk-popover__pointer {
left: 100%;
width: var(--smbk-popover-pointer-depth);
height: var(--smbk-popover-pointer-base);
clip-path: polygon(100% 50%, 0 0, 0 100%);
}
.smbk-popover--pointer:is([data-side="left"], .smbk-popover--side-left:not([data-side])) > .smbk-popover__pointer::after {
translate: calc(var(--smbk-border-width-hairline) * -1.4142) 0;
}
/* Where along the edge: until it is placed, the inset in from the end the panel lines up with. */
.smbk-popover--pointer:is([data-side="below"], [data-side="above"], .smbk-popover--side-below:not([data-side]), .smbk-popover--side-above:not([data-side])) > .smbk-popover__pointer {
left: var(--smbk-space-inset-xl);
}
.smbk-popover--pointer.smbk-popover--align-end:is([data-side="below"], [data-side="above"], .smbk-popover--side-below:not([data-side]), .smbk-popover--side-above:not([data-side])) > .smbk-popover__pointer {
right: var(--smbk-space-inset-xl);
left: auto;
}
.smbk-popover--pointer:is([data-side="left"], [data-side="right"], .smbk-popover--side-left:not([data-side]), .smbk-popover--side-right:not([data-side])) > .smbk-popover__pointer {
top: var(--smbk-space-inset-xl);
}
.smbk-popover--pointer.smbk-popover--align-end:is([data-side="left"], [data-side="right"], .smbk-popover--side-left:not([data-side]), .smbk-popover--side-right:not([data-side])) > .smbk-popover__pointer {
top: auto;
bottom: var(--smbk-space-inset-xl);
}
/* Placed, the wrapper writes the place of its middle, measured from the panel's outside edge, where the pointer's own
place is measured from inside it: the hairline between the two is taken back here. */
.smbk-popover--pointer[data-placed]:is([data-side="below"], [data-side="above"]) > .smbk-popover__pointer {
right: auto;
translate: calc(-50% - var(--smbk-border-width-hairline)) 0;
}
.smbk-popover--pointer[data-placed]:is([data-side="left"], [data-side="right"]) > .smbk-popover__pointer {
bottom: auto;
translate: 0 calc(-50% - var(--smbk-border-width-hairline));
}
/* ---- high contrast (forced colors) ---- */
/* The browser drops fills there: drawn in the user's own colors, the pointer points again and keeps its edge. */
@media (forced-colors: active) {
.smbk-popover__pointer {
background: CanvasText;
}
.smbk-popover__pointer::after {
background: Canvas;
}
}Tokens it reads
| Token and CSS | Value | Preview | Foundation |
|---|---|---|---|
| motion.enter.duration--smbk-motion-enter-duration | 320ms | Motion | |
| motion.enter.easing--smbk-motion-enter-easing | cubic-bezier(0.16, 1, 0.3, 1) | Motion | |
| space.stack.xs--smbk-space-stack-xs | 4px | Spacing | |
| motion.exit.duration--smbk-motion-exit-duration | 200ms | Motion | |
| motion.exit.easing--smbk-motion-exit-easing | cubic-bezier(0.7, 0, 0.84, 0) | Motion | |
| space.stack.lg--smbk-space-stack-lg | comfortable: 16pxcompact: 12pxchanges with density | Spacing | |
| size.popover.width--smbk-size-popover-width | 320px | Size | |
| space.inset.xl--smbk-space-inset-xl | comfortable: 16pxcompact: 12pxchanges with density | Spacing | |
| border-width.hairline--smbk-border-width-hairline | 1px | Border width | |
| color.border.rim--smbk-color-border-rim | light: #ffffffe6dark: #ffffff0dchanges with theme | Color | |
| radius.container.sm--smbk-radius-container-sm | 8px | Radius | |
| color.surface.overlay--smbk-color-surface-overlay | light: #ffffffdark: #2d3237changes with theme | Color | |
| color.text.default--smbk-color-text-default | light: #232527dark: #e7ebf0changes with theme | Color | |
| elevation.overlay--smbk-elevation-overlay | light: 0px 12px 16px 2px #373d431a, 0px 2px 8px 0px #373d4326dark: 0px 12px 16px 2px #00000026, 0px 2px 8px 0px #00000033 | Elevation | |
| space.stack.md--smbk-space-stack-md | comfortable: 12pxcompact: 8pxchanges with density | Spacing | |
| space.inline.md--smbk-space-inline-md | comfortable: 8pxcompact: 6pxchanges with density | Spacing | |
| size.control.sm--smbk-size-control-sm | comfortable: 32pxcompact: 28pxchanges with density | Size | |
| space.inset.sm--smbk-space-inset-sm | comfortable: 6pxcompact: 4pxchanges with density | Spacing | |
| typography.label-lg--smbk-typography-label-lg-font-family--smbk-typography-label-lg-font-size--smbk-typography-label-lg-font-weight--smbk-typography-label-lg-line-height--smbk-typography-label-lg-letter-spacing--smbk-typography-label-lg-text-transform | fontFamily: Geist, Geist Fallback, system-ui, sans-seriffontSize: 16pxfontWeight: 500lineHeight: 1.4letterSpacing: 0px | The quick brown fox | Typography |
| color.text.subtle--smbk-color-text-subtle | light: #4e555edark: #9ba5b1changes with theme | Color | |
| size.icon.sm--smbk-size-icon-sm | 16px | Size | |
| space.text.label--smbk-space-text-label | 0px | Spacing | |
| typography.body-sm--smbk-typography-body-sm-font-family--smbk-typography-body-sm-font-size--smbk-typography-body-sm-font-weight--smbk-typography-body-sm-line-height--smbk-typography-body-sm-letter-spacing--smbk-typography-body-sm-text-transform | fontFamily: Geist, Geist Fallback, system-ui, sans-seriffontSize: 14pxfontWeight: 400lineHeight: 1.5letterSpacing: 0px | The quick brown fox | Typography |
| color.text.subtlest--smbk-color-text-subtlest | light: #606a75dark: #7c8692changes with theme | Color | |
| typography.body-xs--smbk-typography-body-xs-font-family--smbk-typography-body-xs-font-size--smbk-typography-body-xs-font-weight--smbk-typography-body-xs-line-height--smbk-typography-body-xs-letter-spacing--smbk-typography-body-xs-text-transform | fontFamily: Geist, Geist Fallback, system-ui, sans-seriffontSize: 12pxfontWeight: 400lineHeight: 1.5letterSpacing: 0px | The quick brown fox | Typography |
| space.link-underline--smbk-space-link-underline | 4px | Spacing | |
| layer.overlay--smbk-layer-overlay | 1200 | Stacking order | |
| color.border.subtle--smbk-color-border-subtle | light: #c4cdd8dark: #2d3237changes with theme | Color | |
| size.icon.lg--smbk-size-icon-lg | 24px | Size | |
| focus.ring.width--smbk-focus-ring-width | 2px | Focus | |
| color.border.focus--smbk-color-border-focus | light: #cc0d0ddark: #f34336changes with theme | Color | |
| color.border.strong--smbk-color-border-strong | light: #9ba5b1dark: #373d43changes with theme | Color |
Manifest
{
"name": "popover",
"block": "smbk-popover",
"summary": "A small raised panel that opens from a button and stays beside it, holding a title, a few lines or a field, and actions, without blocking the page.",
"adapt": "css",
"axes": {
"align": {
"values": [
"start",
"end"
],
"default": "start",
"description": "Which edge of the opener the panel lines up with. End keeps a popover at the right of a bar inside the window."
},
"side": {
"values": [
"below",
"above",
"left",
"right"
],
"default": "below",
"description": "The side of the opener the panel prefers. With script it takes the opposite side when this one has no room, as at the foot of the window, and below when neither the left nor the right has room, as on a phone."
}
},
"flags": {
"pointer": {
"description": "A small triangle on the panel's edge, pointing at the middle of what opened it."
}
},
"parts": [
"anchor",
"layer",
"scroll",
"pointer",
"header",
"heading",
"icon",
"titles",
"title",
"description",
"close",
"divider",
"body",
"footer",
"helper"
],
"figmaNodeId": "4804:65705"
}