来源：https://ui.docs.xihanfun.com/components/loading-bar

# LoadingBar 加载条

页面顶部的细进度线，表示正在导航或正在获取数据。

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

## 用法

进度条贴在视口顶边（见页面最上方）；不提供 value 即为不确定进度，宽度自行向前爬升，loading 切换为 false 后才到达终点并淡出

```vue
<script setup lang="ts">
import {
  XhButton,
  XhLoadingBarRange,
  XhLoadingBarRoot,
  XhLoadingBarTrack,
} from "@xihan-ui/vue";
import { ref } from "vue";

const loading = ref(false);
// 只读进度用 value-change 接；写进 value prop 会把它变成确定进度，爬升就停了
const value = ref(0);
</script>

<template>
  <XhLoadingBarRoot :loading="loading" @value-change="value = $event.value">
    <XhLoadingBarTrack>
      <XhLoadingBarRange />
    </XhLoadingBarTrack>
  </XhLoadingBarRoot>

  <XhButton variant="solid" @click="loading = true">开始加载</XhButton>
  <XhButton variant="outline" @click="loading = false">结束加载</XhButton>
  <span>假进度：{{ Math.round(value) }}%</span>
</template>
```

```html
<xh-loading-bar id="loading-bar-basic">
  <div data-xh-part="root">
    <div data-xh-part="track">
      <div data-xh-part="range"></div>
    </div>
  </div>
</xh-loading-bar>

<div
  id="loading-bar-basic-actions"
  style="display: flex; flex-wrap: wrap; align-items: center; gap: 12px"
>
  <xh-button variant="solid" data-loading="on">
    <button data-xh-part="root">开始加载</button>
  </xh-button>
  <xh-button variant="outline" data-loading="off">
    <button data-xh-part="root">结束加载</button>
  </xh-button>
  <span>假进度：<span id="loading-bar-basic-value">0</span>%</span>
</div>

<script type="module">
  const bar = document.getElementById("loading-bar-basic");
  const actions = document.getElementById("loading-bar-basic-actions");
  const readout = document.getElementById("loading-bar-basic-value");

  for (const button of actions.querySelectorAll("[data-loading]")) {
    button.addEventListener("click", () => {
      bar.loading = button.dataset.loading === "on";
    });
  }

  // 只读进度用 value-change 接；写进 value 会把它变成确定进度，爬升就停了
  bar.addEventListener("value-change", (event) => {
    readout.textContent = String(Math.round(event.detail.value));
  });
</script>
```

## 组件结构

加粗的是必需部件。

`data-scope="loading-bar"`：**`root`** · `track` · **`range`** · `peg`

## 示例

### 确定进度

传入 value 后由宿主决定，宽度按它显示，内部爬升不再介入；loading 仍然负责显示与收起

```vue
<script setup lang="ts">
import {
  XhButton,
  XhLoadingBarRange,
  XhLoadingBarRoot,
  XhLoadingBarTrack,
} from "@xihan-ui/vue";
import { ref } from "vue";

const loading = ref(false);
const value = ref(0);

function start(): void {
  value.value = 0;
  loading.value = true;
}

function advance(): void {
  value.value = Math.min(100, value.value + 25);
}
</script>

<template>
  <XhLoadingBarRoot :loading="loading" :value="value">
    <XhLoadingBarTrack>
      <XhLoadingBarRange />
    </XhLoadingBarTrack>
  </XhLoadingBarRoot>

  <XhButton variant="solid" @click="start">开始</XhButton>
  <XhButton variant="outline" @click="advance">推进 25%</XhButton>
  <XhButton variant="ghost" @click="loading = false">结束</XhButton>
  <span>进度：{{ value }}%</span>
</template>
```

