Popover
<gui-popover> floats a panel of content next to the button that opens it, such as a set of
filters or a short form. The panel is your own element with the popover attribute, so the
browser does the hard parts: it shows the panel above everything else, closes it on
Escape or a click outside, and manages the focus. The element places the panel next to
its button and keeps it there.
import { GuiButton, GuiCheckbox, GuiPopover,} from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function PopoverBasic() { return ( <> <GuiButton label="Filters" variant="outlined" commandFor="task-filters" command="toggle-popover" /> <GuiPopover> <div id="task-filters" popover="auto"> <GuiCheckbox label="Assigned to me" value /> <GuiCheckbox label="Due this week" /> <GuiCheckbox label="Show completed" /> </div> </GuiPopover> </> );}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/button';import '@golemui/gui-components/checkbox';import '@golemui/gui-components/popover';
@Component({ selector: 'app-popover-basic', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-button label="Filters" variant="outlined" commandfor="task-filters" command="toggle-popover" ></gui-button> <gui-popover> <div id="task-filters" popover> <gui-checkbox label="Assigned to me" value></gui-checkbox> <gui-checkbox label="Due this week"></gui-checkbox> <gui-checkbox label="Show completed"></gui-checkbox> </div> </gui-popover> `,})export class PopoverBasicComponent {}<script setup lang="ts">import '@golemui/gui-components/button';import '@golemui/gui-components/checkbox';import '@golemui/gui-components/popover';import '@golemui/gui-components/index.css';</script>
<template> <gui-button label="Filters" variant="outlined" commandfor="task-filters" command="toggle-popover" ></gui-button> <gui-popover> <div id="task-filters" popover> <gui-checkbox label="Assigned to me" :value="true"></gui-checkbox> <gui-checkbox label="Due this week"></gui-checkbox> <gui-checkbox label="Show completed"></gui-checkbox> </div> </gui-popover></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/button';import '@golemui/gui-components/checkbox';import '@golemui/gui-components/popover';import '@golemui/gui-components/index.css';
@customElement('popover-basic')export class PopoverBasic extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-button label="Filters" variant="outlined" commandfor="task-filters" command="toggle-popover" ></gui-button> <gui-popover> <div id="task-filters" popover> <gui-checkbox label="Assigned to me" value></gui-checkbox> <gui-checkbox label="Due this week"></gui-checkbox> <gui-checkbox label="Show completed"></gui-checkbox> </div> </gui-popover> `; }}<gui-button label="Filters" variant="outlined" commandfor="task-filters" command="toggle-popover"></gui-button><gui-popover> <div id="task-filters" popover> <gui-checkbox label="Assigned to me" value></gui-checkbox> <gui-checkbox label="Due this week"></gui-checkbox> <gui-checkbox label="Show completed"></gui-checkbox> </div></gui-popover>The element’s entry point is @golemui/gui-components/popover, which registers <gui-popover>
and nothing else. To install the package and load the stylesheet, see
Installation.
The @golemui/gui-components/react components have typed props and an on* prop for each event. React 18 or later.
import { GuiPopover } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function PopoverExample() { return ( <GuiPopover placement="bottom-start" > <div id="task-filters" popover="auto">…</div> </GuiPopover> );}Add CUSTOM_ELEMENTS_SCHEMA to the component that uses the tags, and the stylesheet to styles in angular.json.
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/popover';
@Component({ selector: 'app-popover-example', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-popover placement="bottom-start" > <div id="task-filters" popover="auto">…</div> </gui-popover> `,})export class PopoverExampleComponent {}"styles": ["node_modules/@golemui/gui-components/lib/styles/index.css"]Tell Vue's compiler that gui-* tags are custom elements, and add the typings once for typed props and @gui-* handlers.
<script setup lang="ts">import '@golemui/gui-components/popover';import '@golemui/gui-components/index.css';</script>
<template> <gui-popover placement="bottom-start" > <div id="task-filters" popover="auto">…</div> </gui-popover></template>vue({ template: { compilerOptions: { isCustomElement: (tag) => tag.startsWith('gui-') } },});import type {} from '@golemui/gui-components/vue';The page's stylesheet styles the gui-* elements, so it only reaches them outside a shadow root. Render the host component into the light DOM too, as below, or load the stylesheet into its shadow root.
import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/popover';import '@golemui/gui-components/index.css';
@customElement('popover-example')export class PopoverExample extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-popover placement="bottom-start" > <div id="task-filters" popover="auto">…</div> </gui-popover> `; }}With any bundler, such as Vite. Import the element once, anywhere, and use the tag in your markup.
<gui-popover placement="bottom-start"> <div id="task-filters" popover="auto">…</div></gui-popover>
<script type="module" src="./main.js"></script>import '@golemui/gui-components/popover';import '@golemui/gui-components/index.css';The panel is the element’s child with the popover attribute, and it needs an id. A button
opens and closes it, with no script:
- A native
<button>whosepopovertargetnames that id. In React, the attribute is thepopoverTargetprop. - A
gui-buttonwithcommandfor, the panel’s id, andcommand="toggle-popover", as in the examples below. In React, they are thecommandForandcommandprops ofGuiButton.
The element adds nothing to your markup: the panel, its popover attribute and the button’s
popovertarget or commandfor are yours, so a server-rendered page shows the same markup before and after the
element upgrades. A closed panel stays hidden until then.
Properties
| Property | Type | Default | Description |
|---|---|---|---|
placement | GuiPlacement | 'bottom-start' | Where the panel sits next to its anchor. Start and end follow the reading direction. |
anchor | string | – | The id of the element to anchor the panel to, when it is not the panel's invoker. |
open | boolean | – | Whether the panel is open. Without a value it takes the panel's own state. Setting it opens or closes the panel and fires no gui-toggle. |
panel read-only, no attribute | HTMLElement | null | – | The app's panel: the element's [popover] child. |
Events
Every event bubbles and is composed, so you can listen on any ancestor.
| Event | Description |
|---|---|
gui-toggle | The user opened or closed the panel. detail.open is the new state. |
Examples
Section titled “Examples”Placement
Section titled “Placement”placement sets the side of the button the panel opens on: top, bottom, start or end,
centered on the button. Add -start or -end to line the panel up with the start or end edge
of the button instead, as in bottom-start, the default. Start and end follow the reading
direction, so they swap sides in a right-to-left page.
When the panel doesn’t fit on that side of the viewport, it opens on the other side, or with the other alignment. The menu and the tooltip take the same values.
import { GuiButton, GuiPopover } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function PopoverPlacement() { return ( <div className="gui-grid gui-grid--row gui-grid--justify-start"> <div className="gui-grid__cell"> <GuiButton label="Top" variant="outlined" commandFor="placement-top" command="toggle-popover" /> <GuiPopover placement="top"> <div id="placement-top" popover="auto"> Above the button, centered on it. </div> </GuiPopover> </div> <div className="gui-grid__cell"> <GuiButton label="End" variant="outlined" commandFor="placement-end" command="toggle-popover" /> <GuiPopover placement="end"> <div id="placement-end" popover="auto"> After the button, in the reading direction. </div> </GuiPopover> </div> <div className="gui-grid__cell"> <GuiButton label="Bottom end" variant="outlined" commandFor="placement-bottom-end" command="toggle-popover" /> <GuiPopover placement="bottom-end"> <div id="placement-bottom-end" popover="auto"> Below, lined up with the end of the button. </div> </GuiPopover> </div> </div> );}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/button';import '@golemui/gui-components/popover';
@Component({ selector: 'app-popover-placement', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <div class="gui-grid gui-grid--row gui-grid--justify-start"> <div class="gui-grid__cell"> <gui-button label="Top" variant="outlined" commandfor="placement-top" command="toggle-popover" ></gui-button> <gui-popover placement="top"> <div id="placement-top" popover>Above the button, centered on it.</div> </gui-popover> </div> <div class="gui-grid__cell"> <gui-button label="End" variant="outlined" commandfor="placement-end" command="toggle-popover" ></gui-button> <gui-popover placement="end"> <div id="placement-end" popover>After the button, in the reading direction.</div> </gui-popover> </div> <div class="gui-grid__cell"> <gui-button label="Bottom end" variant="outlined" commandfor="placement-bottom-end" command="toggle-popover" ></gui-button> <gui-popover placement="bottom-end"> <div id="placement-bottom-end" popover>Below, lined up with the end of the button.</div> </gui-popover> </div> </div> `,})export class PopoverPlacementComponent {}<script setup lang="ts">import '@golemui/gui-components/button';import '@golemui/gui-components/popover';import '@golemui/gui-components/index.css';</script>
<template> <div class="gui-grid gui-grid--row gui-grid--justify-start"> <div class="gui-grid__cell"> <gui-button label="Top" variant="outlined" commandfor="placement-top" command="toggle-popover" ></gui-button> <gui-popover placement="top"> <div id="placement-top" popover>Above the button, centered on it.</div> </gui-popover> </div> <div class="gui-grid__cell"> <gui-button label="End" variant="outlined" commandfor="placement-end" command="toggle-popover" ></gui-button> <gui-popover placement="end"> <div id="placement-end" popover>After the button, in the reading direction.</div> </gui-popover> </div> <div class="gui-grid__cell"> <gui-button label="Bottom end" variant="outlined" commandfor="placement-bottom-end" command="toggle-popover" ></gui-button> <gui-popover placement="bottom-end"> <div id="placement-bottom-end" popover>Below, lined up with the end of the button.</div> </gui-popover> </div> </div></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/button';import '@golemui/gui-components/popover';import '@golemui/gui-components/index.css';
@customElement('popover-placement')export class PopoverPlacement extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <div class="gui-grid gui-grid--row gui-grid--justify-start"> <div class="gui-grid__cell"> <gui-button label="Top" variant="outlined" commandfor="placement-top" command="toggle-popover" ></gui-button> <gui-popover placement="top"> <div id="placement-top" popover>Above the button, centered on it.</div> </gui-popover> </div> <div class="gui-grid__cell"> <gui-button label="End" variant="outlined" commandfor="placement-end" command="toggle-popover" ></gui-button> <gui-popover placement="end"> <div id="placement-end" popover>After the button, in the reading direction.</div> </gui-popover> </div> <div class="gui-grid__cell"> <gui-button label="Bottom end" variant="outlined" commandfor="placement-bottom-end" command="toggle-popover" ></gui-button> <gui-popover placement="bottom-end"> <div id="placement-bottom-end" popover>Below, lined up with the end of the button.</div> </gui-popover> </div> </div> `; }}<div class="gui-grid gui-grid--row gui-grid--justify-start"> <div class="gui-grid__cell"> <gui-button label="Top" variant="outlined" commandfor="placement-top" command="toggle-popover" ></gui-button> <gui-popover placement="top"> <div id="placement-top" popover>Above the button, centered on it.</div> </gui-popover> </div> <div class="gui-grid__cell"> <gui-button label="End" variant="outlined" commandfor="placement-end" command="toggle-popover" ></gui-button> <gui-popover placement="end"> <div id="placement-end" popover>After the button, in the reading direction.</div> </gui-popover> </div> <div class="gui-grid__cell"> <gui-button label="Bottom end" variant="outlined" commandfor="placement-bottom-end" command="toggle-popover" ></gui-button> <gui-popover placement="bottom-end"> <div id="placement-bottom-end" popover>Below, lined up with the end of the button.</div> </gui-popover> </div></div>| Placement | Where the panel opens |
|---|---|
top, top-start, top-end | Above the button. |
bottom, bottom-start, bottom-end | Below the button. bottom-start is the default. |
start, start-start, start-end | Before the button, in the reading direction. |
end, end-start, end-end | After the button, in the reading direction. |
Reacting to opening and closing
Section titled “Reacting to opening and closing”When the user opens or closes the panel, the element fires gui-toggle with the new state in
event.detail.open. Use it to load the panel’s content when it opens, or to save what the user
changed in it when it closes.
Ada Lovelace, owner
Grace Hopper, editor
The member list is closed.
import { useState } from 'react';import { GuiButton, GuiPopover } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function PopoverEvents() { const [open, setOpen] = useState(false);
return ( <> <GuiButton label="Members" variant="outlined" commandFor="board-members" command="toggle-popover" /> <GuiPopover id="members-popover" onGuiToggle={(event) => setOpen(event.detail.open)} > <div id="board-members" popover="auto"> <p>Ada Lovelace, owner</p> <p>Grace Hopper, editor</p> </div> </GuiPopover> <p id="members-status" role="status"> The member list is {open ? 'open' : 'closed'}. </p> </> );}import { Component, CUSTOM_ELEMENTS_SCHEMA, signal } from '@angular/core';import type { GuiEventMap, GuiPopoverEvents } from '@golemui/gui-components';import '@golemui/gui-components/button';import '@golemui/gui-components/popover';
type PopoverEvent<K extends keyof typeof GuiPopoverEvents> = GuiEventMap< typeof GuiPopoverEvents>[K];
@Component({ selector: 'app-popover-events', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-button label="Members" variant="outlined" commandfor="board-members" command="toggle-popover" ></gui-button> <gui-popover id="members-popover" (gui-toggle)="onToggle($event)"> <div id="board-members" popover> <p>Ada Lovelace, owner</p> <p>Grace Hopper, editor</p> </div> </gui-popover> <p id="members-status" role="status"> The member list is {{ open() ? 'open' : 'closed' }}. </p> `,})export class PopoverEventsComponent { open = signal(false);
onToggle(event: Event) { this.open.set((event as PopoverEvent<'gui-toggle'>).detail.open); }}<script setup lang="ts">import { ref } from 'vue';import '@golemui/gui-components/button';import '@golemui/gui-components/popover';import '@golemui/gui-components/index.css';
const open = ref(false);</script>
<template> <gui-button label="Members" variant="outlined" commandfor="board-members" command="toggle-popover" ></gui-button> <gui-popover id="members-popover" @gui-toggle="open = $event.detail.open"> <div id="board-members" popover> <p>Ada Lovelace, owner</p> <p>Grace Hopper, editor</p> </div> </gui-popover> <p id="members-status" role="status"> The member list is {{ open ? 'open' : 'closed' }}. </p></template>import { LitElement, html } from 'lit';import { customElement, state } from 'lit/decorators.js';import type { GuiEventMap, GuiPopoverEvents } from '@golemui/gui-components';import '@golemui/gui-components/button';import '@golemui/gui-components/popover';import '@golemui/gui-components/index.css';
type PopoverEvent<K extends keyof typeof GuiPopoverEvents> = GuiEventMap< typeof GuiPopoverEvents>[K];
@customElement('popover-events')export class PopoverEvents extends LitElement { @state() private open = false;
protected override createRenderRoot() { return this; }
private onToggle(event: PopoverEvent<'gui-toggle'>) { this.open = event.detail.open; }
override render() { return html` <gui-button label="Members" variant="outlined" commandfor="board-members" command="toggle-popover" ></gui-button> <gui-popover id="members-popover" @gui-toggle=${this.onToggle}> <div id="board-members" popover> <p>Ada Lovelace, owner</p> <p>Grace Hopper, editor</p> </div> </gui-popover> <p id="members-status" role="status"> The member list is ${this.open ? 'open' : 'closed'}. </p> `; }}<gui-button label="Members" variant="outlined" commandfor="board-members" command="toggle-popover"></gui-button><gui-popover id="members-popover"> <div id="board-members" popover> <p>Ada Lovelace, owner</p> <p>Grace Hopper, editor</p> </div></gui-popover><p id="members-status" role="status">The member list is closed.</p>
<script type="module"> const members = document.querySelector('#members-popover');
members.addEventListener('gui-toggle', (event) => { const state = event.detail.open ? 'open' : 'closed'; document.querySelector('#members-status').textContent = `The member list is ${state}.`; });</script>Opening from your code
Section titled “Opening from your code”Set open to open or close the panel yourself, such as from a click handler or after a step of a
tour. Setting it fires no gui-toggle. When the user opens or closes the panel, open follows,
so it always tells the panel’s state. In a framework that holds open in its state, update that
state from gui-toggle, or the next time you set it to true nothing changes.
anchor takes the id of the element to place the panel next to. Without it, the panel opens next
to the button that opened it, or the first button whose popovertarget or commandfor names it. A panel with
nothing to anchor to opens in the middle of the viewport, and the element logs a warning in
development.
The panel below has popover="manual": Escape and a click outside don’t close it, so
it waits until the user presses “Got it”. A panel with the plain popover attribute closes on
both, and closes any other such panel that is open, except the one it opened from.
A panel you open from your code has no button for the browser to return the focus to. When it closes with the focus inside, the element moves the focus to its anchor, where the user can open it again, unless a click outside the panel closed it: the user is pointing somewhere else. Make the anchor an element that takes the focus, such as the native button in this example.
Drag a card to another column to change its status.
import { useState } from 'react';import { GuiButton, GuiPopover } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function PopoverFromCode() { const [open, setOpen] = useState(false);
return ( <> <button type="button" id="board-help" className="gui-button gui-button--outlined" onClick={() => setOpen(true)} > Show me how </button> <GuiPopover id="board-tip" anchor="board-help" open={open} onGuiToggle={(event) => setOpen(event.detail.open)} > <div id="board-tip-panel" popover="manual"> <div className="gui-grid gui-grid--gap-sm"> <p>Drag a card to another column to change its status.</p> <GuiButton id="board-tip-close" label="Got it" size="sm" onClick={() => setOpen(false)} /> </div> </div> </GuiPopover> </> );}import { Component, CUSTOM_ELEMENTS_SCHEMA, signal } from '@angular/core';import type { GuiEventMap, GuiPopoverEvents } from '@golemui/gui-components';import '@golemui/gui-components/button';import '@golemui/gui-components/popover';
type PopoverEvent<K extends keyof typeof GuiPopoverEvents> = GuiEventMap< typeof GuiPopoverEvents>[K];
@Component({ selector: 'app-popover-from-code', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <button type="button" id="board-help" class="gui-button gui-button--outlined" (click)="open.set(true)" > Show me how </button> <gui-popover id="board-tip" anchor="board-help" [open]="open()" (gui-toggle)="onToggle($event)" > <div id="board-tip-panel" popover="manual"> <div class="gui-grid gui-grid--gap-sm"> <p>Drag a card to another column to change its status.</p> <gui-button id="board-tip-close" label="Got it" size="sm" (click)="open.set(false)" ></gui-button> </div> </div> </gui-popover> `,})export class PopoverFromCodeComponent { open = signal(false);
onToggle(event: Event) { this.open.set((event as PopoverEvent<'gui-toggle'>).detail.open); }}<script setup lang="ts">import { ref } from 'vue';import '@golemui/gui-components/button';import '@golemui/gui-components/popover';import '@golemui/gui-components/index.css';
const open = ref(false);</script>
<template> <button type="button" id="board-help" class="gui-button gui-button--outlined" @click="open = true" > Show me how </button> <gui-popover id="board-tip" anchor="board-help" :open="open" @gui-toggle="open = $event.detail.open" > <div id="board-tip-panel" popover="manual"> <div class="gui-grid gui-grid--gap-sm"> <p>Drag a card to another column to change its status.</p> <gui-button id="board-tip-close" label="Got it" size="sm" @click="open = false" ></gui-button> </div> </div> </gui-popover></template>import { LitElement, html } from 'lit';import { customElement, state } from 'lit/decorators.js';import type { GuiEventMap, GuiPopoverEvents } from '@golemui/gui-components';import '@golemui/gui-components/button';import '@golemui/gui-components/popover';import '@golemui/gui-components/index.css';
type PopoverEvent<K extends keyof typeof GuiPopoverEvents> = GuiEventMap< typeof GuiPopoverEvents>[K];
@customElement('popover-from-code')export class PopoverFromCode extends LitElement { @state() private open = false;
protected override createRenderRoot() { return this; }
private onToggle(event: PopoverEvent<'gui-toggle'>) { this.open = event.detail.open; }
override render() { return html` <button type="button" id="board-help" class="gui-button gui-button--outlined" @click=${() => (this.open = true)} > Show me how </button> <gui-popover id="board-tip" anchor="board-help" .open=${this.open} @gui-toggle=${this.onToggle} > <div id="board-tip-panel" popover="manual"> <div class="gui-grid gui-grid--gap-sm"> <p>Drag a card to another column to change its status.</p> <gui-button id="board-tip-close" label="Got it" size="sm" @click=${() => (this.open = false)} ></gui-button> </div> </div> </gui-popover> `; }}<button type="button" id="board-help" class="gui-button gui-button--outlined"> Show me how</button><gui-popover id="board-tip" anchor="board-help"> <div id="board-tip-panel" popover="manual"> <div class="gui-grid gui-grid--gap-sm"> <p>Drag a card to another column to change its status.</p> <gui-button id="board-tip-close" label="Got it" size="sm"></gui-button> </div> </div></gui-popover>
<script type="module"> const tip = document.querySelector('#board-tip');
document.querySelector('#board-help').addEventListener('click', () => { tip.open = true; }); document.querySelector('#board-tip-close').addEventListener('click', () => { tip.open = false; });</script>Styling
Section titled “Styling”Anatomy
Section titled “Anatomy”<gui-popover> renders nothing of its own and has no box of its own (display: contents). It
places your panel and the stylesheet draws it as a card:
<button type="button" popovertarget="task-filters">Filters</button><gui-popover placement="bottom-start" open> <div id="task-filters" popover> <!-- Your content. --> </div></gui-popover>While the panel is open, the element sets its place as inline styles, through the CSSOM: anchor
positioning properties where the browser supports them, with an inline anchor-name on the
button, or a measured position elsewhere. Leave those properties alone. The element reflects
open as an attribute, so your CSS can select gui-popover[open]. See
Floating panels for how the panels of the
GolemUI Components open above the page.
Select the panel as gui-popover > [popover], or by a class of your own:
/* A wider panel with more room, for the popovers of the board toolbar. */.board-toolbar gui-popover > [popover] { inline-size: 20rem; padding: var(--gui-space-4);}The panel is never wider or taller than the viewport, less a small gap: longer content scrolls inside it.
Tokens
Section titled “Tokens”The panel reads these design tokens. Set them on :root to change every popover, or on any
element to change the popovers inside it:
| Token | What it styles |
|---|---|
--gui-bg-default | The background. |
--gui-border-default | The border. |
--gui-radius-md | The corner radius. |
--gui-shadow-md | The shadow. |
--gui-text-default | The text. |
--gui-space-3 | The padding. |
--gui-overlay-duration | How long the panel takes to fade in and out. Reduced motion skips it. |
To restyle beyond the tokens, see Overriding styles; for dark mode and the Clay theme, see Themes and dark mode.
Accessibility
Section titled “Accessibility”- Focus. Opening the panel leaves the focus on the button, and Tab from the button moves into the panel. Closing the panel with Escape while the focus is in it returns the focus to the button. A panel you open from your code returns it to its anchor: see Opening from your code.
- State. The browser tells assistive technology whether the panel of a
popovertargetorcommandforbutton is expanded or collapsed. - Role. A popover has no role of its own. Give the panel the role and the name of what it
holds, such as
role="dialog"and anaria-labelfor a panel of settings. For a list of actions usegui-menu, and for a short description of a controlgui-tooltip. - Not modal. The rest of the page stays usable while the panel is open. For content the user
must deal with first, use
gui-dialog.
| Key | Action |
|---|---|
| Enter Space | On the button: opens the panel, or closes it. |
| Tab | From the button: moves into the panel. From its end: moves on. |
| Escape | Closes the panel and returns the focus to the button. |
What every element shares is in Accessibility.