Hover Card
An hover card allows sighted users to preview content available behind a link
Features
- Customize side, alignment, offsets
- Optionally render a pointing arrow
- Supports custom open and close delays
- Opens on hover only
- Ignored by screen readers
Installation
To use the hover card machine in your project, run the following command in your command line:
npm install @zag-js/hover-card @zag-js/react # or yarn add @zag-js/hover-card @zag-js/react
npm install @zag-js/hover-card @zag-js/solid # or yarn add @zag-js/hover-card @zag-js/solid
npm install @zag-js/hover-card @zag-js/vue # or yarn add @zag-js/hover-card @zag-js/vue
npm install @zag-js/hover-card @zag-js/svelte # or yarn add @zag-js/hover-card @zag-js/svelte
Anatomy
To set up the hover card correctly, you'll need to understand its anatomy and how we name its parts.
Each part includes a
data-partattribute to help identify them in the DOM.
Usage
First, import the hover card package into your project
import * as hoverCard from "@zag-js/hover-card"
The hover card package exports two key functions:
machine— The state machine logic for the hover card widget.connect— The function that translates the machine's state to JSX attributes and event handlers.
Next, import the required hooks and functions for your framework and use the hover-card machine in your project 🔥
import * as hoverCard from "@zag-js/hover-card" import { useMachine, normalizeProps, Portal } from "@zag-js/react" function HoverCard() { const service = useMachine(hoverCard.machine, { id: "1" }) const api = hoverCard.connect(service, normalizeProps) return ( <> <a href="https://twitter.com/zag_js" target="_blank" {...api.getTriggerProps()} > Twitter </a> {api.open && ( <Portal> <div {...api.getPositionerProps()}> <div {...api.getContentProps()}> <div {...api.getArrowProps()}> <div {...api.getArrowTipProps()} /> </div> Twitter Preview </div> </div> </Portal> )} </> ) }
import * as hoverCard from "@zag-js/hover-card" import { normalizeProps, useMachine } from "@zag-js/solid" import { createMemo, createUniqueId, Show } from "solid-js" import { Portal } from "solid-js/web" function Checkbox() { const service = useMachine(hoverCard.machine, { id: "1" }) const api = hoverCard.connect(service, normalizeProps) return ( <> <a href="https://twitter.com/zag_js" target="_blank" {...api().getTriggerProps()} > Twitter </a> <Show when={api().open}> <Portal> <div {...api().getPositionerProps()}> <div {...api().getContentProps()}> <div {...api().getArrowProps()}> <div {...api().getArrowTipProps()} /> </div> Twitter Preview </div> </div> </Portal> </Show> </> ) }
<script setup> import * as hoverCard from "@zag-js/hover-card" import { normalizeProps, useMachine } from "@zag-js/vue" import { computed, Teleport } from "vue" const service = useMachine(hoverCard.machine, { id: "1" }) const api = computed(() => hoverCard.connect(service, normalizeProps)) </script> <template> <a href="https://twitter.com/zag_js" target="_blank" v-bind="api.getTriggerProps()" > Twitter </a> <Teleport to="body" v-if="api.open"> <div v-bind="api.getPositionerProps()"> <div v-bind="api.getContentProps()"> <div v-bind="api.getArrowProps()"> <div v-bind="api.getArrowTipProps()" /> </div> Twitter Preview </div> </div> </Teleport> </template>
<script lang="ts"> import * as hoverCard from "@zag-js/hover-card" import { portal, useMachine, normalizeProps } from "@zag-js/svelte" const id = $props.id() const service = useMachine(hoverCard.machine, ({ id })) const api = $derived(hoverCard.connect(service, normalizeProps)) </script> <a href="https://twitter.com/zag_js" target="_blank" {...api.getTriggerProps()}> Twitter </a> {#if api.open} <div use:portal {...api.getPositionerProps()}> <div {...api.getPositionerProps()}> <div {...api.getContentProps()}> <div {...api.getArrowProps()}> <div {...api.getArrowTipProps()}></div> </div> Twitter Preview </div> </div> </div> {/if}
Setting the initial state
To make an hover card open by default, set the defaultOpen property to true
const service = useMachine(hoverCard.machine, { defaultOpen: true, })
Listening for open state changes
When the hover card is opened or closed, the onOpenChange callback is
invoked.
const service = useMachine(hoverCard.machine, { onOpenChange(details) { // details => { open: boolean } console.log("hovercard is:", details.open ? "opened" : "closed") }, })
Styling guide
Earlier, we mentioned that each hover card part has a data-part attribute
added to them to select and style them in the DOM.
[data-part="trigger"] { /* styles for trigger */ } [data-part="content"] { /* styles for content */ }
Open and closed state
The hover card exposes a data-state attribute that can be used to style the
hover card based on its open-close state.
[data-part="trigger"][data-state="open|closed"] { /* styles for open or closed state */ } [data-part="content"][data-state="open|closed"] { /* styles for open or closed state */ }
Arrow
Zag exposes some variable that can be used to style the arrow.
[data-part="arrow"] { /* styles for arrow */ --arrow-background: white; --arrow-size: 8px; }
[data-part="content"] { /* styles for content */ }
Methods and Properties
Machine Context
The hover card machine exposes the following context properties:
idsPartial<{ trigger: string; content: string; positioner: string; arrow: string; }>The ids of the elements in the popover. Useful for composition.onOpenChange(details: OpenChangeDetails) => voidFunction called when the hover card opens or closes.openDelaynumberThe duration from when the mouse enters the trigger until the hover card opens.closeDelaynumberThe duration from when the mouse leaves the trigger or content until the hover card closes.openbooleanThe controlled open state of the hover carddefaultOpenbooleanThe initial open state of the hover card when rendered. Use when you don't need to control the open state of the hover card.positioningPositioningOptionsThe user provided options used to position the popover contentdir"ltr" | "rtl"The document's text/writing direction.idstringThe unique identifier of the machine.getRootNode() => ShadowRoot | Node | DocumentA root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.onPointerDownOutside(event: PointerDownOutsideEvent) => voidFunction called when the pointer is pressed down outside the componentonFocusOutside(event: FocusOutsideEvent) => voidFunction called when the focus is moved outside the componentonInteractOutside(event: InteractOutsideEvent) => voidFunction called when an interaction happens outside the component
Machine API
The hover card api exposes the following methods:
openbooleanWhether the hover card is opensetOpen(open: boolean) => voidFunction to open the hover cardreposition(options?: Partial<PositioningOptions>) => voidFunction to reposition the popover
Data Attributes
Accessibility
Keyboard Interactions
The hover card is intended for mouse users only so will not respond to keyboard navigation.
Edit this page on GitHub