```html
<xh-loading-bar id="loading-bar-determinate" value="0">
  <div data-xh-part="root">
    <div data-xh-part="track">
      <div data-xh-part="range"></div>
    </div>
  </div>
</xh-loading-bar>

<div
  id="loading-bar-determinate-actions"
  style="display: flex; flex-wrap: wrap; align-items: center; gap: 12px"
>
  <xh-button variant="solid" data-act="start">
    <button data-xh-part="root">开始</button>
  </xh-button>
  <xh-button variant="outline" data-act="advance">
    <button data-xh-part="root">推进 25%</button>
  </xh-button>
  <xh-button variant="ghost" data-act="finish">
    <button data-xh-part="root">结束</button>
  </xh-button>
  <span>进度：<span id="loading-bar-determinate-value">0</span>%</span>
</div>

<script type="module">
  const bar = document.getElementById("loading-bar-determinate");
  const actions = document.getElementById("loading-bar-determinate-actions");
  const readout = document.getElementById("loading-bar-determinate-value");

  function set(value) {
    bar.value = value;
    readout.textContent = String(value);
  }

  const acts = {
    start: () => {
      set(0);
      bar.loading = true;
    },
    advance: () => set(Math.min(100, bar.value + 25)),
    finish: () => {
      bar.loading = false;
    },
  };

  for (const button of actions.querySelectorAll("[data-act]")) {
    button.addEventListener("click", () => acts[button.dataset.act]());
  }
</script>
```

### 厚度

height 数字按像素、字符串按任意 CSS 长度；进度段的颜色经语气或皮肤槽，不使用内联

```vue
<script setup lang="ts">
import {
  XhButton,
  XhLoadingBarRange,
  XhLoadingBarRoot,
  XhLoadingBarTrack,
} from "@xihan-ui/vue";
import { ref } from "vue";

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

<template>
  <XhLoadingBarRoot :loading="loading" :height="6" tone="warning">
    <XhLoadingBarTrack>
      <XhLoadingBarRange />
    </XhLoadingBarTrack>
  </XhLoadingBarRoot>

  <XhButton variant="solid" @click="loading = true">开始加载</XhButton>
  <XhButton variant="outline" @click="loading = false">结束加载</XhButton>
  <span>6px 厚的警示色条子，仍然贴在视口顶边</span>
</template>
```

```html
<xh-loading-bar id="loading-bar-appearance" height="6" tone="warning">
  <div data-xh-part="root">
    <div data-xh-part="track">
      <div data-xh-part="range"></div>
    </div>
  </div>
</xh-loading-bar>

<div
  id="loading-bar-appearance-actions"
  style="display: flex; flex-wrap: wrap; align-items: center; gap: 12px"
>
  <xh-button variant="solid" data-loading="on">
    <button data-xh-part="root">开始加载</button>
  </xh-button>
  <xh-button variant="outline" data-loading="off">
    <button data-xh-part="root">结束加载</button>
  </xh-button>
  <span>6px 厚的警示色条子，仍然贴在视口顶边</span>
</div>

<script type="module">
  const bar = document.getElementById("loading-bar-appearance");
  const actions = document.getElementById("loading-bar-appearance-actions");
  for (const button of actions.querySelectorAll("[data-loading]")) {
    button.addEventListener("click", () => {
      bar.loading = button.dataset.loading === "on";
    });
  }
</script>
```

### 关闭爬升

trickle 为 false 时进度条停在起步值 minimum 不动，向前推进全部由宿主收尾

```vue
<script setup lang="ts">
import {
  XhButton,
  XhLoadingBarRange,
  XhLoadingBarRoot,
  XhLoadingBarTrack,
} from "@xihan-ui/vue";
import { ref } from "vue";

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

<template>
  <XhLoadingBarRoot
    :loading="loading"
    :trickle="false"
    :minimum="30"
    :height="6"
  >
    <XhLoadingBarTrack>
      <XhLoadingBarRange />
    </XhLoadingBarTrack>
  </XhLoadingBarRoot>

  <XhButton variant="solid" @click="loading = true">开始加载</XhButton>
  <XhButton variant="outline" @click="loading = false">结束加载</XhButton>
  <span>开始后停在 30%，按「结束加载」才冲到 100 并淡出</span>
</template>
```

