Theme
Sound
Jump to a component
Core · Containers
Popover
Popover is a raised surface that floats over the page: a menu, a hover card, a small form.
Live, from @anicanon/sakuga 2.0.0
Sakuga for iOS and Android is coming
iOS
Web only
Popover is made for the web, and isn’t part of Sakuga for iOS yet.
Android
Web only
Popover is made for the web, and isn’t part of Sakuga for Android yet.
- Use it for
- A raised surface anchored to what opened it: a menu, a hover card on a mention, a small form.
Guidelines
anchored(default) pins it under its field: wrap the field in a relative container and put the Popover inside. Passanchored={false}and astylewith a position to place it yourself, as for a hover card on a mention.- Content is yours. For a list of choices use
Picker, which is a Popover with search and options. - One Popover open at a time. Escape, a click outside and Tab out of it close it.
- With sound on, it plays the open sound as it arrives and the close sound as it leaves (README · Sound).
- It drops from its field on
motion-fastwithease-enter.open={false}plays its exit (motion-instant,ease-exit) and then removes it.
onClose: with it the popover takes focus while open, Escape or a press outside runs it, and focus goes back to what opened it. Without it the popover never moves focus (a hover card).onKeyDowngets the keys pressed inside it, before its own Escape.
import { useState } from 'react';
import { Button, Popover } from '@anicanon/sakuga/ui';
export default function PopoverExample() {
const [open, setOpen] = useState(true);
return (
<>
<Button
variant="secondary"
size="sm"
aria-expanded={open ? 'true' : 'false'}
onClick={() => setOpen(!open)}
>
The Professor
</Button>
<Popover
open={open}
label="The Professor"
style={{ gap: 10, padding: 14 }}
>
<b>The Professor</b>
<span style={{ color: 'var(--text-secondary)', fontSize: 13 }}>
Held in London since Arthur taken.
</span>
<Button variant="ghost" size="sm" icon="bookmark">
Open in Story
</Button>
</Popover>
</>
);
}PropsPopoverProps
openbooleanDefault true.false plays its exit, then it unmounts.
onKeyDownKeyboardEventHandlerKeys pressed inside it, before its own Escape.
onClose() => voidWith it, the popover takes focus while open; Escape or a press outside runs it, and focus goes back.
anchoredbooleanalign'start' | 'end'Anchored with its own width, lined up with the anchor's start or end edge (default: the anchor's width).
labelstringrolestringstyleCSSPropertieschildrenReactNodeclassNamestringEach token reads a role, and the role lands on a value in each theme. Change the role and every component that reads it follows.
Colour
popover-bgPopover and Picker: surface.bg-surface-raisedneutral-100#ececf0bg-surface-raisedneutral-875#1a1a21popover-borderPopover and Picker: outline.border-dividershade-a12rgba(11,10,16,0.12)border-dividerwhite-a12rgba(255,255,255,0.12)Space and shape
popover-gapgap-xs4px
popover-paddinginset-sm8px
popover-radiusradius-md12px
popover-ring-widthline-hairline1px
popover-aligned-max-widthits own value
calc(100vw - 2 * 16px)
popover-anchored-topinset-xs4px
popover-forced-edge-widthline-hairline1px
Motion
popover-drop-in-animationmotion-drop-insk-drop-in 160ms cubic-bezier(0, 0, 0, 1)
popover-leaving-drop-out-animationmotion-drop-outsk-drop-out 100ms cubic-bezier(0.3, 0, 1, 1)
Checked automatically with axe-core 4.11.4 against WCAG 2.2 A and AA, on the live preview above, in light and dark, on 26 October 2026.
- Result
- No issues found, in light or in dark.
- By design
- A dialog named by
label(rolechanges the role). WithonCloseit takes focus while open (its first control, else itself), and a press on its words keeps focus inside; Escape, a press outside or Tab out of it runsonClose, and after Escape focus goes back to what opened it; withoutonCloseit never moves focus. - Not tested
- The automated check doesn't test keyboard use, focus order or what a screen reader says. The lines above are the design system's own account of them.