---
title: "ローディングインジケーター"
description: "長さの分からない短い待ち時間が進行中であることを、インラインで、またはコンテンツの上に重ねて示します。"
url: "https://tanrei.chnotchy.com/ja/components/loading-indicator/"
last_updated: 2026-10-23T21:17:12.000Z
---

# ローディングインジケーター

> 長さの分からない短い待ち時間が進行中であることを、インラインで、またはコンテンツの上に重ねて示します。

## 概要

> [!NOTE]
> **仕様は改訂済み、一部のプラットフォームは追従待ち**
>
> このページは、仕様の現在の形のローディングインジケーターを説明しています。表示する前に待ち、それから現れます。Web コンポーネントはすでにこの動作で、デモも待ち時間のあとに現れます。上の表で「追従待ち」の SwiftUI、Compose、Flutter、React Native は、追従するまでは挿入された時点で描画して支援技術に伝え、新しい 3 つのプロパティも受け付けません。そのため、以下のこれらの例は、いま提供しているものだけを示しています。

### 使い方

**Web**

```html
<section aria-busy="true">
  <tanrei-loading-indicator label="メッセージを読み込み中"></tanrei-loading-indicator>
</section>

<!-- contained は、専用の浮き上がった円の上に描き、コンテンツに重ねて使います。 -->
<tanrei-loading-indicator contained size="large" label="更新中"></tanrei-loading-indicator>

<!-- 処理中のボタンは、押したらすぐに応えます。待ち時間なし。 -->
<tanrei-loading-indicator size="small" show-delay="0" label="保存中"></tanrei-loading-indicator>

<!-- 1 つだけ別の間合いに：長めに待ってから、大きくなりながら現れます。 -->
<tanrei-loading-indicator show-delay="600" entrance="grow" label="レポートを読み込み中"></tanrei-loading-indicator>

<script type="module">
  import '@tanrei/web/components/loading-indicator';
</script>
```

**React**

```tsx
import { LoadingIndicator } from '@tanrei/react';

<section aria-busy={loading}>
  {loading ? <LoadingIndicator label="メッセージを読み込み中" /> : <MessageList />}
</section>
<LoadingIndicator contained size="large" label="更新中" />
<LoadingIndicator size="small" showDelay={0} label="保存中" />
<LoadingIndicator showDelay={600} entrance="grow" label="レポートを読み込み中" />
```

**SwiftUI**

```swift
import Tanrei

TanreiLoadingIndicator("メッセージを読み込み中")

// コンテンツの上に重ねるときは、専用の浮き上がったコンテナに載せます。
TanreiLoadingIndicator("更新中", size: .large, contained: true)
```

**Compose**

```kotlin
import io.github.chnotchy.tanrei.components.TanreiContainedLoadingIndicator
import io.github.chnotchy.tanrei.components.TanreiLoadingIndicator
import io.github.chnotchy.tanrei.tokens.TanreiLoadingIndicatorSize

TanreiLoadingIndicator(contentDescription = "メッセージを読み込み中")

// コンテナ付きは、同じ引数を取る 2 つ目のコンポーザブルです。
TanreiContainedLoadingIndicator(contentDescription = "更新中", size = TanreiLoadingIndicatorSize.Large)
```

**Flutter**

```dart
import 'package:tanrei/tanrei.dart';

const TanreiLoadingIndicator(semanticsLabel: 'メッセージを読み込み中');
const TanreiLoadingIndicator(
  semanticsLabel: '更新中',
  size: TanreiLoadingIndicatorSize.large,
  contained: true,
);
```

**React Native**

```tsx
import { LoadingIndicator } from '@tanrei/react-native';

<LoadingIndicator accessibilityLabel="メッセージを読み込み中" />
<LoadingIndicator accessibilityLabel="更新中" size="large" contained />
```

## 仕様

- Category: Communication
- Level: base
- Listing: default
- Page: https://tanrei.chnotchy.com/components/loading-indicator/
- Spec: https://github.com/chnotchy/tanrei/blob/main/specs/loading-indicator.spec.json

