Skip to content
weftGitHub

Popover

A small panel that opens from a button and stays out of the way.

SourcePackage @weft-js/warpPlayground

Preview

tsx
import { Popover } from '@weft-js/warp'export function Basic() {  return (    <Popover label="What is a preview?" title="Preview deploys" description="Every branch gets its own address, so you can try a change before it goes live." />  )}

Installation

npm install @weft-js/warp

Usage

tsx
import { Popover } from '@weft-js/warp'<Popover label="What is a preview?" title="Preview deploys" description="Every branch gets its own address, so you can try a change before it goes live." />

Placement

Use side to pick where it opens. With no room there, it flips.

tsx
import { Popover } from '@weft-js/warp'export function Sides() {  return (    <>      <Popover label="Top" side="top" title="Top" description="Opens above the button." />      <Popover label="Bottom" side="bottom" title="Bottom" description="Opens below the button." />      <Popover label="Left" side="left" title="Left" description="Opens before the button." />      <Popover label="Right" side="right" title="Right" description="Opens after the button." />    </>  )}

Alignment

Use align to line it up with the button.

tsx
import { Popover } from '@weft-js/warp'export function Aligns() {  return (    <>      <Popover label="Start" align="start" title="Start" description="Lines up with the left edge. The default." />      <Popover label="Center" align="center" title="Center" description="Sits in the middle." />      <Popover label="End" align="end" title="End" description="Lines up with the right edge." />    </>  )}

Trigger styles

trigger takes any Button prop.

tsx
import { Popover } from '@weft-js/warp'export function Trigger() {  return (    <>      <Popover label="Soft" trigger={{ emphasis: 'soft' }} title="Soft" />      <Popover label="With an icon" trigger={{ icon: 'info', emphasis: 'ghost', tone: 'neutral' }} title="Ghost" />      <Popover label="Small" trigger={{ size: 'sm' }} title="Small" />    </>  )}

Closing from inside

Anything with data-close-popover closes it.

tsx
import { state } from '@weft-js/core'import { Button, Popover, Row, Stack, Text } from '@weft-js/warp'export function Close() {  const on = state('Nobody is on call')  return (    <Stack gap="related" align="center">      <Text subtle>{on.value}</Text>      <Popover label="Take the pager" title="Go on call?" description="Alerts for every project will come to you tonight.">        <Row gap="related" justify="end">          <Button size="sm" emphasis="ghost" tone="neutral" data-close-popover="">Not now</Button>          <Button size="sm" data-close-popover="" onClick={() => (on.value = 'You are on call')}>Go on call</Button>        </Row>      </Popover>    </Stack>  )}

A filter

A popover opens a small panel from its trigger, here a set of filter chips.

tsx
import { state } from '@weft-js/core'import { Chip, Popover, Row, Text } from '@weft-js/warp'export function Filter() {  const on = state(['Failed'])  const flip = (s: string) => (on.value = on.value.includes(s) ? on.value.filter(x => x !== s) : [...on.value, s])  return (    <Row gap="related">      <Popover label={`Status (${on.value.length})`} trigger={{ icon: 'filter' }} title="Show deploys that are">        <Row gap="tight">          {STATUSES.map(s => <Chip key={s} pressed={on.value.includes(s)} onClick={() => flip(s)}>{s}</Chip>)}        </Row>      </Popover>      <Text subtle>{on.value.join(', ') || 'Everything'}</Text>    </Row>  )}type Status = 'Live' | 'Building' | 'Failed' | 'Paused'const STATUSES = ['Live', 'Building', 'Failed']

When to use

Use it for

  • A few controls or a short note, next to what they affect
  • People may carry on with the page while it is open

Reach for something else

  • Tooltip, when it is a hint with nothing to press
  • Menu, when it is a list of actions
  • Modal, when it must be answered first

Playground

Side
Align
import { Popover } from '@weft-js/warp'<Popover label="Details" title="storefront" description="Deployed 2 minutes ago by Priya." />

Accessibility

Enter
Opens it and moves focus inside
Esc
Closes and returns focus to the button
Tab
Leaving the panel closes it
  • It is a non-modal dialog, named by its title, else by its button.
  • The button reports whether it is open.

API Reference

PropTypeDefaultDescription
labelunknown—The button's text.
titleunknown—A heading in the panel. Also names it.
descriptionunknown—A line under the title.
sideSide—Which side of the button it opens on.
alignAlign—How it lines up with the button.
triggerButtonProps—Button props for the trigger.
inlinebooleanfalseDraws the panel open under its trigger, in the page's flow: no top layer or light dismiss. The trigger still shows and hides it. For docs and previews.
childrenunknown—What it contains.

Parts

Every element carries a stable name, so you can style it from your own CSS: [data-scope="popover"] [data-part="content"]

contenttitledescription

RelatedTooltipMenuModal