```html
<xh-loading-bar id="loading-bar-trickle" trickle="false" minimum="30" height="6">
  <div data-xh-part="root">
    <div data-xh-part="track">
      <div data-xh-part="range"></div>
    </div>
  </div>
</xh-loading-bar>

<div
  id="loading-bar-trickle-actions"
  style="display: flex; flex-wrap: wrap; align-items: center; gap: 12px"
>
  <xh-button variant="solid" data-loading="on">
    <button data-xh-part="root">开始加载</button>
  </xh-button>
  <xh-button variant="outline" data-loading="off">
    <button data-xh-part="root">结束加载</button>
  </xh-button>
  <span>开始后停在 30%，按「结束加载」才冲到 100 并淡出</span>
</div>

<script type="module">
  const bar = document.getElementById("loading-bar-trickle");
  const actions = document.getElementById("loading-bar-trickle-actions");
  for (const button of actions.querySelectorAll("[data-loading]")) {
    button.addEventListener("click", () => {
      bar.loading = button.dataset.loading === "on";
    });
  }
</script>
```

### 颜色

tone 只更换进度段的底色（取柔和档）；进度条本身是 fixed，这里改写为 absolute 并配一个相对定位的容器，六条才留在示例中而不是叠到页面顶边

```vue
<script setup lang="ts">
import type { CSSProperties } from "vue";
import {
  XhLoadingBarRange,
  XhLoadingBarRoot,
  XhLoadingBarTrack,
} from "@xihan-ui/vue";

const tones = [
  { value: "brand", label: "brand 品牌" },
  { value: "neutral", label: "neutral 中性" },
  { value: "success", label: "success 成功" },
  { value: "warning", label: "warning 警示" },
  { value: "danger", label: "danger 危险" },
  { value: "info", label: "info 提示" },
];

// 框住条子：条子改走 absolute，inset 就落在这个框上
const frameStyle: CSSProperties = {
  position: "relative",
  overflow: "hidden",
  inlineSize: "100%",
  blockSize: "6px",
  borderRadius: "999px",
  background: "var(--xh-bg-muted)",
};
</script>

<template>
  <div style="display: flex; flex-direction: column; gap: 12px; inline-size: 100%">
    <div
      v-for="tone in tones"
      :key="tone.value"
      style="display: flex; flex-direction: column; gap: 6px"
    >
      <span>{{ tone.label }}</span>
      <div :style="frameStyle">
        <XhLoadingBarRoot
          :loading="true"
          :value="60"
          :height="6"
          :tone="tone.value"
          style="position: absolute"
        >
          <XhLoadingBarTrack>
            <XhLoadingBarRange />
          </XhLoadingBarTrack>
        </XhLoadingBarRoot>
      </div>
    </div>
  </div>
</template>
```

