# Popover Component Usage
## Overview
The popover component displays rich content in a floating panel triggered by a button click. Unlike tooltips, popovers can contain interactive elements, forms, and complex layouts. Popovers use JavaScript for positioning and toggle behavior.
## JavaScript Requirements
### Required Scripts
```html
<!-- Include Basecoat CSS and JavaScript -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/basecoat-css@latest/dist/basecoat.cdn.min.css">
<script src="https://cdn.jsdelivr.net/npm/basecoat-css@latest/dist/js/basecoat.min.js" defer></script>
<script src="https://cdn.jsdelivr.net/npm/basecoat-css@latest/dist/js/popover.min.js" defer></script>
```
## HTML Structure
### Basic Popover
```html
<div class="popover">
<button type="button" class="btn-outline" aria-haspopup="dialog" aria-expanded="false">
Open Popover
</button>
<div role="dialog" aria-label="Popover content">
<p>This is the popover content.</p>
</div>
</div>
```
### Popover with Header
```html
<div class="popover">
<button type="button" class="btn-outline" aria-haspopup="dialog" aria-expanded="false">
Open
</button>
<div role="dialog" aria-labelledby="popover-title" class="w-80">
<header class="border-b px-4 py-3">
<h3 id="popover-title" class="font-medium">Popover Title</h3>
</header>
<section class="p-4">
<p class="text-sm text-muted-foreground">This is the popover content with a header.</p>
</section>
</div>
</div>
```
## Positioning
### Positions
```html
<!-- Top (default) -->
<div class="popover">
<button type="button" class="btn-outline">Top</button>
<div role="dialog" data-side="top">Content above</div>
</div>
<!-- Bottom -->
<div class="popover">
<button type="button" class="btn-outline">Bottom</button>
<div role="dialog" data-side="bottom">Content below</div>
</div>
<!-- Left -->
<div class="popover">
<button type="button" class="btn-outline">Left</button>
<div role="dialog" data-side="left">Content to the left</div>
</div>
<!-- Right -->
<div class="popover">
<button type="button" class="btn-outline">Right</button>
<div role="dialog" data-side="right">Content to the right</div>
</div>
```
### Alignment
```html
<!-- Start aligned -->
<div class="popover">
<button type="button" class="btn-outline">Start</button>
<div role="dialog" data-align="start">Aligned to start</div>
</div>
<!-- Center aligned (default) -->
<div class="popover">
<button type="button" class="btn-outline">Center</button>
<div role="dialog" data-align="center">Centered</div>
</div>
<!-- End aligned -->
<div class="popover">
<button type="button" class="btn-outline">End</button>
<div role="dialog" data-align="end">Aligned to end</div>
</div>
```
## Implementation Examples
### User Profile Popover
```html
<div class="popover">
<button type="button" class="flex items-center gap-2" aria-haspopup="dialog" aria-expanded="false">
<img src="/avatar.png" alt="John Doe" class="size-8 rounded-full">
<span class="hidden sm:inline">John Doe</span>
<svg class="size-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="m6 9 6 6 6-6"></path>
</svg>
</button>
<div role="dialog" aria-label="User menu" class="w-64 p-0" data-align="end">
<header class="p-4 border-b">
<div class="flex items-center gap-3">
<img src="/avatar.png" alt="" class="size-10 rounded-full">
<div>
<p class="font-medium">John Doe</p>
<p class="text-sm text-muted-foreground">john@example.com</p>
</div>
</div>
</header>
<nav class="p-2">
<a href="/profile" class="block px-3 py-2 rounded hover:bg-accent">Profile</a>
<a href="/settings" class="block px-3 py-2 rounded hover:bg-accent">Settings</a>
<a href="/billing" class="block px-3 py-2 rounded hover:bg-accent">Billing</a>
<hr class="my-2">
<button type="button" class="w-full text-left px-3 py-2 rounded hover:bg-accent text-destructive">
Sign out
</button>
</nav>
</div>
</div>
```
### Date Picker Trigger
```html
<div class="popover">
<button type="button" class="btn-outline" aria-haspopup="dialog" aria-expanded="false">
<svg class="size-4 mr-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M8 2v4"></path>
<path d="M16 2v4"></path>
<rect width="18" height="18" x="3" y="4" rx="2"></rect>
<path d="M3 10h18"></path>
</svg>
<span id="selected-date">Select date</span>
</button>
<div role="dialog" aria-label="Date picker" class="w-auto p-4">
<!-- Calendar component would go here -->
<div class="grid grid-cols-7 gap-1 text-center text-sm">
<div class="font-medium text-muted-foreground">Su</div>
<div class="font-medium text-muted-foreground">Mo</div>
<div class="font-medium text-muted-foreground">Tu</div>
<div class="font-medium text-muted-foreground">We</div>
<div class="font-medium text-muted-foreground">Th</div>
<div class="font-medium text-muted-foreground">Fr</div>
<div class="font-medium text-muted-foreground">Sa</div>
<!-- Days... -->
</div>
</div>
</div>
```
### Filter Popover
```html
<div class="popover">
<button type="button" class="btn-outline" aria-haspopup="dialog" aria-expanded="false">
<svg class="size-4 mr-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"></polygon>
</svg>
Filters
<span class="badge-secondary ml-2">3</span>
</button>
<div role="dialog" aria-labelledby="filter-title" class="w-80 p-0" data-align="start">
<header class="flex items-center justify-between p-4 border-b">
<h3 id="filter-title" class="font-medium">Filters</h3>
<button type="button" class="text-sm text-muted-foreground hover:text-foreground">
Clear all
</button>
</header>
<section class="p-4 space-y-4">
<div class="grid gap-2">
<label class="label">Status</label>
<select class="input">
<option value="">All</option>
<option value="active">Active</option>
<option value="pending">Pending</option>
<option value="completed">Completed</option>
</select>
</div>
<div class="grid gap-2">
<label class="label">Date Range</label>
<div class="flex gap-2">
<input type="date" class="input flex-1">
<input type="date" class="input flex-1">
</div>
</div>
<div class="space-y-2">
<label class="label">Categories</label>
<label class="label gap-2 font-normal">
<input type="checkbox" class="input">
Technology
</label>
<label class="label gap-2 font-normal">
<input type="checkbox" class="input">
Design
</label>
<label class="label gap-2 font-normal">
<input type="checkbox" class="input">
Marketing
</label>
</div>
</section>
<footer class="flex justify-end gap-2 p-4 border-t">
<button type="button" class="btn-outline">Cancel</button>
<button type="button" class="btn">Apply Filters</button>
</footer>
</div>
</div>
```
### Share Popover
```html
<div class="popover">
<button type="button" class="btn-icon-outline" aria-haspopup="dialog" aria-expanded="false" aria-label="Share">
<svg class="size-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="18" cy="5" r="3"></circle>
<circle cx="6" cy="12" r="3"></circle>
<circle cx="18" cy="19" r="3"></circle>
<line x1="8.59" x2="15.42" y1="13.51" y2="17.49"></line>
<line x1="15.41" x2="8.59" y1="6.51" y2="10.49"></line>
</svg>
</button>
<div role="dialog" aria-label="Share options" class="w-72 p-4" data-side="bottom" data-align="end">
<h3 class="font-medium mb-3">Share this page</h3>
<div class="flex gap-2 mb-4">
<button class="btn-icon-outline flex-1" title="Share on Twitter">
<svg class="size-4"><!-- twitter icon --></svg>
</button>
<button class="btn-icon-outline flex-1" title="Share on Facebook">
<svg class="size-4"><!-- facebook icon --></svg>
</button>
<button class="btn-icon-outline flex-1" title="Share on LinkedIn">
<svg class="size-4"><!-- linkedin icon --></svg>
</button>
<button class="btn-icon-outline flex-1" title="Share via Email">
<svg class="size-4"><!-- mail icon --></svg>
</button>
</div>
<div class="relative">
<input type="text" readonly class="input pr-20" value="https://example.com/page">
<button class="absolute right-1.5 top-1/2 -translate-y-1/2 btn-sm-secondary">
Copy
</button>
</div>
</div>
</div>
```
### Settings Popover
```html
<div class="popover">
<button type="button" class="btn-icon-ghost" aria-haspopup="dialog" aria-expanded="false" aria-label="Settings">
<svg class="size-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"></path>
<circle cx="12" cy="12" r="3"></circle>
</svg>
</button>
<div role="dialog" aria-labelledby="settings-title" class="w-72 p-0" data-align="end">
<header class="px-4 py-3 border-b">
<h3 id="settings-title" class="font-medium">Display Settings</h3>
</header>
<section class="p-4 space-y-4">
<div class="flex items-center justify-between">
<label for="dark-mode" class="text-sm">Dark Mode</label>
<input type="checkbox" role="switch" id="dark-mode" class="input">
</div>
<div class="flex items-center justify-between">
<label for="compact" class="text-sm">Compact View</label>
<input type="checkbox" role="switch" id="compact" class="input">
</div>
<div class="grid gap-2">
<label class="text-sm">Font Size</label>
<input type="range" class="slider" min="12" max="20" value="16">
</div>
</section>
</div>
</div>
```
## JavaScript API
### Programmatic Control
```javascript
// The popover toggles automatically on button click
// You can also control it programmatically:
const popover = document.querySelector('.popover');
const trigger = popover.querySelector('button');
const content = popover.querySelector('[role="dialog"]');
// Check if open
const isOpen = trigger.getAttribute('aria-expanded') === 'true';
// Toggle
trigger.click();
// Close on outside click is handled automatically
```
### Close Popover from Inside
```javascript
// Button inside popover that closes it
document.querySelector('[data-close-popover]').addEventListener('click', function() {
const popover = this.closest('.popover');
const trigger = popover.querySelector('[aria-haspopup]');
trigger.click(); // Toggle to close
});
```
## Accessibility Guidelines
### Required Attributes
```html
<div class="popover">
<button
type="button"
aria-haspopup="dialog"
aria-expanded="false"
aria-controls="popover-content"
>
Open
</button>
<div
role="dialog"
id="popover-content"
aria-labelledby="popover-heading"
>
<h3 id="popover-heading">Title</h3>
<p>Content...</p>
</div>
</div>
```
### Keyboard Navigation
- **Enter/Space**: Open/close popover
- **Escape**: Close popover
- **Tab**: Navigate through focusable elements inside
- Focus is trapped within the popover when open
### Focus Management
```javascript
// Focus first focusable element when opened
popover.addEventListener('toggle', function(e) {
if (e.target.getAttribute('aria-expanded') === 'true') {
const firstFocusable = content.querySelector('button, input, select, textarea, a[href]');
if (firstFocusable) {
firstFocusable.focus();
}
}
});
```
## Best Practices
### When to Use Popovers
- User profile menus
- Filter panels
- Settings panels
- Share dialogs
- Quick actions
- Additional options
### When to Use Alternatives
- Simple hover information: Use Tooltip
- Critical confirmations: Use Alert Dialog
- Complex forms: Use Dialog/Modal
- Navigation menus: Use Dropdown Menu
### Content Guidelines
- Keep content focused and concise
- Include a clear way to close (click outside or close button)
- Use headers to provide context
- Group related actions
### Visual Guidelines
- Position to avoid viewport edges
- Use consistent spacing
- Maintain visual hierarchy
- Ensure adequate contrast
## Jinja/Nunjucks Macro
### Using the Macro
```jinja
{% from "popover.njk" import popover %}
{% set trigger %}
<svg><!-- icon --></svg>
Settings
{% endset %}
{% call popover(
trigger=trigger,
trigger_attrs={"class": "btn-outline"},
popover_attrs={"class": "w-72", "data-align": "end"}
) %}
<h3 class="font-medium mb-3">Settings</h3>
<p>Popover content here...</p>
{% endcall %}
```