Popover
A popup that displays an anchored interactive dialog on click. It uses the awesome Floating UI library in the background for positioning and allows you to fully customize its behavior.
import { Gear } from '@examples/primitives/popover/icons'
import Popover from '@corvu/popover'
import type { VoidComponent } from 'solid-js'
const PopoverExample: VoidComponent = () => {
return (
<Popover
floatingOptions={{
offset: 13,
flip: true,
shift: true,
}}
>
<Popover.Trigger class="my-auto rounded-full bg-corvu-100 p-3 transition-all duration-100 hover:bg-corvu-200 active:translate-y-0.5">
<Gear size="26" />
<span class="sr-only">Settings</span>
</Popover.Trigger>
<Popover.Portal>
<Popover.Content class="z-50 rounded-lg bg-corvu-100 px-3 py-2 shadow-md corvu-open:animate-in corvu-open:fade-in-50 corvu-open:slide-in-from-top-1 corvu-closed:animate-out corvu-closed:fade-out-50 corvu-closed:slide-out-to-top-1">
<Popover.Label class="font-bold">Settings</Popover.Label>
<div class="grid grid-cols-[auto,1fr]">
<label class="col-span-2 mt-2 grid grid-cols-subgrid">
<span>Width</span>
<input
type="number"
value="32"
class="ml-10 w-20 rounded border-2 border-corvu-400 bg-corvu-200 px-2 py-1 text-sm"
/>
</label>
<label class="col-span-2 mt-2 grid grid-cols-subgrid">
<span>Height</span>
<input
type="number"
value="32"
class="ml-10 w-20 rounded border-2 border-corvu-400 bg-corvu-200 px-2 py-1 text-sm"
/>
</label>
</div>
<Popover.Arrow class="text-corvu-100" />
</Popover.Content>
</Popover.Portal>
</Popover>
)
}
export default PopoverExample
import animatePlugin from 'tailwindcss-animate'
import corvuPlugin from '@corvu/tailwind'
import formsPlugin from '@tailwindcss/forms'
/** @type {import('tailwindcss').Config} */
export default {
content: ['./src/**/*.{ts,tsx}'],
theme: {
extend: {
colors: {
corvu: {
bg: '#f3f1fe',
100: '#e6e2fd',
200: '#d4cbfb',
300: '#bcacf6',
400: '#a888f1',
text: '#180f24',
},
},
},
},
plugins: [animatePlugin, corvuPlugin, formsPlugin],
}
import './index.css'
import { Gear } from '@examples/primitives/popover/icons'
import Popover from '@corvu/popover'
import type { VoidComponent } from 'solid-js'
const PopoverExample: VoidComponent = () => {
return (
<Popover
floatingOptions={{
offset: 13,
flip: true,
shift: true,
}}
>
<Popover.Trigger>
<Gear size="26" />
<span class="sr-only">Settings</span>
</Popover.Trigger>
<Popover.Portal>
<Popover.Content>
<Popover.Label>Settings</Popover.Label>
<div class="input_grid">
<label>
<span>Width</span>
<input type="number" value="32" />
</label>
<label>
<span>Height</span>
<input type="number" value="32" />
</label>
</div>
<Popover.Arrow />
</Popover.Content>
</Popover.Portal>
</Popover>
)
}
export default PopoverExample
[data-corvu-popover-trigger] {
margin-top: auto;
margin-bottom: auto;
border-radius: 9999px;
background-color: hsl(249, 87%, 94%);
padding: 0.75rem;
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 100ms;
}
[data-corvu-tooltip-trigger]:hover {
background-color: hsl(251, 86%, 89%);
}
[data-corvu-tooltip-trigger]:active {
transform: translate(0, 0.125rem);
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
[data-corvu-tooltip-content] {
z-index: 50;
border-radius: 0.5rem;
background-color: hsl(249, 87%, 94%);
padding-left: 0.75rem;
padding-right: 0.75rem;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
}
@keyframes enter {
from {
opacity: var(--tw-enter-opacity, 1);
transform: translate3d(var(--tw-enter-translate-x, 0), var(--tw-enter-translate-y, 0), 0) scale3d(var(--tw-enter-scale, 1), var(--tw-enter-scale, 1), var(--tw-enter-scale, 1)) rotate(var(--tw-enter-rotate, 0))
}
}
@keyframes exit {
to {
opacity: var(--tw-exit-opacity, 1);
transform: translate3d(var(--tw-exit-translate-x, 0), var(--tw-exit-translate-y, 0), 0) scale3d(var(--tw-exit-scale, 1), var(--tw-exit-scale, 1), var(--tw-exit-scale, 1)) rotate(var(--tw-exit-rotate, 0))
}
}
[data-corvu-tooltip-content][data-open] {
animation-name: enter;
animation-duration: 150ms;
--tw-enter-opacity: initial;
--tw-enter-scale: initial;
--tw-enter-rotate: initial;
--tw-enter-translate-x: initial;
--tw-enter-translate-y: initial;
--tw-enter-opacity: 0.5;
--tw-enter-translate-y: -0.25rem;
}
[data-corvu-tooltip-content][data-closed] {
animation-name: exit;
animation-duration: 150ms;
--tw-exit-opacity: initial;
--tw-exit-scale: initial;
--tw-exit-rotate: initial;
--tw-exit-translate-x: initial;
--tw-exit-translate-y: initial;
--tw-exit-opacity: 0.5;
--tw-exit-translate-y: -0.25rem;
}
[data-corvu-popover-label] {
font-weight: 500;
}
.input_grid {
display: grid;
grid-template-columns: auto 1fr;
}
label {
grid-column: span 2 / span 2;
margin-top: 0.5rem;
display: grid;
grid-template-columns: subgrid;
}
input {
margin-left: 2.5rem;
width: 5rem;
border-radius: 0.25rem;
border-width: 2px;
border-color: hsl(258, 79%, 74%);
background-color: hsl(251, 86%, 89%);
padding-left: 0.5rem;
padding-right: 0.5rem;
padding-top: 0.25rem;
padding-bottom: 0.25rem;
font-size: 0.875rem;
line-height: 1.25rem;
}
[data-corvu-tooltip-arrow] {
color: hsl(249, 87%, 94%);
}
Features Section titled Features
- Fully customize the positioning behavior
- Waits for pending animations before removing the popover from the DOM
- Pressing the escape key and interacting outside closes the popover
- Customizable focus management
- Optional arrow component
Installation Section titled Installation
npm install @corvu/popover
The popover is also included in the main corvu
package under corvu/popover
.
Usage Section titled Usage
import Popover from '@corvu/popover' // 'corvu/popover'
// or
// import { Root, Trigger, ... } from '@corvu/popover'
Anatomy Section titled Anatomy
<Popover>
<Popover.Anchor>
<Popover.Trigger />
</Popover.Anchor>
<Popover.Portal>
<Popover.Overlay />
<Popover.Content>
<Popover.Arrow />
<Popover.Close />
<Popover.Label />
<Popover.Description />
</Popover.Content>
</Popover.Portal>
</Popover>
The anchor component can optionally be used to make the popover anchor itself to another element than the trigger.
Floating UI Section titled Floating UI
The initial placement of the tooltip can be configured with the placement
property on the Root
component. Refer to the Floating UI placement option to see all available placements.
You can configure the positioning strategy with the strategy
property on the Root
component. Valid options are absolute
or fixed
. In most cases, it’s recommended to leave this property at the default setting absolute
, as this requires the browser to do the least work when updating the position.
FloatingOptions Section titled FloatingOptions
Middlewares can be configured with the floatingOptions
property on the Root
component. Check out the FloatingOptions
type in the API Reference.
If you want to enable a middleware with default options, you can pass true
as the value.
The option types are directly taken from the Floating UI middlewares. Check out Floating UI’s middleware documentation.
FloatingState Section titled FloatingState
The popover also provides a you with a floatingState
property that contains the current state of the popover. You can use this property to override a specific behavior according to your needs. Check out the FloatingState
type in the API Reference.
Accessibility Section titled Accessibility
Adheres to the Dialog WAI-ARIA design pattern.
API reference Section titled API reference
Only components which are specific to the popover are documented here. For all other components, please refer to the Dialog API reference.
The dialog context is re-exported as Popover.useDialogContext
and the root children callback also accepts all props of the dialog root callback function, which are documented in the Dialog API reference.
Context wrapper for the popover. Is required for every popover you create.
Props
Inherits <Dialog.Root />
Props.
Property | Default | Type/Description |
---|---|---|
placement | 'bottom' | Placement The initial placement of the popover. |
strategy | 'absolute' | Strategy The strategy to use when positioning the popover. |
floatingOptions | { flip: true, shift: true } | FloatingOptions | null Floating options of the popover. |
Anchor element to override the floating reference.
Props
Property | Default | Type/Description |
---|---|---|
as | div | ValidComponent Component to render the dynamic component as. |
contextId | - | string The id of the popover context to use. |
Data attributes
Data attributes present on <Anchor />
components.
Property | Description |
---|---|
data-corvu-popover-anchor | Present on every popover anchor element. |
Button that changes the open state of the popover when clicked.
Props
Inherits <Dialog.Trigger />
Props.
Data attributes
Data attributes present on <Trigger />
components.
Property | Description |
---|---|
data-corvu-popover-trigger | Present on every popover trigger element. |
data-open | Present when the popover is open. |
data-closed | Present when the popover is closed. |
data-placement | Current placement of the popover. Only present when the popover is open. |
Portals its children at the end of the body element to ensure that the dialog always rendered on top.
Props
Inherits <Dialog.Portal />
Props.
Component which can be used to create a faded background. Can be animated.
Props
Inherits <Dialog.Overlay />
Props.
Data attributes
Data attributes present on <Overlay />
components.
Property | Description |
---|---|
data-corvu-popover-overlay | Present on every popover overlay element. |
data-open | Present when the popover is open. |
data-closed | Present when the popover is closed. |
Arrow element that automatically points towards the floating reference. Comes with a default arrow svg, but can be overridden by providing your own as the children.
Props
Property | Default | Type/Description |
---|---|---|
size | 16 | number Size of the arrow in px. |
as | div | ValidComponent Component to render the dynamic component as. |
contextId | - | string The id of the popover context to use. |
Data attributes
Data attributes present on <Arrow />
components.
Property | Description |
---|---|
data-corvu-popover-arrow | Present on every popover arrow element. |
Content of the popover. Can be animated.
Props
Inherits <Dialog.Content />
Props.
Data attributes
Data attributes present on <Content />
components.
Property | Description |
---|---|
data-corvu-popover-content | Present on every popover content element. |
data-open | Present when the popover is open. |
data-closed | Present when the popover is closed. |
data-placement | Current placement of the popover. |
Close button that changes the open state to false when clicked.
Props
Inherits <Dialog.Close />
Props.
Data attributes
Data attributes present on <Close />
components.
Property | Description |
---|---|
data-corvu-popover-close | Present on every popover close element. |
Label element to announce the popover to accessibility tools.
Props
Inherits <Dialog.Label />
Props.
Data attributes
Data attributes present on <Label />
components.
Property | Description |
---|---|
data-corvu-popover-label | Present on every popover label element. |
Description element to announce the popover to accessibility tools.
Props
Inherits <Dialog.Description />
Props.
Data attributes
Data attributes present on <Description />
components.
Property | Description |
---|---|
data-corvu-popover-description | Present on every popover description element. |
Context which exposes various properties to interact with the popover. Optionally provide a contextId to access a keyed context.
Returns
Property | Type/Description |
---|---|
placement | Accessor<Placement> The initial placement of the popover. |
strategy | Accessor<Strategy> The strategy to use when positioning the popover. |
floatingOptions | Accessor<FloatingOptions | null> Floating options of the popover. |
floatingState | Accessor<FloatingState> The current floating state of the popover. |
Inherited from <Dialog.useContext />
.
Props that are passed to the Root component children callback.
Props
Inherits <Dialog.RootChildrenProps />
Props.
Property | Type/Description |
---|---|
placement | Placement The initial placement of the popover. |
strategy | Strategy The strategy to use when positioning the popover. |
floatingOptions | FloatingOptions | null Floating options of the popover. |
floatingState | FloatingState The current floating state of the popover. |
type FloatingOptions = {
arrow: Padding,
autoPlacement: boolean | AutoPlacementOptions,
flip: boolean | FlipOptions,
hide: boolean | HideOptions,
inline: boolean | InlineOptions,
offset: OffsetOptions,
shift: boolean | ShiftOptions,
size: DetectOverflowOptions & {
fitViewPort: boolean,
matchSize: boolean,
},
}
type FloatingState = {
arrowX: number | null,
arrowY: number | null,
height: number | null,
maxHeight: number | null,
maxWidth: number | null,
placement: Placement,
width: number | null,
x: number,
y: number,
}
Developed and designed by Jasmin