来源：https://ui.docs.xihanfun.com/components/descriptions

# Descriptions 描述列表

成对的标签与值，按列排开。

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

## 用法

标签与取值的配对依靠 dl / dt / dd 表达，组件只提供身份与排版；不传 columns 即每行一组

```vue
<script setup lang="ts">
import {
  XhDescriptionsItem,
  XhDescriptionsLabel,
  XhDescriptionsRoot,
  XhDescriptionsValue,
} from "@xihan-ui/vue";

const order = [
  { label: "订单号", value: "XH-20260810-0042" },
  { label: "下单时间", value: "2026-08-10 09:31" },
  { label: "支付方式", value: "余额支付" },
];
</script>

<template>
  <XhDescriptionsRoot style="max-inline-size: 420px">
    <XhDescriptionsItem v-for="row in order" :key="row.label">
      <XhDescriptionsLabel>{{ row.label }}</XhDescriptionsLabel>
      <XhDescriptionsValue>{{ row.value }}</XhDescriptionsValue>
    </XhDescriptionsItem>
  </XhDescriptionsRoot>
</template>
```

```html
<xh-descriptions>
  <dl data-xh-part="root" style="max-inline-size: 420px">
    <div data-xh-part="item">
      <dt data-xh-part="label">订单号</dt>
      <dd data-xh-part="value">XH-20260810-0042</dd>
    </div>
    <div data-xh-part="item">
      <dt data-xh-part="label">下单时间</dt>
      <dd data-xh-part="value">2026-08-10 09:31</dd>
    </div>
    <div data-xh-part="item">
      <dt data-xh-part="label">支付方式</dt>
      <dd data-xh-part="value">余额支付</dd>
    </div>
  </dl>
</xh-descriptions>
```

## 组件结构

加粗的是必需部件。

`data-scope="descriptions"`：**`root`** · `item` · `label` · `value`

## 示例

### 列数

columns 决定每行排几组，一到六列；排版使用 CSS Grid，不使用表格

```vue
<script setup lang="ts">
import {
  XhDescriptionsItem,
  XhDescriptionsLabel,
  XhDescriptionsRoot,
  XhDescriptionsValue,
} from "@xihan-ui/vue";

const rows = [
  { label: "姓名", value: "张三" },
  { label: "工号", value: "A-1024" },
  { label: "部门", value: "技术部" },
  { label: "岗位", value: "前端工程师" },
  { label: "入职", value: "2024-03-01" },
  { label: "座机", value: "8021" },
];
</script>

<template>
  <XhDescriptionsRoot :columns="3">
    <XhDescriptionsItem v-for="row in rows" :key="row.label">
      <XhDescriptionsLabel>{{ row.label }}</XhDescriptionsLabel>
      <XhDescriptionsValue>{{ row.value }}</XhDescriptionsValue>
    </XhDescriptionsItem>
  </XhDescriptionsRoot>
</template>
```

```html
<xh-descriptions columns="3">
  <dl data-xh-part="root">
    <div data-xh-part="item">
      <dt data-xh-part="label">姓名</dt>
      <dd data-xh-part="value">张三</dd>
    </div>
    <div data-xh-part="item">
      <dt data-xh-part="label">工号</dt>
      <dd data-xh-part="value">A-1024</dd>
    </div>
    <div data-xh-part="item">
      <dt data-xh-part="label">部门</dt>
      <dd data-xh-part="value">技术部</dd>
    </div>
    <div data-xh-part="item">
      <dt data-xh-part="label">岗位</dt>
      <dd data-xh-part="value">前端工程师</dd>
    </div>
    <div data-xh-part="item">
      <dt data-xh-part="label">入职</dt>
      <dd data-xh-part="value">2024-03-01</dd>
    </div>
    <div data-xh-part="item">
      <dt data-xh-part="label">座机</dt>
      <dd data-xh-part="value">8021</dd>
    </div>
  </dl>
</xh-descriptions>
```

### 标签位置

placement 决定标签在上还是在左，不传即在上

