Popover
A small panel that opens from a button and stays out of the way.
Preview
import { Popover } from '@kerf-js/veneer'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 @kerf-js/veneerTry it
Change the controls: the preview and its code update together. The address keeps what you set, so you can link to it.
import { Popover } from '@kerf-js/veneer'<Popover label="Details" title="storefront" description="Deployed 2 minutes ago by Priya." />Usage
import { Popover } from '@kerf-js/veneer'<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.
import { Popover } from '@kerf-js/veneer'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.
import { Popover } from '@kerf-js/veneer'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.
import { Popover } from '@kerf-js/veneer'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.
import { state } from '@kerf-js/core'import { Button, Popover, Row, Stack, Text} from '@kerf-js/veneer'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.
import { state } from '@kerf-js/core'import { Chip, Popover, Row, Text } from '@kerf-js/veneer'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> )}const STATUSES = ['Live', 'Building', 'Failed']When to use
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.
Theming
Restyle it app-wide from veneer() in your kerf config. Every key takes colour, spacing, size, radius, shadow, type and motion; its own keys are listed. A state nests inside: hover: { bg: 'ink' }. Text on a fill you set is contrast-checked at build. How theming works
| Key | Own keys | States |
|---|---|---|
parts.popover | — | none |
veneer({ colors: { brand: '#e8590c', ink: '#1b1b1f' }, parts: { popover: { radius: 12, shadow: 'lg' }, },})API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
label | unknown | — | The button's text. |
title | unknown | — | A heading in the panel. Also names it. |
description | unknown | — | A line under the title. |
side | Side | — | Which side of the button it opens on. |
align | Align | — | How it lines up with the button. |
trigger | ButtonProps | — | Button props for the trigger. |
inline | boolean | false | Draws 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. |
children | unknown | — | 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