Overlays
Popover
A small panel beside a button, for extra detail or a quick edit, without leaving the page.
Installation
npx sinaui add popoverCopy src/lib/ui/Popover.svelte, floating.ts and tokens.css. No dependencies.
Examples
Detail on demand
Focus moves into the panel when it opens; Escape closes it and returns focus to the trigger.
Muhammad ibn Musa al-Khwarizmi
Mathematician and astronomer at the House of Wisdom, Baghdad, around 820. His book on restoring and balancing gave algebra its name.
Read more<script lang="ts">
import Button from '#lib/ui/Button.svelte';
import Popover from '#lib/ui/Popover.svelte';
</script>
<Popover title="Muhammad ibn Musa al-Khwarizmi">
{#snippet trigger(props)}
<Button variant="link" {...props}>al-Khwarizmi</Button>
{/snippet}
<p class="bio">
Mathematician and astronomer at the House of Wisdom, Baghdad, around 820. His book on restoring
and balancing gave algebra its name.
</p>
<Button href="#profile" variant="secondary" size="sm">Read more</Button>
</Popover>
<style>
.bio {
margin: 0 0 0.75rem;
color: var(--ui-muted);
}
</style>Quick edit
Tab or click away to close it; Save closes it too, through bind:open.
Extend the loan
<script lang="ts">
import Button from '#lib/ui/Button.svelte';
import Input from '#lib/ui/Input.svelte';
import Popover from '#lib/ui/Popover.svelte';
let open = $state(false);
let days = $state('40');
</script>
<Popover bind:open title="Extend the loan">
{#snippet trigger(props)}
<Button variant="secondary" {...props}>Loan: {days} days</Button>
{/snippet}
<form
class="form"
onsubmit={(e) => {
e.preventDefault();
open = false;
}}
>
<Input label="Days" type="number" min="1" max="90" bind:value={days} />
<Button type="submit" size="sm">Save</Button>
</form>
</Popover>
<style>
.form {
display: grid;
gap: 0.75rem;
inline-size: 14rem;
}
.form > :global(button) {
justify-self: end;
}
</style>Props
| Prop | Type | Default |
|---|---|---|
open | boolean, bindable | false |
title | string (required) | |
hideTitle | boolean: screen readers only | false |
side | 'top' | 'right' | 'bottom' | 'left' | 'bottom' |
align | 'start' | 'center' | 'start' |
trigger | Snippet receiving trigger props (required) |
Accessibility
- A non-modal
role="dialog"labeled by its title; the trigger hasaria-haspopup="dialog"andaria-expanded. - It sits right after its trigger in the page, so Tab flows from the trigger into it and out again.
- Opens in the top layer, positions itself around the viewport and follows its trigger on scroll.
- For something people must act on before continuing, use a Dialog instead.
| Key | Action |
|---|---|
| Enter / Space | On the trigger: opens or closes it. |
| Escape | Closes it and returns focus to the trigger. |
| Tab | Moves through its content; leaving it closes it. |