```vue
<script setup lang="ts">
import {
  XhDescriptionsItem,
  XhDescriptionsLabel,
  XhDescriptionsRoot,
  XhDescriptionsValue,
} from "@xihan-ui/vue";

const rows = [
  { label: "订单号", value: "XH-20260810-0042" },
  { label: "下单时间", value: "2026-08-10 09:31" },
];

// 上面那一档不写 placement，用 undefined 表达
const placements = [
  { placement: undefined, caption: "标签在上（默认）" },
  { placement: "left", caption: "标签在左" },
] as const;
</script>

<template>
  <div style="display: flex; flex-wrap: wrap; gap: 24px">
    <div v-for="p in placements" :key="p.caption" style="inline-size: 260px">
      <p>{{ p.caption }}</p>
      <XhDescriptionsRoot :placement="p.placement">
        <XhDescriptionsItem v-for="row in rows" :key="row.label">
          <XhDescriptionsLabel>{{ row.label }}</XhDescriptionsLabel>
          <XhDescriptionsValue>{{ row.value }}</XhDescriptionsValue>
        </XhDescriptionsItem>
      </XhDescriptionsRoot>
    </div>
  </div>
</template>
```

```html
<div style="display: flex; flex-wrap: wrap; gap: 24px">
  <div style="inline-size: 260px">
    <p>标签在上（默认）</p>
    <xh-descriptions>
      <dl data-xh-part="root">
        <div data-xh-part="item">
          <dt data-xh-part="label">订单号</dt>
          <dd data-xh-part="value">XH-20260810-0042</dd>
        </div>
        <div data-xh-part="item">
          <dt data-xh-part="label">下单时间</dt>
          <dd data-xh-part="value">2026-08-10 09:31</dd>
        </div>
      </dl>
    </xh-descriptions>
  </div>

  <div style="inline-size: 260px">
    <p>标签在左</p>
    <xh-descriptions placement="left">
      <dl data-xh-part="root">
        <div data-xh-part="item">
          <dt data-xh-part="label">订单号</dt>
          <dd data-xh-part="value">XH-20260810-0042</dd>
        </div>
        <div data-xh-part="item">
          <dt data-xh-part="label">下单时间</dt>
          <dd data-xh-part="value">2026-08-10 09:31</dd>
        </div>
      </dl>
    </xh-descriptions>
  </div>
</div>
```

### 外框

variant="outline" 绘制一圈描边，并在格与格之间补上网格线

```vue
<script setup lang="ts">
import {
  XhDescriptionsItem,
  XhDescriptionsLabel,
  XhDescriptionsRoot,
  XhDescriptionsValue,
} from "@xihan-ui/vue";

const rows = [
  { label: "商品", value: "机械键盘" },
  { label: "单价", value: "￥499.00" },
  { label: "数量", value: "2" },
  { label: "小计", value: "￥998.00" },
];
</script>

<template>
  <XhDescriptionsRoot variant="outline" :columns="2" placement="left">
    <XhDescriptionsItem v-for="row in rows" :key="row.label">
      <XhDescriptionsLabel>{{ row.label }}</XhDescriptionsLabel>
      <XhDescriptionsValue>{{ row.value }}</XhDescriptionsValue>
    </XhDescriptionsItem>
  </XhDescriptionsRoot>
</template>
```

```html
<xh-descriptions variant="outline" columns="2" placement="left">
  <dl data-xh-part="root">
    <div data-xh-part="item">
      <dt data-xh-part="label">商品</dt>
      <dd data-xh-part="value">机械键盘</dd>
    </div>
    <div data-xh-part="item">
      <dt data-xh-part="label">单价</dt>
      <dd data-xh-part="value">￥499.00</dd>
    </div>
    <div data-xh-part="item">
      <dt data-xh-part="label">数量</dt>
      <dd data-xh-part="value">2</dd>
    </div>
    <div data-xh-part="item">
      <dt data-xh-part="label">小计</dt>
      <dd data-xh-part="value">￥998.00</dd>
    </div>
  </dl>
</xh-descriptions>
```

### 尺寸

size 改变每格的内边距、组与组的间距与整体字号，不传 size 即默认档

