Frontend Lab
← 回到所有模式
PB09🪟 Overlay · 覆蓋層預覽

Hover Card

懸停卡片

難度 ★★★★★標籤 hover · card · profile3 min read

模式簡述

Hover Card:類似 link preview,但通常用在「人」或「實體」的快速資訊卡:頭像、@提及、tag。

E

Ephraim

@ephraim · 接案工程師

專做品牌官網與後台介面,主玩 Next.js + Cloudflare。

128 追蹤者42 專案
懸停|顯示用戶卡片

Claude Code Prompt

💬 Claude Code Prompt
參考:PB09 hover-card
組件:HoverCard.tsx

Hover Card:類似 link preview,但通常用在「人」或「實體」的快速資訊卡:頭像、@提及、tag。

限制:
- 沒做 600ms 緩衝
- 卡片內容太多
- 卡片離開 trigger 太遠

完整程式碼

HoverCard.tsx
"use client";
import { useEffect, useState } from "react";

type State = "hover" | "loading";

export function HoverCard({ state = "hover" }: { state?: State }) {
const [hover, setHover] = useState(true);
useEffect(() => setHover(true), [state]);
const loading = state === "loading";

return (
  <div className="grid h-[280px] w-full max-w-md place-items-center">
    <div className="relative">
      <button
        onMouseEnter={() => setHover(true)}
        onMouseLeave={() => setHover(false)}
        className="inline-flex items-center gap-2 rounded-md border border-border bg-background/50 px-3 py-1.5 text-xs hover:border-lab-accent"
      >
        <span className="flex h-5 w-5 items-center justify-center rounded-full bg-lab-accent/20 font-mono text-[10px] text-lab-accent">
          E
        </span>
        @ephraim
      </button>
      {hover && (
        <div className="absolute left-1/2 top-full z-10 mt-2 w-64 -translate-x-1/2 rounded-lg border border-border bg-background p-3 shadow-xl animate-in fade-in zoom-in-95 duration-150">
          {loading ? (
            <div className="space-y-2">
              <div className="flex gap-2">
                <div className="h-10 w-10 animate-pulse rounded-full bg-muted" />
                <div className="flex-1 space-y-1.5">
                  <div className="h-3 w-20 animate-pulse rounded bg-muted" />
                  <div className="h-2.5 w-28 animate-pulse rounded bg-muted" />
                </div>
              </div>
              <div className="h-2.5 w-full animate-pulse rounded bg-muted" />
              <div className="h-2.5 w-2/3 animate-pulse rounded bg-muted" />
            </div>
          ) : (
            <>
              <div className="flex items-start gap-3">
                <div className="flex h-10 w-10 items-center justify-center rounded-full bg-lab-accent/20 font-mono text-sm text-lab-accent">
                  E
                </div>
                <div>
                  <p className="text-sm font-medium">Ephraim</p>
                  <p className="font-mono text-[11px] text-muted-foreground">@ephraim · 接案工程師</p>
                </div>
              </div>
              <p
                className="mt-2.5 text-xs text-muted-foreground"
                style={{ fontFamily: "var(--font-zh)" }}
              >
                專做品牌官網與後台介面,主玩 Next.js + Cloudflare。
              </p>
              <div className="mt-2.5 flex gap-3 font-mono text-[11px] text-muted-foreground">
                <span>
                  <span className="text-foreground">128</span> 追蹤者
                </span>
                <span>
                  <span className="text-foreground">42</span> 專案
                </span>
              </div>
            </>
          )}
        </div>
      )}
    </div>
  </div>
);
}

何時用

  • ✅ @username 提及的個人卡片
  • ✅ tag 的描述與相關項目
  • ✅ GitHub 風格的 commit author 預覽

何時別用

  • ❌ 手機優先的介面
  • ❌ 卡片內有複雜互動(用 popover)
  • ❌ 純文字提示(用 tooltip)

我踩過的坑

  1. 沒做 600ms 緩衝:滑鼠移出又移回不該關閉。用 framer-motion 加 delay。

  2. 卡片內容太多:超過 3-4 行就太擠,改用 popover 或新分頁。

  3. 卡片離開 trigger 太遠:使用者要把滑鼠移過空白才到卡片,途中關了。要做 safe triangle。

相關模式

  • PB04
  • PB08

搜尋

按 ⌘K 隨時開啟