来源：https://ui.docs.xihanfun.com/components/hover-card

# HoverCard 悬浮卡片

指针停留后出现的信息卡：预览一个对象，不打断当前操作。

<div class="xh-resource-links">
  <a href="https://github.com/XiHanFun/XiHan.UI/tree/dev/ui/packages/engine/headless/src/hover-card" target="_blank" rel="noreferrer">Headless</a>
  <a href="https://github.com/XiHanFun/XiHan.UI/blob/dev/ui/packages/design/styles/css/hover-card.css" target="_blank" rel="noreferrer">Styles</a>
  <a href="https://github.com/XiHanFun/XiHan.UI/tree/dev/ui/packages/adapters/vue/src/components/hover-card" target="_blank" rel="noreferrer">Vue</a>
  <a href="https://github.com/XiHanFun/XiHan.UI/tree/dev/ui/packages/adapters/react/src/components/hover-card" target="_blank" rel="noreferrer">React</a>
  <a href="https://github.com/XiHanFun/XiHan.UI/blob/dev/ui/packages/adapters/web-components/src/elements/hover-card.ts" target="_blank" rel="noreferrer">Web Components</a>
</div>

## 用法

与 Tooltip 的分界在于卡片本体可交互：指针停在卡片上不收起，其中的链接与按钮都可点击

```vue
<script setup lang="ts">
import {
  XhButton,
  XhHoverCardArrow,
  XhHoverCardContent,
  XhHoverCardDescription,
  XhHoverCardPositioner,
  XhHoverCardRoot,
  XhHoverCardTitle,
  XhHoverCardTrigger,
} from "@xihan-ui/vue";
import { ref } from "vue";

const following = ref(false);
</script>

<template>
  <div>
    最近这批组件由
    <XhHoverCardRoot placement="bottom-start">
      <XhHoverCardTrigger>@xihan</XhHoverCardTrigger>
      <XhHoverCardPositioner>
        <XhHoverCardContent>
          <XhHoverCardArrow />
          <XhHoverCardTitle>XiHan.UI</XhHoverCardTitle>
          <XhHoverCardDescription>
            框架无关的设计系统运行时，Vue 与 Web Components 共用同一套无头内核。
          </XhHoverCardDescription>
          <XhButton size="sm" variant="outline" @click="following = !following">
            {{ following ? "已关注" : "关注" }}
          </XhButton>
        </XhHoverCardContent>
      </XhHoverCardPositioner>
    </XhHoverCardRoot>
    推上来。
  </div>
</template>
```

```html
<div>
  最近这批组件由
  <xh-hover-card placement="bottom-start">
    <div data-xh-part="root">
      <button data-xh-part="trigger">@xihan</button>
      <div data-xh-part="positioner">
        <div data-xh-part="content">
          <div data-xh-part="arrow"></div>
          <h2 data-xh-part="title">XiHan.UI</h2>
          <p data-xh-part="description">
            框架无关的设计系统运行时，Vue 与 Web Components 共用同一套无头内核。
          </p>
          <xh-button size="sm" variant="outline">
            <button data-xh-part="root" id="hover-card-basic-follow">
              关注
            </button>
          </xh-button>
        </div>
      </div>
    </div>
  </xh-hover-card>
  推上来。
</div>

<script type="module">
  // 卡片里的按钮点得到：在关注与已关注之间切换
  const follow = document.getElementById("hover-card-basic-follow");
  follow.addEventListener("click", () => {
    follow.textContent = follow.textContent === "关注" ? "已关注" : "关注";
  });
</script>
```

## 组件结构

加粗的是必需部件。

`data-scope="hover-card"`：`root` · **`trigger`** · `positioner` · **`content`** · `title` · `description` · `arrow`

## 示例

### 延时

openDelay 默认 700ms，closeDelay 默认 300ms：收起等待正是留给指针从触发器移动到卡片上的通行时间