```vue
<script setup lang="ts">
import {
  XhDescriptionsItem,
  XhDescriptionsLabel,
  XhDescriptionsRoot,
  XhDescriptionsValue,
} from "@xihan-ui/vue";

const rows = [
  { label: "状态", value: "已发货" },
  { label: "承运商", value: "顺丰速运" },
];

// 中间一档不写 size，用 undefined 表达
const sizes = [
  { size: "sm", label: "小" },
  { size: undefined, label: "默认" },
  { size: "lg", label: "大" },
] as const;
</script>

<template>
  <div style="display: flex; flex-direction: column; gap: 16px">
    <XhDescriptionsRoot
      v-for="s in sizes"
      :key="s.label"
      :size="s.size"
      variant="outline"
      :columns="2"
      placement="left"
    >
      <XhDescriptionsItem v-for="row in rows" :key="row.label">
        <XhDescriptionsLabel>{{ s.label }} · {{ row.label }}</XhDescriptionsLabel>
        <XhDescriptionsValue>{{ row.value }}</XhDescriptionsValue>
      </XhDescriptionsItem>
    </XhDescriptionsRoot>
  </div>
</template>
```

```html
<div style="display: flex; flex-direction: column; gap: 16px">
  <xh-descriptions size="sm" variant="outline" columns="2" placement="left">
    <dl data-xh-part="root">
      <div data-xh-part="item">
        <dt data-xh-part="label">小 · 状态</dt>
        <dd data-xh-part="value">已发货</dd>
      </div>
      <div data-xh-part="item">
        <dt data-xh-part="label">小 · 承运商</dt>
        <dd data-xh-part="value">顺丰速运</dd>
      </div>
    </dl>
  </xh-descriptions>

  <!-- 中间这一档不写 size -->
  <xh-descriptions variant="outline" columns="2" placement="left">
    <dl data-xh-part="root">
      <div data-xh-part="item">
        <dt data-xh-part="label">默认 · 状态</dt>
        <dd data-xh-part="value">已发货</dd>
      </div>
      <div data-xh-part="item">
        <dt data-xh-part="label">默认 · 承运商</dt>
        <dd data-xh-part="value">顺丰速运</dd>
      </div>
    </dl>
  </xh-descriptions>

  <xh-descriptions size="lg" variant="outline" columns="2" placement="left">
    <dl data-xh-part="root">
      <div data-xh-part="item">
        <dt data-xh-part="label">大 · 状态</dt>
        <dd data-xh-part="value">已发货</dd>
      </div>
      <div data-xh-part="item">
        <dt data-xh-part="label">大 · 承运商</dt>
        <dd data-xh-part="value">顺丰速运</dd>
      </div>
    </dl>
  </xh-descriptions>
</div>
```

### 跨列

一格写 span 横跨几列，上限是当前列数；长文本字段因此不必另开一份描述列表

```vue
<script setup lang="ts">
import {
  XhDescriptionsItem,
  XhDescriptionsLabel,
  XhDescriptionsRoot,
  XhDescriptionsValue,
} from "@xihan-ui/vue";
</script>

<template>
  <XhDescriptionsRoot :columns="3" variant="outline" style="max-inline-size: 720px">
    <XhDescriptionsItem>
      <XhDescriptionsLabel>订单号</XhDescriptionsLabel>
      <XhDescriptionsValue>XH-20260810-0042</XhDescriptionsValue>
    </XhDescriptionsItem>
    <XhDescriptionsItem>
      <XhDescriptionsLabel>下单时间</XhDescriptionsLabel>
      <XhDescriptionsValue>2026-08-10 09:31</XhDescriptionsValue>
    </XhDescriptionsItem>
    <XhDescriptionsItem>
      <XhDescriptionsLabel>支付方式</XhDescriptionsLabel>
      <XhDescriptionsValue>余额支付</XhDescriptionsValue>
    </XhDescriptionsItem>

    <XhDescriptionsItem :span="2">
      <XhDescriptionsLabel>收货地址</XhDescriptionsLabel>
      <XhDescriptionsValue>浙江省杭州市余杭区文一西路 969 号</XhDescriptionsValue>
    </XhDescriptionsItem>
    <XhDescriptionsItem>
      <XhDescriptionsLabel>联系电话</XhDescriptionsLabel>
      <XhDescriptionsValue>138 0000 0000</XhDescriptionsValue>
    </XhDescriptionsItem>

    <!-- 超过 columns 时按 columns 算，不会跨出网格另起一行 -->
    <XhDescriptionsItem :span="9">
      <XhDescriptionsLabel>备注</XhDescriptionsLabel>
      <XhDescriptionsValue>工作日 09:00–18:00 送达，到前电联。</XhDescriptionsValue>
    </XhDescriptionsItem>
  </XhDescriptionsRoot>
</template>
```