```html
<div style="display: flex; flex-direction: column; gap: 12px; inline-size: 100%">
  <div style="display: flex; flex-direction: column; gap: 6px">
    <span>brand 品牌</span>
    <!-- 框住条子：条子改走 absolute，inset 就落在这个框上 -->
    <div style="position: relative; overflow: hidden; inline-size: 100%; block-size: 6px; border-radius: 999px; background: var(--xh-bg-muted)">
      <xh-loading-bar loading value="60" height="6" tone="brand">
        <div data-xh-part="root" style="position: absolute">
          <div data-xh-part="track">
            <div data-xh-part="range"></div>
          </div>
        </div>
      </xh-loading-bar>
    </div>
  </div>
  <div style="display: flex; flex-direction: column; gap: 6px">
    <span>neutral 中性</span>
    <!-- 框住条子：条子改走 absolute，inset 就落在这个框上 -->
    <div style="position: relative; overflow: hidden; inline-size: 100%; block-size: 6px; border-radius: 999px; background: var(--xh-bg-muted)">
      <xh-loading-bar loading value="60" height="6" tone="neutral">
        <div data-xh-part="root" style="position: absolute">
          <div data-xh-part="track">
            <div data-xh-part="range"></div>
          </div>
        </div>
      </xh-loading-bar>
    </div>
  </div>
  <div style="display: flex; flex-direction: column; gap: 6px">
    <span>success 成功</span>
    <!-- 框住条子：条子改走 absolute，inset 就落在这个框上 -->
    <div style="position: relative; overflow: hidden; inline-size: 100%; block-size: 6px; border-radius: 999px; background: var(--xh-bg-muted)">
      <xh-loading-bar loading value="60" height="6" tone="success">
        <div data-xh-part="root" style="position: absolute">
          <div data-xh-part="track">
            <div data-xh-part="range"></div>
          </div>
        </div>
      </xh-loading-bar>
    </div>
  </div>
  <div style="display: flex; flex-direction: column; gap: 6px">
    <span>warning 警示</span>
    <!-- 框住条子：条子改走 absolute，inset 就落在这个框上 -->
    <div style="position: relative; overflow: hidden; inline-size: 100%; block-size: 6px; border-radius: 999px; background: var(--xh-bg-muted)">
      <xh-loading-bar loading value="60" height="6" tone="warning">
        <div data-xh-part="root" style="position: absolute">
          <div data-xh-part="track">
            <div data-xh-part="range"></div>
          </div>
        </div>
      </xh-loading-bar>
    </div>
  </div>
  <div style="display: flex; flex-direction: column; gap: 6px">
    <span>danger 危险</span>
    <!-- 框住条子：条子改走 absolute，inset 就落在这个框上 -->
    <div style="position: relative; overflow: hidden; inline-size: 100%; block-size: 6px; border-radius: 999px; background: var(--xh-bg-muted)">
      <xh-loading-bar loading value="60" height="6" tone="danger">
        <div data-xh-part="root" style="position: absolute">
          <div data-xh-part="track">
            <div data-xh-part="range"></div>
          </div>
        </div>
      </xh-loading-bar>
    </div>
  </div>
  <div style="display: flex; flex-direction: column; gap: 6px">
    <span>info 提示</span>
    <!-- 框住条子：条子改走 absolute，inset 就落在这个框上 -->
    <div style="position: relative; overflow: hidden; inline-size: 100%; block-size: 6px; border-radius: 999px; background: var(--xh-bg-muted)">
      <xh-loading-bar loading value="60" height="6" tone="info">
        <div data-xh-part="root" style="position: absolute">
          <div data-xh-part="track">
            <div data-xh-part="range"></div>
          </div>
        </div>
      </xh-loading-bar>
    </div>
  </div>
</div>
```

### 挂在局部

进度条默认贴视口顶边，改写为 absolute 再套一个相对定位的容器，它就只贴该卡片的上沿

```vue
<script setup lang="ts">
import {
  XhButton,
  XhLoadingBarRange,
  XhLoadingBarRoot,
  XhLoadingBarTrack,
} from "@xihan-ui/vue";
import { ref } from "vue";

const loading = ref(false);

function reload(): void {
  loading.value = true;
  window.setTimeout(() => (loading.value = false), 1600);
}
</script>

<template>
  <div
    style="
      position: relative;
      overflow: hidden;
      inline-size: 100%;
      max-inline-size: 420px;
      border: 1px solid var(--xh-border-subtle);
      border-radius: 8px;
    "
  >
    <XhLoadingBarRoot
      :loading="loading"
      :height="3"
      style="position: absolute"
    >
      <XhLoadingBarTrack>
        <XhLoadingBarRange />
      </XhLoadingBarTrack>
    </XhLoadingBarRoot>

    <div style="display: grid; gap: 10px; padding: 16px">
      <span>这块卡片自己的加载条，不会跑到页面最上方</span>
      <XhButton size="sm" variant="outline" @click="reload">刷新本卡片</XhButton>
    </div>
  </div>
</template>
```

```html
<div
  style="
    position: relative;
    overflow: hidden;
    inline-size: 100%;
    max-inline-size: 420px;
    border: 1px solid var(--xh-border-subtle);
    border-radius: 8px;
  "
>
  <xh-loading-bar id="loading-bar-container" height="3">
    <div data-xh-part="root" style="position: absolute">
      <div data-xh-part="track">
        <div data-xh-part="range"></div>
      </div>
    </div>
  </xh-loading-bar>

  <div style="display: grid; gap: 10px; padding: 16px">
    <span>这块卡片自己的加载条，不会跑到页面最上方</span>
    <xh-button id="loading-bar-container-reload" size="sm" variant="outline">
      <button data-xh-part="root">刷新本卡片</button>
    </xh-button>
  </div>
</div>

<script type="module">
  // 按一下加载 1.6 秒后自行收尾
  const bar = document.getElementById("loading-bar-container");
  document
    .getElementById("loading-bar-container-reload")
    .addEventListener("click", () => {
      bar.loading = true;
      window.setTimeout(() => {
        bar.loading = false;
      }, 1600);
    });
</script>
```