### Availability

| Design system | Has it | Notes |
| --- | --- | --- |
| Tanrei (`tanrei`) | Yes | — |
| Cupertino (Apple HIG-inspired) (`cupertino`) | Yes | — |
| M3 Expressive-like (Material 3 Expressive-inspired) (`m3-expressive-like`) | Yes | — |
| M3-like (Material 3-inspired) (`m3-like`) | Yes | — |

### Platforms

- Web: the element `<tanrei-loading-indicator>`, registered by importing `@tanrei/web` (or only its own module, `@tanrei/web/components/loading-indicator`).
- React: `LoadingIndicator` from `@tanrei/react`.

| Platform | Name | Status |
| --- | --- | --- |
| Web | `tanrei-loading-indicator` | lagging |
| SwiftUI | `TanreiLoadingIndicator` | lagging |
| Jetpack Compose | `TanreiLoadingIndicator` | lagging |
| Flutter | `TanreiLoadingIndicator` | lagging |
| React Native | `LoadingIndicator` | lagging |

Names a platform gives in its own idiom:

| Spec | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
| --- | --- | --- | --- | --- | --- |
| `contained` (prop) | `contained` | `contained` | `TanreiContainedLoadingIndicator` | `contained` | `contained` |
| `label` (prop) | `label` | `label` | `contentDescription` | `semanticsLabel` | `accessibilityLabel` |
| `showDelay` (prop) | `showDelay` | `showDelay` | `showDelayMillis` | `showDelay` | `showDelay` |
| `enterDuration` (prop) | `enterDuration` | `enterDuration` | `enterDurationMillis` | `enterDuration` | `enterDuration` |

Statuses: `planned` — In the inventory. Nothing shippable exists yet. `lagging` — An implementation of an earlier spec exists; it has not caught up with the current one. `alpha` — Implemented with tests. The API may still change. `beta` — Feature complete and at parity. Only fixes are expected. `stable` — Covered by semantic versioning. `deprecated` — Still shipped; scheduled for removal.

### Anatomy

- `indicator`: The moving mark: an arc a quarter of a circle long that turns. It has no track, because there is no value to read.
- `container` (optional): A filled, raised circle behind the indicator, drawn only when contained, so the indicator can be seen over content.

### Props

| Prop | Type | Default | Web attribute | Description |
| --- | --- | --- | --- | --- |
| `size` | `enum`: `small`, `medium`, `large` | `"medium"` | `size` | The indicator's box (16 / 24 / 40): small replaces an icon in a control, medium sits next to text, large fills an empty section. Contained, the container is 24 / 32 / 48. A design system may add values. |
| `contained` | `boolean` | `false` | `contained` | Draws the indicator on a raised container of its own, for places where it floats over content (pull to refresh, over an image or a list). |
| `label` | `string` | `""` | `label` | The accessible name: what is loading ("Loading messages"). Required: a moving mark alone is not a name. |
| `showDelay` | `number` | — | `show-delay` | How long, in milliseconds, this indicator waits from the moment it is inserted before it is drawn and exposed to assistive technology; 0 shows it as it is inserted. Null takes comp.loading-indicator.motion.show-delay, the system's answer for every indicator. Set it only where one indicator needs another pace than the rest (a wait the product knows is never instant, a busy button that should answer the press at once). |
| `entrance` | `enum`: `auto`, `fade`, `grow` | `"auto"` | `entrance` | How this indicator enters once its show delay has passed: fade eases it in, grow eases it in while it grows from its center. Auto takes comp.loading-indicator.motion.entrance, the system's answer for every indicator. Under reduced motion grow enters as fade (LDG-13). |
| `enterDuration` | `number` | — | `enter-duration` | How long, in milliseconds, this indicator's entrance lasts; 0 shows it whole at once. Null takes comp.loading-indicator.motion.enter-duration. |

### States

`enabled`

### Accessibility