```vue
<script setup lang="ts">
import {
  XhHoverCardArrow,
  XhHoverCardContent,
  XhHoverCardPositioner,
  XhHoverCardRoot,
  XhHoverCardTrigger,
} from "@xihan-ui/vue";
</script>

<template>
  <div style="display: flex; flex-wrap: wrap; gap: 24px">
    <XhHoverCardRoot placement="bottom-start">
      <XhHoverCardTrigger>默认（700 / 300）</XhHoverCardTrigger>
      <XhHoverCardPositioner>
        <XhHoverCardContent>
          <XhHoverCardArrow />
          <span>停够 700ms 才展开，指针移开 300ms 才收起。</span>
        </XhHoverCardContent>
      </XhHoverCardPositioner>
    </XhHoverCardRoot>

    <XhHoverCardRoot placement="bottom-start" :open-delay="0" :close-delay="800">
      <XhHoverCardTrigger>快开慢收（0 / 800）</XhHoverCardTrigger>
      <XhHoverCardPositioner>
        <XhHoverCardContent>
          <XhHoverCardArrow />
          <span>指针一进就展开，移开后还留 800ms 给你走回来。</span>
        </XhHoverCardContent>
      </XhHoverCardPositioner>
    </XhHoverCardRoot>
  </div>
</template>
```

```html
<div style="display: flex; flex-wrap: wrap; gap: 24px">
  <xh-hover-card placement="bottom-start">
    <div data-xh-part="root">
      <button data-xh-part="trigger">默认（700 / 300）</button>
      <div data-xh-part="positioner">
        <div data-xh-part="content">
          <div data-xh-part="arrow"></div>
          <span>停够 700ms 才展开，指针移开 300ms 才收起。</span>
        </div>
      </div>
    </div>
  </xh-hover-card>

  <xh-hover-card placement="bottom-start" open-delay="0" close-delay="800">
    <div data-xh-part="root">
      <button data-xh-part="trigger">快开慢收（0 / 800）</button>
      <div data-xh-part="positioner">
        <div data-xh-part="content">
          <div data-xh-part="arrow"></div>
          <span>指针一进就展开，移开后还留 800ms 给你走回来。</span>
        </div>
      </div>
    </div>
  </xh-hover-card>
</div>
```

### 受控

传入 open 后由宿主决定；悬停与 Escape 都只发意图，最终是否写回由外部按钮共用的同一份状态决定

```vue
<script setup lang="ts">
import {
  XhButton,
  XhHoverCardArrow,
  XhHoverCardContent,
  XhHoverCardPositioner,
  XhHoverCardRoot,
  XhHoverCardTrigger,
} from "@xihan-ui/vue";
import { ref } from "vue";

const open = ref(false);
</script>

<template>
  <div style="display: flex; align-items: center; gap: 16px">
    <XhHoverCardRoot v-model:open="open" placement="bottom-start">
      <XhHoverCardTrigger>@xihan</XhHoverCardTrigger>
      <XhHoverCardPositioner>
        <XhHoverCardContent>
          <XhHoverCardArrow />
          <strong>XiHan.UI</strong>
          <span>卡片从不抢焦点，也不锁页面滚动。</span>
        </XhHoverCardContent>
      </XhHoverCardPositioner>
    </XhHoverCardRoot>

    <XhButton variant="outline" @click="open = !open">
      {{ open ? "收起" : "展开" }}
    </XhButton>
    <span>当前：{{ open ? "展开" : "收起" }}</span>
  </div>
</template>
```

```html
<div style="display: flex; align-items: center; gap: 16px">
  <xh-hover-card id="hover-card-controlled" open="false" placement="bottom-start">
    <div data-xh-part="root">
      <button data-xh-part="trigger">@xihan</button>
      <div data-xh-part="positioner">
        <div data-xh-part="content">
          <div data-xh-part="arrow"></div>
          <strong>XiHan.UI</strong>
          <span>卡片从不抢焦点，也不锁页面滚动。</span>
        </div>
      </div>
    </div>
  </xh-hover-card>

  <xh-button id="hover-card-controlled-toggle" variant="outline">
    <button data-xh-part="root">展开</button>
  </xh-button>
  <span>当前：<span id="hover-card-controlled-state">收起</span></span>
</div>

<script type="module">
  // 展开态由这段脚本持有：组件只发意图，写回 open 才真的展开
  const card = document.getElementById("hover-card-controlled");
  const toggle = document.getElementById("hover-card-controlled-toggle");
  const label = toggle.querySelector('[data-xh-part="root"]');
  const readout = document.getElementById("hover-card-controlled-state");

  function apply(open) {
    card.open = open;
    label.textContent = open ? "收起" : "展开";
    readout.textContent = open ? "展开" : "收起";
  }

  toggle.addEventListener("click", () => apply(!card.open));
  card.addEventListener("open-change", (event) => apply(event.detail.open));
</script>
```