## 设计指引

### 何时使用

- 路由切换、整页数据刷新等用户不需要具体百分比的过程。

### 何时不用

- 进度确定且用户关心具体数值时，使用[进度条](./progress)。
- 局部区域加载时，使用[骨架屏](./skeleton)或[加载指示器](./spinner)。

### 特性

- 未提供确定进度时自动爬升（`trickle`）：先快后慢，不到 100%，收到完成信号后补满。
- `minimum` 是起始位置，让用户立即看到反应。
- 可以挂在局部容器上，不限于页面顶部。

### 组合

- 与路由守卫配合：进入时启动，完成或失败时收起。

### 最佳实践

- 失败时也要收起，停留在页面上的半截进度条比没有更差。
- 极快的请求可以延迟显示，避免闪烁。

### 反模式

- 爬升到 100% 却尚未加载完成，用户会以为卡住。
- 同时挂多条。

## API 参考

### 产物

| 层 | 值 |
| --- | --- |
| 自定义元素 | `<xh-loading-bar>` |
| Vue 组件 | `XhLoadingBarPeg` `XhLoadingBarRange` `XhLoadingBarRoot` `XhLoadingBarTrack` |
| 组合式函数 | `useLoadingBar` |
| 状态机 | `loadingBarMachine` |
| 皮肤 | `@xihan-ui/styles/loading-bar.css` |

### Props

| 属性 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `value` | `number` |  | 受控进度值（0-100）。提供后即为确定进度：宽度按它显示，内部爬升停止。 |
| `defaultValue` | `number` |  | 非受控初值，默认 0。 |
| `loading` | `boolean` |  | 加载开关：true 开始，false 结束（到达 100 后淡出归零）。只由宿主写入，无配套回调。 |
| `height` | `string \| number` |  | 进度条厚度：数字按像素，字符串按任意 CSS 长度。默认 2px。 |
| `tone` | `Tone` |  | 语气：brand / neutral / success / warning / danger / info，决定进度段使用哪族颜色。需要其他颜色时修改皮肤槽 --xh-loading-bar-range。 |
| `trickle` | `boolean` |  | 不确定进度时自行向前爬升，默认开启。关闭则停在起步值等待宿主收尾。 |
| `trickleSpeed` | `number` |  | 爬升节拍毫秒，默认 200；&lt;=0 或非有限数等同于关闭爬升。 |
| `minimum` | `number` |  | 起步值，默认 8：开始加载时先跳到该值。 |
| `fadeDuration` | `number` |  | 到达 100 之后的淡出时长毫秒，写进皮肤的淡出时长槽 --xh-loading-bar-fade，不给按退场令牌；淡出过渡真正播完才归零并收起。 |
| `translations` | `Partial<LoadingBarTranslations>` |  |  |
| `onValueChange` | `(details: LoadingBarValueChangeDetails) => void` |  | 进度值变化。不确定进度下每爬升一步、到达 100、归零各通知一次。 |

### 事件

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

| 事件 | 载荷 | 说明 |
| --- | --- | --- |
| `value-change` | `LoadingBarValueChangeDetails` | 进度值变化；detail 为 `{ value: number }` |

### 插槽

仅列出带载荷的插槽。

| Vue 组件 | 插槽 | 载荷 | 说明 |
| --- | --- | --- | --- |
| `XhLoadingBarRoot` | `default` | `LoadingBarRootSlotProps` |  |

### React 适配器 props

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

| React 组件 | 属性 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `XhLoadingBarRoot` | `children` | `SlotChildren<LoadingBarRootSlotProps>` |  |  |

### 状态

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

| 部件 | 取值 |
| --- | --- |
| `root` | 'idle' \| 'loading' \| 'finishing' |
| `track` | 'idle' \| 'loading' \| 'finishing' |
| `range` | 'idle' \| 'loading' \| 'finishing' |
| `peg` | 'idle' \| 'loading' \| 'finishing' |

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