```html
<xh-descriptions columns="3" variant="outline">
  <dl data-xh-part="root" style="max-inline-size: 720px">
    <div data-xh-part="item">
      <dt data-xh-part="label">订单号</dt>
      <dd data-xh-part="value">XH-20260810-0042</dd>
    </div>
    <div data-xh-part="item">
      <dt data-xh-part="label">下单时间</dt>
      <dd data-xh-part="value">2026-08-10 09:31</dd>
    </div>
    <div data-xh-part="item">
      <dt data-xh-part="label">支付方式</dt>
      <dd data-xh-part="value">余额支付</dd>
    </div>

    <div data-xh-part="item" span="2">
      <dt data-xh-part="label">收货地址</dt>
      <dd data-xh-part="value">浙江省杭州市余杭区文一西路 969 号</dd>
    </div>
    <div data-xh-part="item">
      <dt data-xh-part="label">联系电话</dt>
      <dd data-xh-part="value">138 0000 0000</dd>
    </div>

    <!-- 超过 columns 时按 columns 算，不会跨出网格另起一行 -->
    <div data-xh-part="item" span="9">
      <dt data-xh-part="label">备注</dt>
      <dd data-xh-part="value">工作日 09:00–18:00 送达，到前电联。</dd>
    </div>
  </dl>
</xh-descriptions>
```

## 设计指引

### 何时使用

- 详情页的属性列表：订单信息、设备参数、用户资料。

### 何时不用

- 数据是多行同构的记录时，使用[表格](./table)。
- 只有一两对时，直接书写。

### 特性

- 语义是 `dt` / `dd`，组件只提供身份与排版。
- `columns` 决定每行几组，不传时每行一组。
- 标签位置可以在值的上方或左侧；`variant="outline"` 提供外框与网格线。
- 每一格可以通过 `span` 横跨多列，上限是当前列数；窄档一行只放一组时忽略该值。

### 组合

- 放入[卡片](./card)或[页头](./page-header)的页脚。

### 最佳实践

- 值为空时写“—”，不留空白，避免用户无法区分没有值与未加载。
- 标签左置时给它们统一宽度，值才能对齐。
- 长文本字段用 `span` 占满整行，不为它另开一份描述列表。

### 反模式

- 用它排版表格。
- 标签比值更长。

## API 参考

### 产物

| 层 | 值 |
| --- | --- |
| 自定义元素 | `<xh-descriptions>` |
| Vue 组件 | `XhDescriptionsItem` `XhDescriptionsLabel` `XhDescriptionsRoot` `XhDescriptionsValue` |
| 状态机 | 无，`connect` 直接由 props 算属性 |
| 皮肤 | `@xihan-ui/styles/descriptions.css` |

### Props

| 属性 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `columns` | `DescriptionsColumns` |  | 每行放置几组，一到六列；未提供时每行一组。 |
| `placement` | `DescriptionsPlacement` |  | 标签的位置：top / left；未提供时标签在上。 |
| `size` | `Size` |  | 尺寸：sm / md / lg。 |
| `variant` | `ControlVariant` |  | 形态：ghost 不画壳（默认），outline 绘制外框并在格与格之间补网格线，subtle 淡底。默认 ghost。 |

### React 适配器 props

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

| React 组件 | 属性 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `XhDescriptionsItem` | `as` | `ElementType` |  | 每一格渲染为哪个标签，默认 div。 |
| `XhDescriptionsItem` | `span` | `number` |  | 该格横跨几列，未写即占一列；上限是根上的 columns。 |
| `XhDescriptionsLabel` | `as` | `ElementType` |  | 标签渲染为哪个标签，默认 dt。 |
| `XhDescriptionsRoot` | `as` | `ElementType` |  | 根渲染为哪个标签，默认 dl。 |
| `XhDescriptionsValue` | `as` | `ElementType` |  | 取值渲染为哪个标签，默认 dd。 |