### 尺寸

三档改变卡片的内边距与字号，不写 size 即默认档；把指针停在触发器上查看差别

```vue
<script setup lang="ts">
import {
  XhHoverCardArrow,
  XhHoverCardContent,
  XhHoverCardPositioner,
  XhHoverCardRoot,
  XhHoverCardTrigger,
} from "@xihan-ui/vue";

const sizes = [
  { value: "sm", label: "小" },
  { value: undefined, label: "缺省" },
  { value: "lg", label: "大" },
] as const;
</script>

<template>
  <div style="display: flex; flex-wrap: wrap; gap: 24px">
    <XhHoverCardRoot
      v-for="s in sizes"
      :key="s.label"
      :size="s.value"
      placement="bottom-start"
      :open-delay="0"
    >
      <XhHoverCardTrigger>{{ s.label }}</XhHoverCardTrigger>
      <XhHoverCardPositioner>
        <XhHoverCardContent>
          <XhHoverCardArrow />
          <strong>{{ s.label }}档</strong>
          <span>size = {{ s.value ?? "未指定" }}。</span>
        </XhHoverCardContent>
      </XhHoverCardPositioner>
    </XhHoverCardRoot>
  </div>
</template>
```

```html
<div style="display: flex; flex-wrap: wrap; gap: 24px">
  <xh-hover-card size="sm" placement="bottom-start" open-delay="0">
    <div data-xh-part="root">
      <button data-xh-part="trigger">小</button>
      <div data-xh-part="positioner">
        <div data-xh-part="content">
          <div data-xh-part="arrow"></div>
          <strong>小档</strong>
          <span>size = sm。</span>
        </div>
      </div>
    </div>
  </xh-hover-card>

  <xh-hover-card placement="bottom-start" open-delay="0">
    <div data-xh-part="root">
      <button data-xh-part="trigger">缺省</button>
      <div data-xh-part="positioner">
        <div data-xh-part="content">
          <div data-xh-part="arrow"></div>
          <strong>缺省档</strong>
          <span>size = 未指定。</span>
        </div>
      </div>
    </div>
  </xh-hover-card>

  <xh-hover-card size="lg" placement="bottom-start" open-delay="0">
    <div data-xh-part="root">
      <button data-xh-part="trigger">大</button>
      <div data-xh-part="positioner">
        <div data-xh-part="content">
          <div data-xh-part="arrow"></div>
          <strong>大档</strong>
          <span>size = lg。</span>
        </div>
      </div>
    </div>
  </xh-hover-card>
</div>
```

### 朝向与间距

placement 是请求值，空间不足时定位引擎会自动翻面；offset 调整卡片与触发器的距离

```vue
<script setup lang="ts">
import {
  XhHoverCardArrow,
  XhHoverCardContent,
  XhHoverCardPositioner,
  XhHoverCardRoot,
  XhHoverCardTrigger,
} from "@xihan-ui/vue";

const cases = [
  { placement: "top", offset: 8, label: "上方" },
  { placement: "right", offset: 8, label: "右侧" },
  { placement: "bottom-end", offset: 20, label: "下方靠尾（间距 20）" },
] as const;
</script>

<template>
  <div style="display: flex; flex-wrap: wrap; gap: 24px">
    <XhHoverCardRoot
      v-for="c in cases"
      :key="c.placement"
      :placement="c.placement"
      :offset="c.offset"
      :open-delay="0"
    >
      <XhHoverCardTrigger>{{ c.label }}</XhHoverCardTrigger>
      <XhHoverCardPositioner>
        <XhHoverCardContent>
          <XhHoverCardArrow />
          <strong>{{ c.label }}</strong>
          <span>请求的朝向是 {{ c.placement }}，间距 {{ c.offset }}px。</span>
        </XhHoverCardContent>
      </XhHoverCardPositioner>
    </XhHoverCardRoot>
  </div>
</template>
```

