Popover
Displays rich content in a portal, triggered by a button.
Dimensions
Set the dimensions for the layer.
Popover est un panel flottant ancré à un trigger, qui s'ouvre au clic. Contrairement à un Dialog (modal centré, backdrop bloquant), le popover est non-modal : le reste de la page reste interactif et le clic extérieur ferme le popover.
Le popover est la fondation d'un paquet d'autres composants qui héritent son pattern :
- DropdownMenu — popover avec une liste d'items menu.
- HoverCard — s'ouvre au hover au lieu du clic.
- Tooltip — hover + contenu minimal texte.
- Combobox — popover avec search input + liste filtrable.
- DatePicker — popover autour d'un Calendar.
Architecture PRISM : le trigger porte data-ui="toggle-pop" data-target="#popover-id", le content est un <div data-overlay data-light hidden> avec l'id correspondant. Le data-light distingue les popovers ("light") des dialogs ("heavy") — les popovers ne bloquent pas le scroll body, se ferment au clic extérieur, et ne pilent pas l'overflow.
Utiliser pour : filtres avancés, éditeurs inline (couleur, dimensions), détails contextuels, forms courts, aperçus d'entités.
Installation
$ bext ui add popover
Copy and paste the following into src/components/ui/popover.tsx.
This component is interactive — make sure the shared island is loaded (see Islands). It reacts to data-ui="popover" hooks.
Usage
Anatomy
<Popover>
├── <PopoverTrigger for="pop-id"> // data-ui="toggle-pop"
│ └── <Button>Open popover</Button>
└── <PopoverContent id="pop-id"> // data-overlay data-light hidden
└── ...contenu libre... // form, liste, texteAPI Reference
Popover
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | — | Wrapper (relative inline-block). Contient trigger + content. |
PopoverTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
| for | string | — | Match l'id du PopoverContent à ouvrir. Génère data-target="#for". |
| children | ReactNode | — | Élément déclencheur (bouton, lien, icône). Le clic dispatch au toggle. |
PopoverContent
| Prop | Type | Default | Description |
|---|---|---|---|
| id | string | — | Match PopoverTrigger.for. |
| align | "start" | "center" | "end" | "start" | Alignement horizontal du panel par rapport au trigger. start→left-0, center→center, end→right-0. |
| className | string | — | Classes CSS. Défaut : absolute top-full mt-2 w-72 rounded-md border bg-popover p-4 shadow-md. |
Data Attributes
Attributs qu'on peut cibler en CSS custom pour override le style par état.
| Attribute | On | Values | Description |
|---|---|---|---|
| data-ui="toggle-pop" | PopoverTrigger | data-target="#id" | Cliquer ouvre/ferme le popover. |
| data-overlay data-light | PopoverContent | hidden par défaut | L'attribut data-light distingue les popovers des dialogs — l'island les ferme au clic extérieur sans piler le body overflow. |
Keyboard
| Key | Action |
|---|---|
| Tab | Focus le trigger. Une fois popover ouvert, Tab traverse les enfants intérieurs. |
| Enter / Space | Sur trigger fermé : ouvre. Sur bouton intérieur : active. |
| Escape | Ferme le popover ouvert. Focus revient au trigger. |
Accessibility
PopoverTrigger devrait porter aria-haspopup="true" et aria-expanded="true|false". En PRISM ce n'est pas automatique — poser à la main sur le bouton enfant.
PopoverContent devrait porter role="dialog" quand le contenu est complexe (form, liste), role="tooltip" pour un contenu descriptif court, ou aucun rôle pour du texte simple.
Focus : contrairement à Dialog (focus trap), le popover ne trap pas le focus. Tab peut sortir. C'est intentionnel — le popover est non-modal. Si tu veux trap, utilise Dialog.
Utiliser Dialog à la place quand : le contenu est un vrai formulaire critique (2+ champs required), une action doit être bloquante, ou le contenu doit avoir un backdrop.
Examples
Contenu minimal (info)
This is a short piece of contextual information that lives in a popover.
Aligned end (à droite du trigger)
align="end" — le popover s'aligne au bord droit du trigger.
Edit this component live in the bext playground. The PRISM + signals version is the bext-native idiom — fine-grained reactivity, no virtual DOM.
On This Page