Popover
Floating panels built on the native HTML Popover API and CSS Anchor Positioning. No JavaScript required.
The trigger needs anchor-name set to match the popover's id (prefixed with --). The Astro and React wrappers set position-anchor automatically from the id prop.
Usage
Popover content goes here.
<button
class="zui-button"
popovertarget="demo-popover"
style="anchor-name: --demo-popover"
>
Open popover
</button>
<div id="demo-popover" popover class="zui-popover" style="position-anchor: --demo-popover;">
<p>Popover content goes here.</p>
</div> import { Popover, Button } from '@mrmartineau/zui/react'
<Button popovertarget="demo-popover" style={{ anchorName: '--demo-popover' }}>
Open popover
</Button>
<Popover id="demo-popover">
<p>Popover content goes here.</p>
</Popover> ---
import { Popover, Button } from '@mrmartineau/zui/astro'
---
<Button popovertarget="demo-popover" style="anchor-name: --demo-popover">
Open popover
</Button>
<Popover id="demo-popover">
<p>Popover content goes here.</p>
</Popover> import { Popover, Button } from '@mrmartineau/zui/solid'
<Button popovertarget="demo-popover" style={{ anchorName: '--demo-popover' }}>
Open popover
</Button>
<Popover id="demo-popover">
<p>Popover content goes here.</p>
</Popover> <script lang="ts">
import { Popover, Button } from '@mrmartineau/zui/svelte'
</script>
<Button popovertarget="demo-popover" style="anchor-name: --demo-popover">
Open popover
</Button>
<Popover id="demo-popover">
<p>Popover content goes here.</p>
</Popover> <template>
<Button popovertarget="demo-popover" style="anchor-name: --demo-popover">
Open popover
</Button>
<Popover id="demo-popover">
<p>Popover content goes here.</p>
</Popover>
</template>
<script setup>
import { Popover, Button } from '@mrmartineau/zui/vue'
</script> Auto vs manual
popover="auto" (default) closes when clicking outside or pressing Esc. popover="manual" requires an explicit close trigger.
I close when you click outside.
I stay open until explicitly closed.
<button class="zui-button" popovertarget="pop-auto" style="anchor-name: --pop-auto">
Auto (click outside to close)
</button>
<div id="pop-auto" popover="auto" class="zui-popover" style="position-anchor: --pop-auto;">
<p>I close when you click outside.</p>
</div>
<button class="zui-button zui-button-variant-outline" popovertarget="pop-manual" style="anchor-name: --pop-manual">
Manual (must use close button)
</button>
<div id="pop-manual" popover="manual" class="zui-popover" style="position-anchor: --pop-manual;">
<p>I stay open until explicitly closed.</p>
<button class="zui-button zui-button-size-sm" style="margin-top:0.5rem;" popovertarget="pop-manual" popovertargetaction="hide">
Close
</button>
</div> import { Popover, Button } from '@mrmartineau/zui/react'
{/* auto (default) */}
<Button popovertarget="pop-auto" style={{ anchorName: '--pop-auto' }}>
Auto (click outside to close)
</Button>
<Popover id="pop-auto">
<p>I close when you click outside.</p>
</Popover>
{/* manual */}
<Button variant="outline" popovertarget="pop-manual" style={{ anchorName: '--pop-manual' }}>
Manual (must use close button)
</Button>
<Popover id="pop-manual" popover="manual">
<p>I stay open until explicitly closed.</p>
<Button size="sm" style={{ marginTop: '0.5rem' }} popovertarget="pop-manual" popovertargetaction="hide">
Close
</Button>
</Popover> ---
import { Popover, Button } from '@mrmartineau/zui/astro'
---
<!-- auto (default) -->
<Button popovertarget="pop-auto" style="anchor-name: --pop-auto">
Auto (click outside to close)
</Button>
<Popover id="pop-auto">
<p>I close when you click outside.</p>
</Popover>
<!-- manual -->
<Button variant="outline" popovertarget="pop-manual" style="anchor-name: --pop-manual">
Manual (must use close button)
</Button>
<Popover id="pop-manual" type="manual">
<p>I stay open until explicitly closed.</p>
<Button size="sm" style="margin-top:0.5rem" popovertarget="pop-manual" popovertargetaction="hide">
Close
</Button>
</Popover> import { Popover, Button } from '@mrmartineau/zui/solid'
{/* auto (default) */}
<Button popovertarget="pop-auto" style={{ anchorName: '--pop-auto' }}>
Auto (click outside to close)
</Button>
<Popover id="pop-auto">
<p>I close when you click outside.</p>
</Popover>
{/* manual */}
<Button variant="outline" popovertarget="pop-manual" style={{ anchorName: '--pop-manual' }}>
Manual (must use close button)
</Button>
<Popover id="pop-manual" popover="manual">
<p>I stay open until explicitly closed.</p>
<Button size="sm" style={{ marginTop: '0.5rem' }} popovertarget="pop-manual" popovertargetaction="hide">
Close
</Button>
</Popover> <script lang="ts">
import { Popover, Button } from '@mrmartineau/zui/svelte'
</script>
<!-- auto (default) -->
<Button popovertarget="pop-auto" style="anchor-name: --pop-auto">
Auto (click outside to close)
</Button>
<Popover id="pop-auto">
<p>I close when you click outside.</p>
</Popover>
<!-- manual -->
<Button variant="outline" popovertarget="pop-manual" style="anchor-name: --pop-manual">
Manual (must use close button)
</Button>
<Popover id="pop-manual" popover="manual">
<p>I stay open until explicitly closed.</p>
<Button size="sm" style="margin-top: 0.5rem" popovertarget="pop-manual" popovertargetaction="hide">
Close
</Button>
</Popover> <template>
<!-- auto (default) -->
<Button popovertarget="pop-auto" style="anchor-name: --pop-auto">
Auto (click outside to close)
</Button>
<Popover id="pop-auto">
<p>I close when you click outside.</p>
</Popover>
<!-- manual -->
<Button variant="outline" popovertarget="pop-manual" style="anchor-name: --pop-manual">
Manual (must use close button)
</Button>
<Popover id="pop-manual" popover="manual">
<p>I stay open until explicitly closed.</p>
<Button size="sm" style="margin-top: 0.5rem" popovertarget="pop-manual" popovertargetaction="hide">
Close
</Button>
</Popover>
</template>
<script setup>
import { Popover, Button } from '@mrmartineau/zui/vue'
</script> With rich content
<button class="zui-button" popovertarget="pop-rich" style="anchor-name: --pop-rich">
Options
</button>
<div id="pop-rich" popover class="zui-popover" style="position-anchor: --pop-rich; min-width:14rem;">
<div style="display:flex;flex-direction:column;gap:0.25rem;">
<button class="zui-button zui-button-variant-ghost" style="justify-content:flex-start;">
<i class="ph ph-pencil"></i> Edit
</button>
<button class="zui-button zui-button-variant-ghost" style="justify-content:flex-start;">
<i class="ph ph-copy"></i> Duplicate
</button>
<button class="zui-button zui-button-variant-ghost zui-button-color-destructive" style="justify-content:flex-start;">
<i class="ph ph-trash"></i> Delete
</button>
</div>
</div> import { Popover, Button } from '@mrmartineau/zui/react'
<Button popovertarget="pop-rich" style={{ anchorName: '--pop-rich' }}>
Options
</Button>
<Popover id="pop-rich" style={{ minWidth: '14rem' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.25rem' }}>
<Button variant="ghost" style={{ justifyContent: 'flex-start' }}>
<i className="ph ph-pencil" /> Edit
</Button>
<Button variant="ghost" style={{ justifyContent: 'flex-start' }}>
<i className="ph ph-copy" /> Duplicate
</Button>
<Button variant="ghost" color="destructive" style={{ justifyContent: 'flex-start' }}>
<i className="ph ph-trash" /> Delete
</Button>
</div>
</Popover> ---
import { Popover, Button } from '@mrmartineau/zui/astro'
---
<Button popovertarget="pop-rich" style="anchor-name: --pop-rich">
Options
</Button>
<Popover id="pop-rich" style="min-width: 14rem">
<div style="display:flex;flex-direction:column;gap:0.25rem">
<Button variant="ghost" style="justify-content:flex-start">
<i class="ph ph-pencil" /> Edit
</Button>
<Button variant="ghost" style="justify-content:flex-start">
<i class="ph ph-copy" /> Duplicate
</Button>
<Button variant="ghost" color="destructive" style="justify-content:flex-start">
<i class="ph ph-trash" /> Delete
</Button>
</div>
</Popover> import { Popover, Button } from '@mrmartineau/zui/solid'
<Button popovertarget="pop-rich" style={{ anchorName: '--pop-rich' }}>
Options
</Button>
<Popover id="pop-rich" style={{ minWidth: '14rem' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.25rem' }}>
<Button variant="ghost" style={{ justifyContent: 'flex-start' }}>
<i class="ph ph-pencil" /> Edit
</Button>
<Button variant="ghost" style={{ justifyContent: 'flex-start' }}>
<i class="ph ph-copy" /> Duplicate
</Button>
<Button variant="ghost" color="destructive" style={{ justifyContent: 'flex-start' }}>
<i class="ph ph-trash" /> Delete
</Button>
</div>
</Popover> <script lang="ts">
import { Popover, Button } from '@mrmartineau/zui/svelte'
</script>
<Button popovertarget="pop-rich" style="anchor-name: --pop-rich">
Options
</Button>
<Popover id="pop-rich" style="min-width: 14rem">
<div style="display: flex; flex-direction: column; gap: 0.25rem">
<Button variant="ghost" style="justify-content: flex-start">
<i class="ph ph-pencil"></i> Edit
</Button>
<Button variant="ghost" style="justify-content: flex-start">
<i class="ph ph-copy"></i> Duplicate
</Button>
<Button variant="ghost" color="destructive" style="justify-content: flex-start">
<i class="ph ph-trash"></i> Delete
</Button>
</div>
</Popover> <template>
<Button popovertarget="pop-rich" style="anchor-name: --pop-rich">
Options
</Button>
<Popover id="pop-rich" style="min-width: 14rem">
<div style="display: flex; flex-direction: column; gap: 0.25rem">
<Button variant="ghost" style="justify-content: flex-start">
<i class="ph ph-pencil" /> Edit
</Button>
<Button variant="ghost" style="justify-content: flex-start">
<i class="ph ph-copy" /> Duplicate
</Button>
<Button variant="ghost" color="destructive" style="justify-content: flex-start">
<i class="ph ph-trash" /> Delete
</Button>
</div>
</Popover>
</template>
<script setup>
import { Popover, Button } from '@mrmartineau/zui/vue'
</script> CSS custom properties
| Property | Default | Description |
|---|---|---|
--zui-popover-bg | var(--color-surface) | Background colour |
--zui-popover-fg | currentColor | Text colour |
--zui-popover-border | var(--color-border) | Border colour |
--zui-popover-radius | var(--radius-lg) | Border radius |
--zui-popover-shadow | var(--shadow-lg) | Box shadow |
--zui-popover-padding | var(--space-xs) | Padding inside the popover |