```html
<div style="display: flex; flex-wrap: wrap; gap: 24px">
  <xh-hover-card placement="top" offset="8" open-delay="0">
    <div data-xh-part="root">
      <button data-xh-part="trigger">上方</button>
      <div data-xh-part="positioner">
        <div data-xh-part="content">
          <div data-xh-part="arrow"></div>
          <strong>上方</strong>
          <span>请求的朝向是 top，间距 8px。</span>
        </div>
      </div>
    </div>
  </xh-hover-card>

  <xh-hover-card placement="right" offset="8" open-delay="0">
    <div data-xh-part="root">
      <button data-xh-part="trigger">右侧</button>
      <div data-xh-part="positioner">
        <div data-xh-part="content">
          <div data-xh-part="arrow"></div>
          <strong>右侧</strong>
          <span>请求的朝向是 right，间距 8px。</span>
        </div>
      </div>
    </div>
  </xh-hover-card>

  <xh-hover-card placement="bottom-end" offset="20" open-delay="0">
    <div data-xh-part="root">
      <button data-xh-part="trigger">下方靠尾（间距 20）</button>
      <div data-xh-part="positioner">
        <div data-xh-part="content">
          <div data-xh-part="arrow"></div>
          <strong>下方靠尾（间距 20）</strong>
          <span>请求的朝向是 bottom-end，间距 20px。</span>
        </div>
      </div>
    </div>
  </xh-hover-card>
</div>
```

### 禁用

disabled 只关闭卡片本身，触发器照常可点击、可聚焦，也照常不进入展开等待

```vue
<script setup lang="ts">
import {
  XhHoverCardArrow,
  XhHoverCardContent,
  XhHoverCardPositioner,
  XhHoverCardRoot,
  XhHoverCardTrigger,
} from "@xihan-ui/vue";
import { ref } from "vue";

const clicks = ref(0);
</script>

<template>
  <div style="display: flex; align-items: center; gap: 16px">
    <XhHoverCardRoot disabled placement="bottom-start" :open-delay="0">
      <XhHoverCardTrigger @click="clicks++">@xihan（卡片已关）</XhHoverCardTrigger>
      <XhHoverCardPositioner>
        <XhHoverCardContent>
          <XhHoverCardArrow />
          <span>这张卡片不会出现。</span>
        </XhHoverCardContent>
      </XhHoverCardPositioner>
    </XhHoverCardRoot>
    <span>已点 {{ clicks }} 次</span>
  </div>
</template>
```

```html
<div style="display: flex; align-items: center; gap: 16px">
  <xh-hover-card id="hover-card-disabled" disabled placement="bottom-start" open-delay="0">
    <div data-xh-part="root">
      <button data-xh-part="trigger">@xihan（卡片已关）</button>
      <div data-xh-part="positioner">
        <div data-xh-part="content">
          <div data-xh-part="arrow"></div>
          <span>这张卡片不会出现。</span>
        </div>
      </div>
    </div>
  </xh-hover-card>
  <span>已点 <span id="hover-card-disabled-count">0</span> 次</span>
</div>

<script type="module">
  // 触发器照样可点，计数落在后面那行文字上
  const card = document.getElementById("hover-card-disabled");
  const trigger = card.querySelector('[data-xh-part="trigger"]');
  const readout = document.getElementById("hover-card-disabled-count");
  let count = 0;
  trigger.addEventListener("click", () => {
    count += 1;
    readout.textContent = String(count);
  });
</script>
```

## 设计指引

### 何时使用

- 链接或头像的预览：用户资料、文档摘要、商品简介。
- 信息属于顺带查看的内容，不需要专门点击。

### 何时不用

- 内容需要交互（按钮、表单）时，使用[气泡卡片](./popover)。
- 只有一句文字时，使用[文字提示](./tooltip)。
- 触摸端是主要场景。

