# Popover

> Anchored floating panel opened by a click on a trigger, holding rich content such as text, forms or actions. Wraps Base UI Popover.

Source: https://docs.nasaqui.com/components/popover

## Install

```bash
npx shadcn@latest add https://docs.nasaqui.com/r/popover.json
```

A small panel that opens next to a trigger when it is clicked. It holds arbitrary content (an explanation, a
mini form, a set of actions) without leaving the page. Built on Base UI `Popover`, so it handles positioning,
collision flipping, focus and dismissal.

## When to use

- Extra detail or a small form attached to one control.
- Content that needs to be interactive, so a tooltip is not enough.

## When not to use

- A list of actions: use a [`DropdownMenu`](https://docs.nasaqui.com/components/dropdown-menu).
- A one-line hint on hover or focus: use a [`Tooltip`](https://docs.nasaqui.com/components/tooltip).
- A preview shown on hover: use a [`HoverCard`](https://docs.nasaqui.com/components/hover-card).
- Blocking decisions or long forms: use a [`Dialog`](https://docs.nasaqui.com/components/dialog).

## Import

```tsx
import { Popover, PopoverTrigger, PopoverContent } from "@fadymondy/nasaq/web";
// inside this monorepo: "@nasaq/web"
```

## Quick start

```tsx
import { Button, Popover, PopoverClose, PopoverContent, PopoverDescription, PopoverTitle, PopoverTrigger } from "@fadymondy/nasaq/web";

export function DeployInfo() {
  return (
    <Popover>
      <PopoverTrigger render={<Button variant="outline" />}>Details</PopoverTrigger>
      <PopoverContent>
        <PopoverTitle>Deployment window</PopoverTitle>
        <PopoverDescription>Releases go out Sunday to Thursday.</PopoverDescription>
        <PopoverClose render={<Button size="sm" />}>Got it</PopoverClose>
      </PopoverContent>
    </Popover>
  );
}
```

## Anatomy

```
Popover                     Base UI Popover.Root
├─ PopoverTrigger           Base UI Popover.Trigger (use render={<Button />})
└─ PopoverContent           Portal + positioner + popup   data-slot="popover-content"
   ├─ PopoverTitle          data-slot="popover-title"
   ├─ PopoverDescription    data-slot="popover-description"
   └─ PopoverClose          Base UI Popover.Close
```

## API

`Popover` = `Popover.Root`, `PopoverTrigger` = `Popover.Trigger`, `PopoverClose` = `Popover.Close`. They take the Base UI props.

| Export | Common props |
| --- | --- |
| `Popover` | `open?`, `defaultOpen?`, `onOpenChange?`, `modal?` |
| `PopoverTrigger` | `render?`, `openOnHover?`, `delay?`, `disabled?` |
| `PopoverClose` | `render?` |

### `PopoverContent`

`PopoverContentProps` extends Base UI `Popover.Popup` props.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `side?` | `Popover.Positioner` `side` (`"top" \| "bottom" \| "left" \| "right" \| "inline-start" \| "inline-end"`) | `"bottom"` | Side of the trigger to open on. |
| `align?` | `"start" \| "center" \| "end"` | `"center"` | Alignment against the trigger. |
| `sideOffset?` | `number` | `6` | Gap to the trigger in px. |
| `className?` | `string` | none | Merged onto the popup (width is `w-72` by default). |

### `PopoverTitle` / `PopoverDescription`

Base UI `Popover.Title` / `Popover.Description` props. They label the popup for assistive tech (`aria-labelledby`, `aria-describedby`).

## Examples

### Controlled

```tsx
import { Button, Popover, PopoverContent, PopoverTrigger } from "@fadymondy/nasaq/web";
import { useState } from "react";

export function Controlled() {
  const [open, setOpen] = useState(false);
  return (
    <Popover open={open} onOpenChange={setOpen}>
      <PopoverTrigger render={<Button />}>{open ? "Hide" : "Show"}</PopoverTrigger>
      <PopoverContent side="inline-end" align="start">Opens at the inline end.</PopoverContent>
    </Popover>
  );
}
```

### Arabic

```tsx
import { Button, Popover, PopoverContent, PopoverDescription, PopoverTitle, PopoverTrigger } from "@fadymondy/nasaq/web";

export function DeployInfoAr() {
  return (
    <Popover>
      <PopoverTrigger render={<Button variant="outline" />}>التفاصيل</PopoverTrigger>
      <PopoverContent align="start">
        <PopoverTitle>نافذة النشر</PopoverTitle>
        <PopoverDescription>تُنشر الإصدارات من الأحد إلى الخميس.</PopoverDescription>
      </PopoverContent>
    </Popover>
  );
}
```

## Accessibility

Base UI sets `aria-expanded` and `aria-controls` on the trigger, and the popup is labelled by `PopoverTitle` and described by `PopoverDescription`.

| Key | Action |
| --- | --- |
| `Enter` / `Space` on trigger | Toggles the popover. |
| `Tab` | Moves through the popup content, then on. |
| `Esc` | Closes and returns focus to the trigger. |

- Give an icon-only trigger an `aria-label`.
- Localise the title, description and close text.

## RTL & i18n

- Use `side="inline-start"` / `"inline-end"` so the placement mirrors with `dir`. `align="start"` follows the reading direction.
- The popup inherits direction from the app provider; portalled content outside a `dir` wrapper needs `dir` set on `PopoverContent` in isolated demos.
- No built-in strings.

## Styling & tokens

- Surface level 3: `bg-popover`, `text-popover-foreground`, `border-border`, `rounded-floating`, `shadow-floating`.
- State attributes on the popup: `data-starting-style`, `data-ending-style`, `data-side`, `data-open`.
- Extend with `className`; never override colours with raw hex.

## Do / Don't

- **Do** keep it short; move long content to a [`Dialog`](https://docs.nasaqui.com/components/dialog).
- **Do** give the trigger a visible label or `aria-label`.
- **Don't** put a menu of actions in it; use a `DropdownMenu`.
- **Don't** use `left` / `right` for `side` when `inline-start` / `inline-end` is meant.

## Related

- [DropdownMenu](https://docs.nasaqui.com/components/dropdown-menu) · [Tooltip](https://docs.nasaqui.com/components/tooltip) · [HoverCard](https://docs.nasaqui.com/components/hover-card) · [Dialog](https://docs.nasaqui.com/components/dialog)

## Lab

https://docs.nasaqui.com/?path=/docs/components-overlays-popover--docs

## Code

### React

```tsx
import { Button, Popover, PopoverClose, PopoverContent, PopoverDescription, PopoverTitle, PopoverTrigger } from "@fadymondy/nasaq/web";

export function DeployInfo() {
  return (
    <Popover>
      <PopoverTrigger render={<Button variant="outline" />}>Details</PopoverTrigger>
      <PopoverContent>
        <PopoverTitle>Deployment window</PopoverTitle>
        <PopoverDescription>Releases go out Sunday to Thursday.</PopoverDescription>
        <PopoverClose render={<Button size="sm" />}>Got it</PopoverClose>
      </PopoverContent>
    </Popover>
  );
}
```

### shadcn

```tsx
import { Button } from "@/components/ui/button";
import { Popover, PopoverClose, PopoverContent, PopoverDescription, PopoverTitle, PopoverTrigger } from "@/components/ui/popover";

export function DeployInfo() {
  return (
    <Popover>
      <PopoverTrigger render={<Button variant="outline" />}>Details</PopoverTrigger>
      <PopoverContent>
        <PopoverTitle>Deployment window</PopoverTitle>
        <PopoverDescription>Releases go out Sunday to Thursday.</PopoverDescription>
        <PopoverClose render={<Button size="sm" />}>Got it</PopoverClose>
      </PopoverContent>
    </Popover>
  );
}
```

### Vue

```vue
<script setup lang="ts">
import {
  NqButton, NqPopover, NqPopoverClose, NqPopoverContent, NqPopoverDescription, NqPopoverTitle, NqPopoverTrigger,
} from "@fadymondy/nasaq/vue";
</script>

<template>
  <NqPopover>
    <NqPopoverTrigger as-child><NqButton variant="outline">Details</NqButton></NqPopoverTrigger>
    <NqPopoverContent>
      <NqPopoverTitle>Deployment window</NqPopoverTitle>
      <NqPopoverDescription>Releases go out Sunday to Thursday.</NqPopoverDescription>
      <NqPopoverClose as-child><NqButton size="sm">Got it</NqButton></NqPopoverClose>
    </NqPopoverContent>
  </NqPopover>
</template>
```

### Blade

```blade
<x-nq::popover>
    <x-nq::popover.trigger variant="outline">Details</x-nq::popover.trigger>
    <x-nq::popover.content>
        <x-nq::popover.title>Deployment window</x-nq::popover.title>
        <x-nq::popover.description>Releases go out Sunday to Thursday.</x-nq::popover.description>
        <x-nq::popover.close size="sm" class="mt-3">Got it</x-nq::popover.close>
    </x-nq::popover.content>
</x-nq::popover>
```

### HTML + Alpine

```html
<div data-slot="popover" x-data="nqPopover(false)" x-modelable="open" x-id="['nq-popover']" class="contents">
    <button data-slot="popover-trigger"
     type="button"                         x-bind:data-popup-open="open ? &#039;&#039; : undefined" x-ref="trigger" aria-haspopup="dialog" x-on:click="toggle()" :aria-expanded="open" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border-border bg-card text-foreground hover:bg-nq-hover h-control px-[var(--nq-control-pad)]">
        Details</button>
    <template x-teleport="body">
    <div data-slot="popover-content" x-bind="popup" x-init="popupEl = $el" x-nq-presence="open" x-anchor.bottom.offset.6="$refs.trigger"
        class="z-50 w-72 max-w-[var(--available-width)] rounded-floating border border-border bg-popover p-4 text-body-sm text-popover-foreground shadow-floating outline-none transition-opacity duration-150 ease-nq data-starting-style:opacity-0 data-ending-style:opacity-0"><h2 data-slot="popover-title" :id="$id('nq-popover', 'title')" class="text-body-sm font-semibold text-foreground">Deployment window</h2>
        <p data-slot="popover-description" :id="$id('nq-popover', 'description')" class="mt-1 text-body-sm text-muted-foreground">Releases go out Sunday to Thursday.</p>
        <button data-slot="popover-close"
     type="button"                         x-on:click="close()" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border-border bg-card text-foreground hover:bg-nq-hover h-control-sm px-2.5 mt-3">
        Got it</button></div>
</template>
</div>
```