### connect API

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

| 成员 | 类型 | 说明 |
| --- | --- | --- |
| `getRootProps` | `() => T['element']` |  |
| `getItemProps` | `(props?: DescriptionsItemProps) => T['element']` |  |
| `getLabelProps` | `() => T['element']` |  |
| `getValueProps` | `() => T['element']` |  |

## 无障碍

### 键盘

规格出处：[W3C APG](https://www.w3.org/WAI/ARIA/apg/)

无键盘交互（不接收焦点，或焦点行为完全由原生元素提供）。

## 样式参考

### 皮肤

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

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

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

| 变量 | 部件 | CSS 属性 | 状态 | 默认来源 | 说明 |
| --- | --- | --- | --- | --- | --- |
| `--xh-descriptions-bg` | `root` | `background` | `variant=outline`<br>`variant=subtle` | `--xh-bg-subtle`<br>`--xh-bg-surface` | descriptions 的 root 部件 background 覆盖槽。 |
| `--xh-descriptions-border` | `root` | `border` | `variant=outline` | `--xh-border-default` | descriptions 的 root 部件 border 覆盖槽。 |
| `--xh-descriptions-divider` | `item`<br>`root` | `border-block-start`<br>`border-inline-start` | `variant=outline` | `--xh-border-subtle` | descriptions 的 item、root 部件 border-block-start、border-inline-start 覆盖槽。 |
| `--xh-descriptions-fg` | `root` | `color` | `default` | `--xh-fg-default` | descriptions 的 root 部件 color 覆盖槽。 |
| `--xh-descriptions-font-size` | `root` | `font-size` | `default` | `--xh-_descriptions-font-size` | descriptions 的 root 部件 font-size 覆盖槽。 |
| `--xh-descriptions-gap` | `root` | `gap` | `default` | `--xh-_descriptions-gap` | descriptions 的 root 部件 gap 覆盖槽。 |
| `--xh-descriptions-item-px` | `item`<br>`root` | `padding-inline` | `variant=outline` | `--xh-_descriptions-px` | descriptions 的 item、root 部件 padding-inline 覆盖槽。 |
| `--xh-descriptions-item-py` | `item`<br>`root` | `padding-block` | `variant=outline` | `--xh-_descriptions-py` | descriptions 的 item、root 部件 padding-block 覆盖槽。 |
| `--xh-descriptions-label-fg` | `label` | `color` | `default` | `--xh-fg-muted` | descriptions 的 label 部件 color 覆盖槽。 |
| `--xh-descriptions-label-font-weight` | `label` | `font-weight` | `default` | `--xh-text-label-weight` | descriptions 的 label 部件 font-weight 覆盖槽。 |
| `--xh-descriptions-label-gap` | `item`<br>`root` | `column-gap` | `@media (min-width: 768px)`<br>`placement=left` | `--xh-_descriptions-label-gap` | descriptions 的 item、root 部件 column-gap 覆盖槽。 |
| `--xh-descriptions-label-w` | `item`<br>`root` | `grid-template-columns` | `@media (min-width: 768px)`<br>`placement=left` | `--xh-_descriptions-label-w` | descriptions 的 item、root 部件 grid-template-columns 覆盖槽。 |
| `--xh-descriptions-pair-gap` | `item` | `gap` | `default` | `--xh-_descriptions-pair-gap` | descriptions 的 item 部件 gap 覆盖槽。 |
| `--xh-descriptions-radius` | `root` | `border-radius` | `variant=outline`<br>`variant=subtle` | `--xh-shape-surface` | descriptions 的 root 部件 border-radius 覆盖槽。 |
| `--xh-descriptions-value-fg` | `value` | `color` | `default` | `--xh-fg-default` | descriptions 的 value 部件 color 覆盖槽。 |
<!-- xh-component-tokens:end -->

### 动效

本组件皮肤不含过渡与关键帧，也没有脚本驱动的动效：状态一变，外观立即到位。

### 响应式

皮肤按视口分档：`min-width: 1024px` · `min-width: 768px`。

### RTL

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