### 特性

- `openDelay` 与 `closeDelay` 成对：进入需要停留、离开有宽限，指针斜向移到卡片上不会误收。
- 开合状态可受控。
- 内容与 Popover 共用 M2 磨砂面：单层背景模糊、柔和顶光和浮层阴影，正文保持不透明。箭头只复用底色与边界，不叠加模糊；减少透明、高对比与强制色偏好由材质令牌统一响应。`--xh-hover-card-backdrop` 可覆盖模糊配方；打印时整块预览收起。

### 组合

- 触发器通常是[头像](./avatar)或链接；卡片内使用[卡片](./card)式的排版。

### 最佳实践

- 打开延时设为几百毫秒，否则指针扫过一段文字会弹出一串卡片。
- 卡片内的信息在其他位置也应有正式入口。

### 反模式

- 卡片内放操作按钮：指针移动过去的途中可能已经关闭。
- 延时为 0。

## API 参考

### 产物

| 层 | 值 |
| --- | --- |
| 自定义元素 | `<xh-hover-card>` |
| Vue 组件 | `XhHoverCardArrow` `XhHoverCardContent` `XhHoverCardDescription` `XhHoverCardPositioner` `XhHoverCardRoot` `XhHoverCardTitle` `XhHoverCardTrigger` |
| 组合式函数 | `useHoverCard` |
| 状态机 | `hoverCardMachine` |
| 皮肤 | `@xihan-ui/styles/hover-card.css` |

### Props

| 属性 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `open` | `boolean` |  |  |
| `defaultOpen` | `boolean` |  |  |
| `placement` | `Placement` |  | 请求的浮层朝向，默认 bottom；空间不足时由定位引擎避让。 |
| `offset` | `number` |  | 浮层与锚点的间距（px）。 |
| `openDelay` | `number` |  | 悬停进入到展开的等待毫秒，默认 700。 |
| `closeDelay` | `number` |  | 指针离开 trigger 或 content 到收起的等待毫秒，默认 300。 |
| `dir` | `Direction` |  | 文字方向，仅在显式提供时写到根节点上。 |
| `disabled` | `boolean` |  | 只关闭卡片本身，不影响 trigger 元素自身的可用性。 |
| `size` | `Size` |  | 尺寸：sm / md / lg，决定卡片的内边距档位。 |
| `onOpenChange` | `(details: HoverCardOpenChangeDetails) => void` |  | open 变化意图回调。 |

### 事件

自定义元素将载荷放在 `detail`；Vue 使用同名 emit。

| 事件 | 载荷 | 说明 |
| --- | --- | --- |
| `open-change` | `HoverCardOpenChangeDetails` | open 状态变化；detail 为 `{ open: boolean }` |

### 插槽

仅列出带载荷的插槽。

| Vue 组件 | 插槽 | 载荷 | 说明 |
| --- | --- | --- | --- |
| `XhHoverCardRoot` | `default` | `HoverCardRootSlotProps` |  |

### React 适配器 props

只列各组件自己声明的那些：继承自 `ComponentPropsWithRef` 的 DOM 属性不在其中，根组件上与上面 Props 表同名的也不重复列。Vue 的对应物是上面的插槽表。

| React 组件 | 属性 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `XhHoverCardPositioner` | `container` | `() => Element \| null` |  | 浮层挂载的容器；未提供时按全局配置，再未提供时挂载到 body。 |
| `XhHoverCardRoot` | `children` | `SlotChildren<HoverCardRootSlotProps>` |  |  |

### 状态

公开状态写入 `data-state`。

| 部件 | 取值 |
| --- | --- |
| `root` | 'open' \| 'closed' |
| `trigger` | 'open' \| 'closed' |
| `positioner` | 'open' \| 'closed' |
| `content` | 'open' \| 'closed' |

以下名称仅用于内部状态机。

**状态**：`closed` · `opening` · `visible` · `visible.open` · `visible.closing`

**事件**：`POINTER.ENTER` · `POINTER.LEAVE` · `FOCUS` · `BLUR` · `ESCAPE` · `OPEN` · `CLOSE` · `after.openDelay` · `after.closeDelay` · `CONTROLLED.OPEN` · `CONTROLLED.CLOSE`