- Role: `progressbar`
- Labeling: label is the accessible name. The indicator has no value: once shown, it is exposed as a progressbar in the platform's busy (indeterminate) state, never as a status message; during its show delay it is not exposed at all (LDG-11).
- Minimum target size: yes

- Not interactive: the indicator takes no input and is never focusable (LDG-6), so the minimum target holds without extending anything.
- comp.loading-indicator.indicator.color must stay at least 3:1 against the surface behind it, and comp.loading-indicator.contained.indicator.color against comp.loading-indicator.contained.container.color (non-text contrast): the moving mark is the only sign that something is happening.
- Text size does not change the indicator: it is not text. Where it replaces an icon in a control, the control keeps its size.
- A product marks the region it loads as busy (aria-busy on the web) and says when the content has arrived; a wait that may exceed a few seconds, or whose length can be measured, belongs to the progress indicator.
- A wait shorter than the show delay shows and exposes nothing (LDG-11): no mark that flashes for a sighted person, and no progressbar that appears and vanishes for a screen-reader user. The product's own busy marking of the region does not wait for the delay; aria-busy and its equivalents announce nothing by themselves.
- Reduced motion replaces the turning arc with an opacity pulse of at most one per second (LDG-4), below the three-per-second flash threshold, and turns a grow entrance into a fade (LDG-13). The show delay is kept: it is a wait, not motion (LDG-15).

### Behaviour

Requirements in EARS form, each with its stable id.

