Menu

Change language

GitHub repo

Hover Card

hover-cardui

Rich popover triggered by hover/focus instead of click. Use for inline previews — user cards on @mentions, link previews, KPI explanations. Hand-rolled runes port (no headless dependency) with configurable open/close delay, side/align placement, and bindable open state.

Installation

$npx shadcn-svelte@latest add https://uipkge.dev/r/svelte/hover-card.json
Manifest:https://uipkge.dev/r/svelte/hover-card.jsonInstalls to:src/lib/components/ui/hover-card/

Examples

Loading interactive previews…

Props

NameType / ValuesDefaultRequired
open

Controlled open state. Bind with `bind:open`.

boolean—optional
openDelay

Delay before opening on hover/focus, in ms. Default 700.

number700optional
closeDelay

Delay before closing after leaving, in ms. Default 300.

number300optional
onOpenChange

Called whenever the card opens or closes.

(open: boolean) => void—optional
childrenSnippet—optional

Files installed (4)

  • src/lib/components/ui/hover-card/HoverCard.svelte2.7 kB
    <script lang="ts" module>
      import { getContext } from 'svelte'
      import type { Snippet } from 'svelte'
      import type { HTMLAttributes } from 'svelte/elements'
    
      export interface HoverCardProps extends HTMLAttributes<HTMLDivElement> {
        /** Controlled open state. Bind with `bind:open`. */
        open?: boolean
        /** Delay before opening on hover/focus, in ms. Default 700. */
        openDelay?: number
        /** Delay before closing after leaving, in ms. Default 300. */
        closeDelay?: number
        /** Called whenever the card opens or closes. */
        onOpenChange?: (open: boolean) => void
        children?: Snippet
      }
    
      export interface HoverCardContext {
        readonly open: boolean
        scheduleOpen: () => void
        scheduleClose: () => void
        cancelClose: () => void
      }
    
      export const HOVER_CARD_CONTEXT_KEY = Symbol('uipkge:hover-card')
    
      export function getHoverCardContext(): HoverCardContext {
        return getContext<HoverCardContext>(HOVER_CARD_CONTEXT_KEY)
      }
    </script>
    
    <script lang="ts">
      import { onDestroy, setContext } from 'svelte'
      import { cn } from '$lib/utils'
    
      let {
        class: className,
        open = $bindable(false),
        openDelay = 700,
        closeDelay = 300,
        onOpenChange,
        children,
        ...restProps
      }: HoverCardProps = $props()
    
      let openTimer: ReturnType<typeof setTimeout> | null = null
      let closeTimer: ReturnType<typeof setTimeout> | null = null
    
      function setOpen(next: boolean) {
        if (open === next) return
        open = next
        onOpenChange?.(next)
      }
    
      function scheduleOpen() {
        if (closeTimer) {
          clearTimeout(closeTimer)
          closeTimer = null
        }
        if (open || openTimer) return
        if (openDelay <= 0) {
          setOpen(true)
          return
        }
        openTimer = setTimeout(() => {
          openTimer = null
          setOpen(true)
        }, openDelay)
      }
    
      function scheduleClose() {
        if (openTimer) {
          clearTimeout(openTimer)
          openTimer = null
        }
        if (!open || closeTimer) return
        if (closeDelay <= 0) {
          setOpen(false)
          return
        }
        closeTimer = setTimeout(() => {
          closeTimer = null
          setOpen(false)
        }, closeDelay)
      }
    
      function cancelClose() {
        if (closeTimer) {
          clearTimeout(closeTimer)
          closeTimer = null
        }
      }
    
      onDestroy(() => {
        if (openTimer) clearTimeout(openTimer)
        if (closeTimer) clearTimeout(closeTimer)
      })
    
      setContext<HoverCardContext>(HOVER_CARD_CONTEXT_KEY, {
        get open() {
          return open
        },
        scheduleOpen,
        scheduleClose,
        cancelClose,
      })
    </script>
    
    <div
      data-uipkge
      data-slot="hover-card"
      data-state={open ? 'open' : 'closed'}
      {...restProps}
      class={cn('relative inline-block', className)}
    >
      {@render children?.()}
    </div>
    
  • src/lib/components/ui/hover-card/HoverCardContent.svelte2.9 kB
  • src/lib/components/ui/hover-card/HoverCardTrigger.svelte0.8 kB
  • src/lib/components/ui/hover-card/index.ts0.3 kB

Raw manifest:https://uipkge.dev/r/svelte/hover-card.json