**判据**：`isOpenControlled` · `isDisabled` · `isFocusHeld`

### connect API

`getXxxProps()` 返回对应部件的宿主属性。

| 成员 | 类型 | 说明 |
| --- | --- | --- |
| `open` | `boolean` |  |
| `setOpen` | `(next: boolean) => void` |  |
| `getRootProps` | `() => T['element']` |  |
| `getTriggerProps` | `() => T['button']` |  |
| `getPositionerProps` | `() => T['element']` |  |
| `getContentProps` | `() => T['element']` |  |
| `getTitleProps` | `() => T['element']` |  |
| `getDescriptionProps` | `() => T['element']` |  |
| `getArrowProps` | `() => T['element']` |  |

## 无障碍

### 键盘

规格出处：[W3C APG](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/#keyboardinteraction)

| 按键 | 生效条件 | 行为 |
| --- | --- | --- |
| `Tab` / `Shift+Tab` | not disabled | 焦点进入 trigger 立即展开、离开卡片即收起，都不走延时 |
| `Escape` | 浮层可见（含收起等待期） | 立即收起，不等 closeDelay |

### ARIA

以下属性由 `connect` 生成。

| 部件 | 属性 | 值 |
| --- | --- | --- |
| `trigger` | `aria-controls` | `content` 部件的 id |
| `trigger` | `aria-expanded` | 'true' \| 'false' |
| `trigger` | `aria-haspopup` | 'dialog' |
| `content` | `aria-describedby` | `description` 部件的 id \| undefined |
| `content` | `aria-hidden` | !open \|\| undefined |
| `content` | `aria-labelledby` | `title` 部件的 id \| `trigger` 部件的 id |
| `content` | `aria-modal` | 'false' |
| `content` | `role` | 'dialog' |
| `arrow` | `aria-hidden` | 'true' |

## 样式参考

### 皮肤

`@xihan-ui/styles/hover-card.css` 使用 `[data-scope="hover-card"][data-part="root"]` 部件选择器，位于 `xihan.components` 层。覆盖样式使用 `xihan.overrides`。

`forced-colors: active` 下另有一套规则：颜色交给系统，边框与状态标记改用系统色关键字。

### 数据属性

由 `connect` 生成；条件不成立时不输出无值属性。

| 部件 | 属性 | 值 |
| --- | --- | --- |
| `root` | `data-disabled` | ''（条件成立时才出现） |
| `root` | `data-state` | 'open' \| 'closed' |
| `trigger` | `data-disabled` | ''（条件成立时才出现） |
| `trigger` | `data-state` | 'open' \| 'closed' |
| `positioner` | `data-hidden` | ''（条件成立时才出现） |
| `positioner` | `data-placement` | 定位引擎算出的实际落位 |
| `positioner` | `data-positioned` | ''（条件成立时才出现） |
| `positioner` | `data-state` | 'open' \| 'closed' |
| `content` | `data-placement` | 定位引擎算出的实际落位 |
| `content` | `data-size` | props.size |
| `content` | `data-state` | 'open' \| 'closed' |
| `content` | `data-xh-material` | 'frosted' |
| `arrow` | `data-placement` | 定位引擎算出的实际落位 |

<!-- xh-component-tokens:start -->
### CSS 变量

本组件公开覆盖槽由独立皮肤的实际消费位生成；默认来源、作用部件和状态均与 CSS 同源。

| 变量 | 部件 | CSS 属性 | 状态 | 默认来源 | 说明 |
| --- | --- | --- | --- | --- | --- |
| `--xh-hover-card-arrow-size` | `arrow` | `--xh-_overlay-arrow-size` | `default` | `--xh-overlay-arrow-size` | hover-card 的 arrow 部件 --xh-_overlay-arrow-size 覆盖槽。 |
| `--xh-hover-card-backdrop` | `content` | `-webkit-backdrop-filter`<br>`backdrop-filter` | `xh-material=frosted` | `--xh-_material-backdrop` | hover-card 的 content 部件 -webkit-backdrop-filter、backdrop-filter 覆盖槽。 |
| `--xh-hover-card-bg` | `arrow`<br>`content` | `background` | `default`<br>`not([data-xh-action-control])`<br>`xh-material=frosted` | `--xh-_material-bg`<br>`--xh-material-frosted-bg` | hover-card 的 arrow、content 部件 background 覆盖槽。 |
| `--xh-hover-card-border` | `arrow`<br>`content` | `border` | `default`<br>`not([data-xh-action-control])`<br>`xh-material=frosted` | `--xh-_material-border`<br>`--xh-material-frosted-border` | hover-card 的 arrow、content 部件 border 覆盖槽。 |
| `--xh-hover-card-description-fg` | `description` | `color` | `default` | `--xh-fg-muted` | hover-card 的 description 部件 color 覆盖槽。 |
| `--xh-hover-card-description-font-size` | `description` | `font-size` | `default` | `--xh-text-secondary-size` | hover-card 的 description 部件 font-size 覆盖槽。 |
| `--xh-hover-card-fg` | `content` | `color` | `not([data-xh-action-control])`<br>`xh-material=frosted` | `--xh-_material-fg` | hover-card 的 content 部件 color 覆盖槽。 |
| `--xh-hover-card-gap` | `content` | `gap` | `default` | `--xh-space-2` | hover-card 的 content 部件 gap 覆盖槽。 |
| `--xh-hover-card-layer` | `positioner` | `z-index` | `default` | `--xh-_layer` | hover-card 的 positioner 部件 z-index 覆盖槽。 |
| `--xh-hover-card-max-h` | `content` | `max-block-size` | `default` | `--xh-overlay-max-h` | hover-card 的 content 部件 max-block-size 覆盖槽。 |
| `--xh-hover-card-max-w` | `content` | `max-inline-size` | `default` | `--xh-_hover-card-max-w` | hover-card 的 content 部件 max-inline-size 覆盖槽。 |
| `--xh-hover-card-px` | `content` | `padding-inline` | `default` | `--xh-_hover-card-pad` | hover-card 的 content 部件 padding-inline 覆盖槽。 |
| `--xh-hover-card-py` | `content` | `padding-block` | `default` | `--xh-_hover-card-pad` | hover-card 的 content 部件 padding-block 覆盖槽。 |
| `--xh-hover-card-radius` | `content` | `border-radius` | `default` | `--xh-shape-overlay` | hover-card 的 content 部件 border-radius 覆盖槽。 |
| `--xh-hover-card-shadow` | `content` | `box-shadow` | `not([data-xh-action-control])`<br>`xh-material=frosted` | `--xh-_material-shadow` | hover-card 的 content 部件 box-shadow 覆盖槽。 |
| `--xh-hover-card-title-fg` | `title` | `color` | `default` | `--xh-fg-default` | hover-card 的 title 部件 color 覆盖槽。 |
| `--xh-hover-card-title-font-size` | `title` | `font-size` | `default` | `--xh-text-label-size` | hover-card 的 title 部件 font-size 覆盖槽。 |
| `--xh-hover-card-title-font-weight` | `title` | `font-weight` | `default` | `--xh-font-weight-semibold` | hover-card 的 title 部件 font-weight 覆盖槽。 |
| `--xh-hover-card-trigger-gap` | `trigger` | `gap` | `default` | `--xh-control-gap-sm` | hover-card 的 trigger 部件 gap 覆盖槽。 |
<!-- xh-component-tokens:end -->

### 动效

动效角色：出现（锚定面板）（见[动效规范](../design/motion#角色)）。

共享关键帧 `xh-overlay-pop-in` · `xh-pop-out` 由 `family/motion.css` 提供，皮肤 `@import` 它，单独引入仍成立。时长与缓动读[动效令牌](../guide/motion)，改令牌即改全局节奏。

皮肤之外还有一段：退场由适配器的退场闸门把关，动画播完才真收起。

系统开启减弱动效时由令牌层统一收敛，皮肤不另作判断。

### RTL

皮肤用逻辑属性排布（`inline-start` 一族），`dir="rtl"` 下自动镜像。