**状态**：`idle` · `loading` · `finishing`

**事件**：`LOADING.START` · `LOADING.END` · `TRICKLE.SYNC` · `after.trickleSpeed` · `FADE.DONE`

### connect API

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

| 成员 | 类型 | 说明 |
| --- | --- | --- |
| `phase` | `LoadingBarPhase` |  |
| `value` | `number` | 当前显示的进度值（0-100，已夹取），也是 range 的宽度百分比。 |
| `visible` | `boolean` | 进度条是否显示：idle 之外都显示。 |
| `indeterminate` | `boolean` | 不确定进度：未提供 value，宽度自行爬升，不输出 aria-valuenow。 |
| `getRootProps` | `() => T['element']` |  |
| `getTrackProps` | `() => T['element']` |  |
| `getRangeProps` | `() => T['element']` |  |
| `getPegProps` | `() => T['element']` | 进度段末端的亮边。纯装饰，作者不渲染它时进度条照常成立。 |

## 无障碍

### 键盘

规格出处：[W3C APG](https://www.w3.org/TR/wai-aria-1.2/#progressbar)

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

### ARIA

以下属性由 `connect` 生成。

| 部件 | 属性 | 值 |
| --- | --- | --- |
| `root` | `aria-label` | props.translations.root |
| `root` | `aria-valuemax` | String(LOADING_BAR_MAX) |
| `root` | `aria-valuemin` | '0' |
| `root` | `aria-valuenow` | String(value) \| undefined |
| `root` | `role` | 'progressbar' |
| `peg` | `aria-hidden` | 'true' |

## 样式参考

### 皮肤

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

### 数据属性

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

| 部件 | 属性 | 值 |
| --- | --- | --- |
| `root` | `data-indeterminate` | ''（条件成立时才出现） |
| `root` | `data-state` | 'idle' \| 'loading' \| 'finishing' |
| `root` | `data-tone` | props.tone |
| `track` | `data-state` | 'idle' \| 'loading' \| 'finishing' |
| `range` | `data-state` | 'idle' \| 'loading' \| 'finishing' |
| `peg` | `data-state` | 'idle' \| 'loading' \| 'finishing' |

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

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

| 变量 | 部件 | CSS 属性 | 状态 | 默认来源 | 说明 |
| --- | --- | --- | --- | --- | --- |
| `--xh-loading-bar-fade` | `root` | `transition` | `default` | `--xh-motion-duration-exit` | loading-bar 的 root 部件 transition 覆盖槽。 |
| `--xh-loading-bar-layer` | `root` | `z-index` | `default` | `--xh-layer-sticky` | loading-bar 的 root 部件 z-index 覆盖槽。 |
| `--xh-loading-bar-peg-fg` | `peg` | `background` | `default` | `--xh-loading-bar-range` | loading-bar 的 peg 部件 background 覆盖槽。 |
| `--xh-loading-bar-peg-w` | `peg` | `inline-size` | `default` | `--xh-space-8` | loading-bar 的 peg 部件 inline-size 覆盖槽。 |
| `--xh-loading-bar-range` | `peg`<br>`range` | `background` | `default` | `--xh-_tone-soft` | loading-bar 的 peg、range 部件 background 覆盖槽。 |
| `--xh-loading-bar-speed` | `range` | `transition` | `default` | `--xh-motion-duration-move` | loading-bar 的 range 部件 transition 覆盖槽。 |
| `--xh-loading-bar-track` | `track` | `background` | `default` | `transparent` | loading-bar 的 track 部件 background 覆盖槽。 |
<!-- xh-component-tokens:end -->

### 动效

动效角色：指示与换位 · 出现（见[动效规范](../design/motion#角色)）。

可覆盖的动效槽：`--xh-loading-bar-fade` · `--xh-loading-bar-speed`。

`opacity` · `translate` 走 `transition` 过渡。时长与缓动读[动效令牌](../guide/motion)，改令牌即改全局节奏。

`prefers-reduced-motion: reduce` 下本组件另有降级规则。

### RTL

皮肤用逻辑属性排布（`inline-start` 一族），`dir="rtl"` 下自动镜像；另有按 `dir` 分支的规则。
