PhiaUi.Components.HoverCard (phia_ui v0.1.17)

Copy Markdown View Source

HoverCard component — a rich floating panel revealed on hover with configurable delays.

A HoverCard is used to show a preview or supplementary information about a linked element when the user hovers over it. It is more powerful than a Tooltip because it can contain structured content — avatars, dates, descriptions, action links — and it has independent open and close delays so users can move into the panel without it disappearing.

Unlike a Popover, a HoverCard is triggered by hover (not click) and carries role="tooltip" semantics — it is supplementary information, not a required step in a workflow.

Typical Use Cases

  • User profile previews on @mention links in chat or comments
  • Repository/project summaries on hover in a dashboard list
  • Link preview cards (URL, title, description) in rich text
  • Product card previews in an e-commerce catalogue

The PhiaHoverCard hook manages:

  • Configurable open delay (prevents flash on quick cursor movement)
  • Configurable close delay (allows the user to move cursor into the card)
  • Smart viewport positioning and edge flipping
  • Mouse and focus event handling

Sub-components

FunctionElementPurpose
hover_card/1divRoot container — hook mount point with delay attrs
hover_card_trigger/1spanHover area with aria-describedby
hover_card_content/1divFloating panel with role="tooltip"

Hook Setup

Copy the hook via mix phia.add hover_card, then register it in app.js:

# assets/js/app.js
import PhiaHoverCard from "./hooks/hover_card"

let liveSocket = new LiveSocket("/live", Socket, {
  hooks: { PhiaHoverCard }
})

Basic Example — User Profile Preview

The canonical use case: hovering a username shows a brief profile card.

<.hover_card id="user-card-42">
  <.hover_card_trigger hover_card_id="user-card-42">
    <.link href="/users/janedoe" class="font-medium hover:underline">
      @janedoe
    </.link>
  </.hover_card_trigger>
  <.hover_card_content hover_card_id="user-card-42">
    <div class="flex gap-3">
      <.avatar src={@user.avatar_url} alt={@user.name} />
      <div>
        <p class="font-semibold">{@user.name}</p>
        <p class="text-sm text-muted-foreground">@{@user.handle}</p>
        <p class="text-sm text-muted-foreground mt-1">
          Joined {Calendar.strftime(@user.joined_at, "%B %Y")}
        </p>
      </div>
    </div>
    <p class="mt-3 text-sm">{@user.bio}</p>
  </.hover_card_content>
</.hover_card>

Example — Repository Preview

Show project metadata on hover in a list view:

<.hover_card id="repo-card-phiaui" open_delay={400} close_delay={150}>
  <.hover_card_trigger hover_card_id="repo-card-phiaui">
    <.link href="/repos/phiaui">{@repo.name}</.link>
  </.hover_card_trigger>
  <.hover_card_content hover_card_id="repo-card-phiaui" side="right">
    <p class="font-semibold">{@repo.name}</p>
    <p class="text-sm text-muted-foreground">{@repo.description}</p>
    <div class="flex gap-4 mt-2 text-xs text-muted-foreground">
      <span> {@repo.stars}</span>
      <span>🍴 {@repo.forks}</span>
      <span>{@repo.language}</span>
    </div>
  </.hover_card_content>
</.hover_card>

Example — Custom Positioning

Use side="top" when the trigger is near the bottom of the viewport:

<.hover_card id="bottom-card" side="top" open_delay={200} close_delay={100}>
  <.hover_card_trigger hover_card_id="bottom-card">
    <span class="cursor-help underline decoration-dotted">Learn more</span>
  </.hover_card_trigger>
  <.hover_card_content hover_card_id="bottom-card" side="top">
    <p class="text-sm">Detailed explanation of this feature.</p>
  </.hover_card_content>
</.hover_card>

Side Values

ValuePanel appears...
"bottom"Below the trigger (default)
"top"Above the trigger
"left"To the left of the trigger
"right"To the right of the trigger

The hook may flip to the opposite side if the preferred side would overflow the viewport.

Delay Tuning

