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/vue # or yarn add @zag-js/hover-card @zag-js/vue
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/solid # or yarn add @zag-js/hover-card @zag-js/solid
This command will install the framework agnostic hover card logic and the reactive utilities for your framework of choice.
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-part
attribute to help identify them in the DOM.
On a high level, the hover card consists of:
- Trigger: The trigger for the Hover Card - Usually a link.
- Positioner: The element that positions the hover card.
- Content: The container for the hover card's content.
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 [state, send] = useMachine(hoverCard.machine({ id: "1" })) const api = hoverCard.connect(state, send, normalizeProps) return ( <> <a href="https://twitter.com/zag_js" target="_blank" {...api.triggerProps} > Twitter </a> {api.isOpen && ( <Portal> <div {...api.positionerProps}> <div {...api.contentProps}> <div {...api.arrowProps}> <div {...api.arrowTipProps} /> </div> Twitter Preview </div> </div> </Portal> )} </> ) }
import * as hoverCard from "@zag-js/hover-card" import { normalizeProps, useMachine } from "@zag-js/vue" import { defineComponent, h, Fragment, computed, Teleport } from "vue" export default defineComponent({ name: "HoverCard", setup() { const [state, send] = useMachine(hoverCard.machine({ id: "hoverCard" })) const apiRef = computed(() => hoverCard.connect(state.value, send, normalizeProps), ) return () => { const api = apiRef.value return ( <> <a href="https://twitter.com/zag_js" target="_blank" {...api.triggerProps} > Twitter </a> {api.isOpen && ( <Teleport to="body"> <div {...api.positionerProps}> <div {...api.contentProps}> <div {...api.arrowProps}> <div {...api.arrowTipProps} /> </div> Twitter Preview </div> </div> </Teleport> )} </> ) } }, })
<script setup> import * as hoverCard from "@zag-js/hover-card" import { normalizeProps, useMachine } from "@zag-js/vue" import { computed, Teleport } from "vue" const [state, send] = useMachine(hoverCard.machine({ id: "1" })) const api = computed(() => hoverCard.connect(state.value, send, normalizeProps)) </script> <template> <a href="https://twitter.com/zag_js" target="_blank" v-bind="api.triggerProps" > Twitter </a> <Teleport to="body" v-if="api.isOpen"> <div v-bind="api.positionerProps"> <div v-bind="api.contentProps"> <div v-bind="api.arrowProps"> <div v-bind="api.arrowTipProps" /> </div> Twitter Preview </div> </div> </Teleport> </template>
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 [state, send] = useMachine(hoverCard.machine({ id: "1" })) const api = hoverCard.connect(state, send, normalizeProps) return ( <> <a href="https://twitter.com/zag_js" target="_blank" {...api().triggerProps} > Twitter </a> <Show when={api().isOpen}> <Portal> <div {...api().positionerProps}> <div {...api().contentProps}> <div {...api().arrowProps}> <div {...api().arrowTipProps} /> </div> Twitter Preview </div> </div> </Portal> </Show> </> ) }
Making it opened initially
To make an hover card open by default, set the context's open
property to true
const [state, send] = useMachine( hoverCard.machine({ open: true, }), )
Listening for open state changes
When the hover card is opened
or closed
, the onOpenChange
callback is
invoked.
const [state, send] = 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
The hover card's api
method exposes the following methods:
isOpen
boolean
Whether the hover card is openopen
() => void
Function to open the hover cardclose
() => void
Function to close the hover cardsetPositioning
(options?: Partial<PositioningOptions>) => void
Function to reposition the popover
Edit this page on GitHub