- **LDG-1** THE loading indicator SHALL draw, inside a comp.loading-indicator.indicator.size.medium square for medium (and so on for each size), an arc a quarter of the circle long with round caps and a stroke of comp.loading-indicator.indicator.thickness.medium for medium (and so on), whose outer edge touches the square, and SHALL turn it clockwise once per comp.loading-indicator.motion.cycle-duration at constant speed (sys.motion.easing.linear); it SHALL draw no track.
- **LDG-2** WHILE contained is false THE loading indicator SHALL take exactly its indicator square, draw no container, and draw the arc in comp.loading-indicator.indicator.color.
- **LDG-3** WHILE contained is true THE loading indicator SHALL draw a container of comp.loading-indicator.container.size.medium for medium (and so on for each size) with corners of comp.loading-indicator.container.shape, filled with comp.loading-indicator.contained.container.color and raised by comp.loading-indicator.contained.container.elevation, center the indicator in it, and draw the arc in comp.loading-indicator.contained.indicator.color.
- **LDG-4** WHERE reduced motion is requested THE loading indicator SHALL NOT turn: it SHALL draw the whole circle at the indicator's size and stroke and ease its opacity from 1 to 0 and back to 1 once per comp.loading-indicator.motion.cycle-duration with sys.motion.easing.standard; a container, when contained, SHALL stay still and opaque.
- **LDG-5** WHILE the loading indicator is shown (LDG-12) THE loading indicator SHALL expose the progressbar role to assistive technology with label as its name, no current value, and the platform's busy or indeterminate state.
- **LDG-6** THE loading indicator SHALL NOT be focusable and SHALL NOT announce itself when it appears or disappears; the region being loaded is marked busy, and its completion announced, by the product.
- **LDG-7** WHILE the loading indicator is not visible (scrolled out of view, in a hidden view, or in an app in the background) THE loading indicator SHALL NOT run its animation, and SHALL resume it when it becomes visible.
- **LDG-8** WHERE the layout direction is right-to-left THE loading indicator SHALL NOT be mirrored: it turns clockwise in every direction.
- **LDG-9** WHEN size names a value that the active design system does not have THE loading indicator SHALL render with the default value's tokens, and in debug builds report the unknown value.
- **LDG-10** WHILE label is empty THE loading indicator SHALL still render, and in debug builds report that it has no accessible name.
- **LDG-11** WHEN the loading indicator is inserted into a view THE loading indicator SHALL wait for its show delay (LDG-14) before it is shown, and WHILE it waits SHALL draw nothing (no indicator, no container, no shadow), SHALL run no animation, SHALL be absent from the accessibility tree (no role, no name, no busy state), and SHALL still take the box it takes when shown (LDG-2, LDG-3), so that nothing around it moves when it appears; a show delay of 0 SHALL show it as it is inserted.
- **LDG-12** WHEN its show delay ends THE loading indicator SHALL be shown: it SHALL expose itself as LDG-5 says, without announcing itself (LDG-6), start its entrance (LDG-13), and start the turn of LDG-1, or the pulse of LDG-4, at the beginning of a cycle.
- **LDG-13** WHEN the loading indicator is shown THE loading indicator SHALL enter over its enter duration (LDG-14) with comp.loading-indicator.motion.enter-easing, as its entrance names: fade SHALL ease the opacity of the whole component (the indicator and, when contained, the container and its shadow) from 0 to 1, and grow SHALL ease the opacity the same way while scaling the whole component from nothing to its full size about its center, on the same curve over the same time. The entrance SHALL be drawn only: the box SHALL keep its full size throughout, and the entrance SHALL run to its end whether or not the indicator is visible (LDG-7 pauses the cycle, not the entrance). An enter duration of 0 SHALL show the indicator whole at once. WHERE reduced motion is requested THE loading indicator SHALL NOT scale: grow SHALL enter as fade, and on a platform whose reduced-motion setting removes every animation (Android's) the indicator SHALL appear at once.
- **LDG-14** THE loading indicator SHALL take its show delay from showDelay and its enter duration from enterDuration, each a number of milliseconds used as given (the motion dial does not scale it), and its kind of entrance from entrance. WHILE showDelay or enterDuration is null THE loading indicator SHALL use comp.loading-indicator.motion.show-delay or comp.loading-indicator.motion.enter-duration in its place, and WHILE entrance is auto it SHALL use comp.loading-indicator.motion.entrance; IF showDelay or enterDuration is negative or not a finite number, or entrance names a kind the indicator does not know, THE loading indicator SHALL use the token likewise and in debug builds report the value it ignored.
- **LDG-15** THE loading indicator SHALL treat its show delay as a waiting time, not motion: WHERE motion is reduced it SHALL keep it, on every platform, including one whose reduced-motion setting removes every animation; and it SHALL keep counting it while the indicator cannot be seen (scrolled out of view, in a hidden view), because the delay measures the wait, not what is seen.
- **LDG-16** WHEN the loading indicator is removed before its show delay ends THE loading indicator SHALL have drawn, exposed and announced nothing, and WHEN it is removed while shown, during its entrance included, it SHALL disappear at once. WHEN it is inserted again THE loading indicator SHALL wait its whole show delay again; hiding it without removing it (a hidden ancestor, scrolling) SHALL neither restart nor pause the delay (LDG-15). WHEN showDelay, entrance or enterDuration changes while the delay or the entrance is running THE loading indicator SHALL finish the running one as it started and use the new value from its next insertion.
- **LDG-17** THE loading indicator SHALL take its layout direction from where it is used — the direction of the nearest ancestor that sets one, and the platform's own where none does — and SHALL have no direction setting of its own; WHEN that direction changes while the loading indicator is shown THE loading indicator SHALL draw itself for the new direction at once, without a motion of its own, keeping its place in its cycle.
- **LDG-18** WHERE the platform has ARIA attributes (the web), the host carries its progressbar role (LDG-5) itself, so what the product writes on it is not moved, but for a name: WHILE label is not empty THE loading indicator SHALL hold the product's aria-label and aria-labelledby on that host as their data-aria- twins, so that label is its whole name although the platform gives aria-labelledby the precedence, and WHEN label is emptied SHALL give them back as they were written; WHILE label is empty a name the product gives so — aria-label, or aria-labelledby naming an element — SHALL be its name and SHALL NOT be reported as having none in debug builds (LDG-10). WHILE it waits to be shown (LDG-11) it has no role, and SHALL hold them as well, so that nothing names an element with no role, and give them back when it is shown. WHEN the product removes one, or its data-aria- twin, or sets the ARIA property that reflects it (ariaLabel, ariaLabelledByElements) to null, THE loading indicator SHALL take it away.

### Tokens

As written in the token documents (DTCG): an alias in braces names the token it takes its value from, which each design system resolves. On the web each token is a CSS custom property: `--tanrei` and the name after `tanrei`, joined by hyphens (`tanrei.comp.loading-indicator.…` → `--tanrei-comp-loading-indicator-…`).

#### `tanrei.comp.loading-indicator`

| Token | Type | Value |
| --- | --- | --- |
| `comp.loading-indicator.indicator.size.small` | `dimension` | `16px` |
| `comp.loading-indicator.indicator.size.medium` | `dimension` | `24px` |
| `comp.loading-indicator.indicator.size.large` | `dimension` | `40px` |
| `comp.loading-indicator.indicator.thickness.small` | `dimension` | `{tanrei.sys.border.thick}` |
| `comp.loading-indicator.indicator.thickness.medium` | `dimension` | `{tanrei.sys.border.thick}` |
| `comp.loading-indicator.indicator.thickness.large` | `dimension` | `{tanrei.sys.border.thick}` |
| `comp.loading-indicator.indicator.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.loading-indicator.container.size.small` | `dimension` | `24px` |
| `comp.loading-indicator.container.size.medium` | `dimension` | `32px` |
| `comp.loading-indicator.container.size.large` | `dimension` | `48px` |
| `comp.loading-indicator.container.shape` | `dimension` | `{tanrei.sys.shape.corner.full}` |
| `comp.loading-indicator.contained.container.color` | `color` | `{tanrei.sys.color.surface-container-lowest}` |
| `comp.loading-indicator.contained.container.elevation` | `shadow` | `{tanrei.sys.elevation.level2}` |
| `comp.loading-indicator.contained.indicator.color` | `color` | `{tanrei.sys.color.on-surface-variant}` |
| `comp.loading-indicator.motion.cycle-duration` | `duration` | `{tanrei.sys.motion.duration.extra-long4}` |
| `comp.loading-indicator.motion.show-delay` | `duration` | `{tanrei.sys.motion.role.show-delay}` |
| `comp.loading-indicator.motion.entrance` | `number` | `0` |
| `comp.loading-indicator.motion.enter-duration` | `duration` | `{tanrei.sys.motion.role.state.large}` |
| `comp.loading-indicator.motion.enter-easing` | `cubicBezier` | `{tanrei.sys.motion.easing.enter}` |

## ガイドライン

### 使う場面

長さの分からない短い待ち時間に使います。次のページを読み込んでいるリスト、保存中のコントロール、描画中のプレビューなどです。何かが進んでいることは伝えますが、どれだけかかるかは伝えません。

数秒以上かかるかもしれない待ち時間や、どこまで進んだかを測れる待ち時間には、代わりに[プログレスインジケーター](https://tanrei.chnotchy.com/ja/components/progress-indicator/)を使います。おおよそでも値があると、長い待ち時間は耐えやすくなります。

### サイズの選び方

| サイズ | 既定のシステムでのインジケーター / コンテナ | 用途 |
| --- | --- | --- |
| Small | 16 / 24 | 保存中のボタンなど、コントロールの中でアイコンの代わりに。 |
| Medium | 24 / 32 | 1 行のテキストの横。既定値です。 |
| Large | 40 / 48 | 内容を待っている空のセクションや、画面全体。 |

### コンテナ付き

コンテナ付き（contained）は、インジケーターを専用の浮き上がった円の上に描きます。引っ張って更新する操作や、画像やリストの上など、インジケーターがコンテンツに重なる場所で使うと、背後に何があっても見えるようになります。無地のサーフェスの上に置くときは、インラインのままにします。

### 表示までの待ち時間と現れ方

一瞬で終わる待ち時間には、何も表示しないのが最善です。そのため、インジケーターは挿入されてから一定の時間待ち、それから表示されます。既定のシステムでは 300 ミリ秒です。そのあとフェードインします。待ち時間より短く終われば、一瞬だけ現れて消える印がちらつくことはありません。待っている間もインジケーターは表示時と同じ大きさを占めるので、現れたときに周りが動くことはありません。

インジケーターは、待ちが始まったら挿入し、終わったら取り除きます。待ち時間は挿入した時点から数えます。取り除かずに隠して再び見せたインジケーターは待ち直さず、挿入し直したインジケーターは待ち時間を最初から待ちます。

待ち時間と現れ方は、システムがすべてのインジケーターに対して決めます。1 つのインジケーターだけ別の間合いが必要なときに限って、次のプロパティを設定します。

- `showDelay` は待ち時間（ミリ秒）です。`0` にすると挿入した時点で表示します。押したらすぐに応えるべき、処理中のボタンなどに使います。
- `entrance` は `fade` か `grow` です。grow は、中心から大きくなりながらフェードインします。既定値の `auto` はシステムの選択に従います。
- `enterDuration` は現れる動きの長さ（ミリ秒）です。`0` にすると最初から全体を表示します。

設定した値はそのまま使われ、モーションのダイヤルで伸び縮みしません。システムの値を使うなら、プロパティは設定しないままにします。

### 推奨

- 何を読み込んでいるのかを表すラベルを付けます。「読み込み中」ではなく「メッセージを読み込み中」です。横にキャプションを表示するなら、同じ言葉を使います。
- 内容が現れる場所に置きます。内容が届いたとき、利用者はすでに正しい場所を見ています。
- コントロールの中でアイコンの代わりに使うときは、コントロールの大きさを保ち、周りが動かないようにします。
- 1 つのインジケーターだけ別の間合いが必要な場合を除き、待ち時間はシステムに任せます。

### 非推奨

- 測れる待ち時間や、長くなるかもしれない待ち時間に使わない。プログレスインジケーターを使います。
- 1 つの待ち時間に複数のインジケーターを出さない。読み込んでいる領域ごとに 1 つで十分です。
- 画面の一部だけを読み込んでいるのに、画面全体を覆わない。残りの部分は使えるままにします。
- 問題が起きたことを色で伝えない。インジケーターを止めて、メッセージを表示します。
- インジケーターを置いたまま、待ちと待ちの間だけ隠さない。取り除けば、次の待ちで待ち時間が改めて始まります。

## アクセシビリティ

### コンポーネントが担うこと

- 表示されたあとは、プログレスバーのロールをプラットフォームのビジー状態で伝え、ラベルを名前にし、値は伝えません。Flutter にはまだプログレスバーのロールもビジー状態もないため、ラベルを伝えます。
- 表示までの待ち時間の間は、アクセシビリティツリーにまったく現れません。ロールも名前もビジー状態もありません。見えないほど短い待ちは、読み上げられることもありません。
- フォーカスを受けず、現れたときも消えたときも自分から読み上げません。
- 動きを減らす設定のときは、弧の回転を止め、代わりに円全体の不透明度をゆるやかに変化させます（1 秒に 1 回まで）。コンテナは動きません。grow の現れ方はフェードになります。表示までの待ち時間は残ります。これは動きではなく、待つ時間だからです。
- アニメーションは、見えていない間は止まり、見えるようになると再開します。アプリがバックグラウンドにあるときはすべてのプラットフォームで、スクロールして画面外に出たときや隠れた画面にあるときは、プラットフォームがそれを判別できる範囲で止まります。
- 空のラベルは、デバッグビルドで報告します。

### 利用者側で行うこと

- 読み込み中の領域は、待ちが始まったときから、インジケーターの待ち時間を待たずにビジー状態にし（Web では `aria-busy`）、内容が届いたら知らせます。ライブリージョンでメッセージを伝えるか、利用者が求めた内容なら新しい内容へフォーカスを移します。
- 色を変えるときは、インジケーターの色が背後のサーフェスに対して、コンテナ付きのインジケーターの色がコンテナに対して、3:1 以上になるようにします。何かが進んでいることを伝えるのは、動いている印だけだからです。