Use CaseRecommended Delays
Dense lists, quick scanningopen_delay={500} close_delay={150}
Important previews (profile cards)open_delay={300} close_delay={200}
Instant feedback (tight interaction)open_delay={100} close_delay={100}

Accessibility

  • hover_card_trigger/1 sets aria-describedby pointing at the content panel, so screen readers announce the card content when focus lands on the trigger (keyboard users receive the same information as mouse users)
  • hover_card_content/1 uses role="tooltip" — supplementary, non-interactive
  • If your card contains interactive elements (links, buttons), consider using popover/1 instead, which has role="dialog" and a proper focus trap

Summary

Functions

Renders the hover card root container and attaches the PhiaHoverCard hook.

Renders the floating hover card content panel.

Renders the hover trigger wrapper.

Functions

hover_card(assigns)

Renders the hover card root container and attaches the PhiaHoverCard hook.

The data-trigger="hover", data-open-delay, data-close-delay, and data-side attributes pass configuration to the JS hook. The hook reads these on mount to configure its event listeners and positioning logic.

Attributes

  • id (:string) (required) - Unique element ID — the hook mount point. Sub-components derive their IDs from this value. Use a unique value per card instance when rendering in a list (e.g. "user-card-#{user.id}").

  • open_delay (:integer) - Milliseconds to wait after mouseenter before showing the card. A moderate delay (300–500ms) avoids flashing the card as the user's cursor passes through the trigger on the way to something else. Set lower for tighter interactions.

    Defaults to 300.

  • close_delay (:integer) - Milliseconds to wait after mouseleave before hiding the card. A close delay (150–300ms) gives the user time to move their cursor from the trigger into the floating card without it disappearing mid-movement.

    Defaults to 200.

  • side (:string) - Preferred side for the floating panel. The hook may flip this if needed. Defaults to "bottom". Must be one of "top", "bottom", "left", or "right".

  • class (:string) - Additional CSS classes for the container. Defaults to nil.

  • Global attributes are accepted. Extra HTML attributes forwarded to the root <div>.

Slots

hover_card_content(assigns)

Renders the floating hover card content panel.

Hidden by default via display: none (the hidden class). The PhiaHoverCard hook removes this class after the open_delay elapses and positions the panel relative to the trigger using getBoundingClientRect.

Uses semantic Tailwind tokens (bg-popover, text-popover-foreground, border-border) so the panel automatically respects dark mode and custom theme presets without any additional CSS.

The data-hover-card-content attribute is the hook's selector for the element to show/hide. The data-side attribute tells the hook which edge to compute the initial offset from.

Attributes

  • hover_card_id (:string) (required) - ID of the parent hover_card/1 — used to build the panel's element id.

  • side (:string) - Preferred display side for this content panel. Takes precedence over the side set on the parent hover_card/1. Use this override when the container and content panel should logically have different preferred sides.

    Defaults to "bottom". Must be one of "top", "bottom", "left", or "right".

  • class (:string) - Additional CSS classes for the content panel. Defaults to nil.

  • Global attributes are accepted. Extra HTML attributes forwarded to the panel <div>.

Slots

  • inner_block (required) - Hover card body. Can contain structured content — avatars, descriptions, stats, inline links. Keep the layout compact and relevant. Note: if you need the user to interact with buttons or forms in the card, use popover/1 instead, which has a focus trap.

hover_card_trigger(assigns)

Renders the hover trigger wrapper.

Sets aria-describedby pointing at "{hover_card_id}-content". Screen readers will announce the card content when the user focuses the trigger — keyboard users receive the same information as mouse users.

The data-hover-card-trigger attribute is the hook's selector for attaching mouse and focus event listeners.

Attributes

  • hover_card_id (:string) (required) - ID of the parent hover_card/1 — used to build aria-describedby.
  • class (:string) - Additional CSS classes. Defaults to nil.
  • Global attributes are accepted. Extra HTML attributes forwarded to the wrapper <span>.

Slots

  • inner_block (required) - The element that reveals the hover card. Typically an <a> link, a user handle, or any inline element. The hook attaches mouseenter/mouseleave listeners to data-hover-card-trigger.