メインコンテンツへスキップ
menumenu tanreiexpand_morecontrasttranslate
tanreiexpand_more
はじめにWebReactSwiftUIJetpack ComposeFlutterReact Nativeプリセット自分のデザインシステムを作るAI エージェントと Tanrei を使うブランドデザイントークンデザインシステムとカスタマイズTheme Studioカラータイポグラフィアイコン形状エレベーションモーションインタラクション状態振る舞いレイアウトアクセシビリティ言葉カラートークンタイプスケールトークン形状トークンエレベーショントークンモーショントークン状態トークンスペースと寸法のトークンコンポーネントボタンメインアクションボタンセグメントボタンボタングループメニュー付きボタンツールバーコピーボタンチェックボックスラジオボタンスイッチチップメニュースライダースライダーフィールドカレンダーDateTimePickerフィールドグループラジオグループチェックボックスグループセレクトセレクトフィールドメニューバーファイルアップロードファイルピッカー 上級者向けドロップゾーンテキスト入力欄 上級者向けテキストフィールドチップフィールドフィールドコンボボックス 上級者向けコンボボックスフィールド数値入力欄 上級者向け数値フィールドカラー入力欄 上級者向けカラーフィールドワンタイムコード入力欄 上級者向けワンタイムコードフィールド埋め込みテキスト入力欄 上級者向けチャットコンポーザーカード区切り線モーダルレイヤー 上級者向けダイアログリストリスト行ボトムシートサイドシートカルーセルコードブロックテーブルポップオーバーステップアコーディオン説明リスト図アプリシェルページヘッダーツリービュータイムライン画像プレビューハンドルサイズ変更できる枠スクロールエリアバッジプログレスインジケーターメーターナンバーティッカーローディングインジケータースナックバーツールチップキーボードショートカットタグインラインコードコールアウトアバター空の状態タググループスケルトンファイル項目色見本マーキーテキストタブタブビューアプリバーナビゲーションバーナビゲーションドロワー検索リンクページネーション前後のリンクステッパーパンくずリスト目次リンクカードFAB メニューステータスTanrei の作り方コントリビューションライセンスと帰属
ホームコンポーネントツールチップ

ツールチップ

ポインターを載せたとき、キーボードでフォーカスしたとき、長押ししたときに、要素を短い言葉で説明します。フォーカスを奪わず、何もふさぎません。

content_copyMarkdown で表示
カテゴリ
コミュニケーション
トークン
comp.tooltip
レベル
基礎

概要

Web
tanrei-tooltip
schedule
SwiftUI
tanreiTooltip
schedule
Jetpack Compose
TanreiTooltip
schedule
Flutter
TanreiTooltip
schedule
React Native
Tooltip
schedule

生成元 docs/status/status.json, specs/tooltip.spec.json

ボタンにポインターを載せるか、フォーカスしてください:プレーンとリッチのツールチップ

deleteinfo

使い方

ツールチップを付ける起点(アンカー)は、アイコンボタン、省略表示されたラベル、グラフの点など、利用者側の要素です。ツールチップはアンカーに説明を加えるだけで、それ以外は何も変えません。Web、Compose、Flutter、React Native ではアンカーを包み、SwiftUI ではアンカーに付けるモディファイアーです。

ツールチップは、ホバー、キーボードフォーカス、長押しで自分から開閉し、そのことを toggle で知らせます。open を設定するのは、初回のヒントのように利用者側から表示したいときだけです。設定しても何も発火せず、その後もツールチップは自分で開閉を続けます。

describes(既定は true)は、テキストをアンカーのアクセシブルな説明にします。ラベルと同じ言葉を表示するアイコンボタンのように、テキストがアンカー自身の名前と重なるときは false にして、スクリーンリーダーが同じ言葉を 2 回読まないようにします。

Web と React の例は現在の仕様に沿っています。SwiftUI、Compose、Flutter、React Native は追従待ちで、以前の版のツールチップをまだ提供しています。その版は、配置が中央揃えの 4 つだけで、ツールチップごとの遅延、オフセット、矢印、もう 1 つの見た目、ショートカットがなく、大きさを変えずにフェードします。これらの例は、追従するまでその版のものです。

<!-- ツールチップはアンカーを包みます。no-describe は describes を false にします:テキストがラベルと同じなので。 -->
<tanrei-tooltip text="ゴミ箱に移動" no-describe>
  <tanrei-button variant="text" label="ゴミ箱に移動"><span slot="icon">delete</span></tanrei-button>
</tanrei-tooltip>

<!-- 名前を早く見せたい、項目の多いツールバー。遅延はミリ秒で指定します。 -->
<tanrei-tooltip text="太字" show-delay="150" no-describe>
  <tanrei-button variant="text" label="太字"><span slot="icon">format_bold</span></tanrei-button>
</tanrei-tooltip>

<!-- 名前の後ろに、そのコマンドのキーを表示します。支援技術にはアンカーが伝えます。 -->
<tanrei-tooltip text="太字" shortcut="mod+b" no-describe>
  <tanrei-button variant="text" label="太字"><span slot="icon">format_bold</span></tanrei-button>
</tanrei-tooltip>

<!-- コントロールの下に、先頭側の端をそろえて、矢印付きで、反転ではなくサーフェスの上に描きます。 -->
<tanrei-tooltip text="日付や所有者で絞り込みます" placement="bottom-start" arrow inverse="false">
  <tanrei-button variant="text" label="絞り込み"><span slot="icon">filter_list</span></tanrei-button>
</tanrei-tooltip>

<tanrei-tooltip id="storage" variant="rich" headline="ストレージ" placement="bottom"
                text="ゴミ箱の中のファイルも、削除されるまではストレージの使用量に含まれます。">
  <tanrei-button variant="text" label="ストレージについて"><span slot="icon">info</span></tanrei-button>
</tanrei-tooltip>

<script type="module">
  import '@tanrei/web/components/tooltip';
  const storage = document.querySelector('#storage');
  // 初回のヒントです。open を設定しても何も発火しません。
  storage.open = true;
  // toggle は、ツールチップが自分で開閉したこと(ホバー、フォーカス、長押し、Escape、時間切れ、別のツールチップ)を知らせます。
  storage.addEventListener('toggle', (e) => console.log(e.detail.open));
</script>
import { Button, Tooltip } from '@tanrei/react';

<Tooltip text="ゴミ箱に移動" describes={false}>
  <Button variant="text" label="ゴミ箱に移動" onClick={trash}><span slot="icon">delete</span></Button>
</Tooltip>
{/* 名前の後ろに、そのコマンドのキーを表示します。文字列 1 つが 1 つの組み合わせです。 */}
<Tooltip text="太字" shortcut={['mod+b']} describes={false}>
  <Button variant="text" label="太字" onClick={bold}><span slot="icon">format_bold</span></Button>
</Tooltip>
{/* コントロールの下に、先頭側の端をそろえて、矢印付きで、反転ではなくサーフェスの上に描きます。 */}
<Tooltip text="日付や所有者で絞り込みます" placement="bottom-start" arrow inverse={false}>
  <Button variant="text" label="絞り込み" onClick={filter}><span slot="icon">filter_list</span></Button>
</Tooltip>
<Tooltip
  variant="rich"
  headline="ストレージ"
  text="ゴミ箱の中のファイルも、削除されるまではストレージの使用量に含まれます。"
  placement="bottom"
  touchTimeout={4000} // 数文あるので、長押しの後に読む時間を長めに取ります
  onToggle={(e) => log(e.detail.open)}
>
  <Button variant="text" label="ストレージについて"><span slot="icon">info</span></Button>
</Tooltip>
import Tanrei

// アンカーに付けるモディファイアーです。ツールチップは、上位に付けた .tanreiTheme(_:) が用意する層に描かれます。
// 説明はアンカーの accessibilityHint になります。
TanreiIconButton("ゴミ箱に移動", action: trash) { Image(systemName: "trash") }
    .tanreiTooltip("ゴミ箱に移動", describes: false)

// isPresented が open です。ツールチップが自分で開閉したときは、ツールチップが書き込みます。
TanreiIconButton("ストレージについて", action: {}) { Image(systemName: "info.circle") }
    .tanreiTooltip("ゴミ箱の中のファイルも、削除されるまではストレージの使用量に含まれます。",
                   title: "ストレージ", variant: .rich, placement: .bottom, isPresented: $showHint)
import io.github.chnotchy.tanrei.components.TanreiIconButton
import io.github.chnotchy.tanrei.components.TanreiTooltip
import io.github.chnotchy.tanrei.components.TanreiTooltipPlacement
import io.github.chnotchy.tanrei.components.TanreiTooltipVariant
import io.github.chnotchy.tanrei.components.rememberTanreiTooltipState
import io.github.chnotchy.tanrei.primitives.TanreiText

// Material の TooltipBox と同じく、ツールチップがアンカーを包みます。
TanreiTooltip(text = "ゴミ箱に移動", describes = false) {
    TanreiIconButton(onClick = ::trash, contentDescription = "ゴミ箱に移動") { TanreiText("delete") }
}

// state が open です。onToggle にはツールチップが自分で開閉したときだけ届き、show() と dismiss() では何も発火しません。
val hint = rememberTanreiTooltipState(onToggle = { open -> log(open) })
TanreiTooltip(
    text = "ゴミ箱の中のファイルも、削除されるまではストレージの使用量に含まれます。",
    variant = TanreiTooltipVariant.Rich,
    title = "ストレージ",
    placement = TanreiTooltipPlacement.Bottom,
    state = hint,
) {
    TanreiIconButton(onClick = {}, contentDescription = "ストレージについて") { TanreiText("info") }
}
LaunchedEffect(Unit) { hint.show() } // 初回のヒント
import 'package:tanrei/tanrei.dart';

// show() と hide() は open を設定し、リスナーには知らせません。リスナーには、ツールチップが自分で開閉したときに届きます。
final TanreiTooltipController hint = TanreiTooltipController();
hint.addListener(() => log(hint.isOpen));

// Flutter の Tooltip と同じ名前です。message がテキストで、excludeFromSemantics は describes を反転したものです。
TanreiTooltip(
  message: 'ゴミ箱に移動',
  excludeFromSemantics: true, // テキストがボタン自身の名前と同じ
  child: TanreiIconButton(onPressed: trash, icon: const Icon(AppSymbols.delete), semanticLabel: 'ゴミ箱に移動'),
);
TanreiTooltip(
  variant: TanreiTooltipVariant.rich,
  title: 'ストレージ',
  message: 'ゴミ箱の中のファイルも、削除されるまではストレージの使用量に含まれます。',
  placement: TanreiTooltipPlacement.bottom,
  controller: hint, // 初回のヒントには hint.show()
  child: TanreiIconButton(onPressed: info, icon: const Icon(AppSymbols.info), semanticLabel: 'ストレージについて'),
);
import { IconButton, Tooltip } from '@tanrei/react-native';

// アンカーは、Pressable の props を受け取る子要素 1 つです。ツールチップはアンカー自身のハンドラーを先に呼びます。
<Tooltip text="ゴミ箱に移動" describes={false}>
  <IconButton accessibilityLabel="ゴミ箱に移動" onPress={trash}>delete</IconButton>
</Tooltip>

// open はコードから表示するためのもので、何も発火しません。onOpenChange には自分で開閉したときに届きます。
<Tooltip
  variant="rich"
  headline="ストレージ"
  text="ゴミ箱の中のファイルも、削除されるまではストレージの使用量に含まれます。"
  placement="bottom"
  open={showHint}
  onOpenChange={setShowHint}
>
  <IconButton accessibilityLabel="ストレージについて">info</IconButton>
</Tooltip>

仕様

アンカーの下に出るプレーンなツールチップと、タイトルのないリッチなツールチップ

deleteinfo

構成要素

パーツ 説明
1 anchor The element the tooltip describes: the product's own (an icon-only Button, a truncated label, a chart mark). The tooltip adds a description to it and changes nothing else about it.
2 container The surface that holds the text, placed on one side of the anchor and aligned along it.
3 arrow · 任意 A triangle on the container's edge that faces the anchor, pointing at it; one shape with the container.
4 title · 任意 A short heading above the supporting text. Rich tooltips only.
5 supporting-text The description. A plain tooltip holds nothing else.
6 shortcut · 任意 The keys that perform the anchor's command, after the text. Plain tooltips only.

プロパティ

プロパティ 型 既定値 説明
variant plain | rich "plain" Plain is one short phrase, by default on the inverse surface, for naming what an icon does. Rich is an optional title and a few sentences, by default on a raised surface, for explaining a term or a setting. A structural choice drawn by code, not a token axis: a design system cannot add a form by values.
text string "" The description. Required.
title string "" A heading for a rich tooltip. Ignored by a plain one.
shortcut strings The keyboard shortcut of the anchor's command, in the keyboard shortcut component's key names: one chord per string, pressed in order (["mod+b"]). A plain tooltip draws it after the text for the keyboard the person uses and exposes it as the anchor's keyboard shortcut (TIP-27); a rich one ignores it. It does not make the keys work; the product binds them.
placement top | top-start | top-end | bottom | bottom-start | bottom-end | start | start-top | start-bottom | end | end-top | end-bottom | auto "top" Where the tooltip prefers to stand: a side of the anchor, and how it lines up along that side (TIP-22). top and bottom centre it above or below the anchor; with -start or -end its start or end edge meets the anchor's. start and end centre it beside the anchor; with -top or -bottom its top or bottom edge meets the anchor's. auto takes the first side with room (TIP-23). Start and end follow the layout direction. The tooltip flips to the opposite side when the preferred one has no room, keeping the alignment (TIP-9).
offset number null The distance between the anchor's edge and the tooltip, in the platform's layout units; 0 lets them touch. Null takes comp.tooltip.offset, the system's answer for every tooltip. Set it only where one anchor's box is not what the eye takes for its edge (a control with a large invisible hit area, a mark of a chart).
crossOffset number 0 How far the tooltip moves along the side it stands on, in the platform's layout units, from where placement puts it: positive towards the end for a tooltip above or below, towards the bottom for one beside. For lining an aligned tooltip up with what is drawn inside the anchor rather than with the anchor's box. It never moves the tooltip off the anchor's side (TIP-24).
arrow boolean null Whether the tooltip draws an arrow that points at the anchor. Null, the default, takes comp.tooltip.arrow.visible, the system's choice, which is off in the default system (TIP-25).
inverse boolean null Whether this tooltip is drawn inverse: in the opposite scheme's colors, so that it stands apart from whatever it lands on. False draws it on a surface of the scheme. Null, the default, takes its form's choice: comp.tooltip.plain.container.inverse (on in the default system) or comp.tooltip.rich.container.inverse (off). It changes colors and elevation only (TIP-26).
describes boolean true Whether assistive technology reads the text as the anchor's description. Set it to false when the text repeats the anchor's own accessible name (an icon-only Button whose tooltip shows its label), so that it is not read twice.
open boolean false Whether the tooltip is showing. The tooltip opens and closes itself on hover, focus and long-press and reports it (toggle). A product sets it to show a tooltip on its own (a first-run hint); a snapshot harness sets it to draw a fixture.
showDelay number null How long, in milliseconds, a fine pointer rests on the anchor before this tooltip opens; 0 opens it at once. Null takes comp.tooltip.motion.show-delay, the system's answer for every tooltip. Set it only where one tooltip needs another pace than the rest (a dense toolbar that wants its names sooner, a chart whose marks the pointer sweeps across and should not flash). It applies to hover alone: keyboard focus still opens at once.
hideDelay number null How long, in milliseconds, this tooltip waits after the pointer has left the anchor, the gap and the tooltip before it closes; 0 closes it at once. Null takes comp.tooltip.motion.hide-delay. The tooltip stays hoverable at any value, 0 included, because the gap between the anchor and the tooltip counts as hover (TIP-1).
touchTimeout number null How long, in milliseconds, a tooltip opened by a long-press stays after the finger lifts; 0 closes it as the finger lifts, so it shows for as long as the press lasts. Null takes comp.tooltip.timeout.touch. Set it only where one tooltip needs more or less reading time on touch than the rest (a rich tooltip of a few sentences, a one-word name).

スロット

スロット 説明
default The anchor.

イベント

イベント 説明
toggle The tooltip opened or closed by itself (hover, focus, long-press, Escape, the touch timeout, another tooltip opening); carries the new open value.

状態

振る舞い

EARS 記法による要件です。各プラットフォームの実装は、この ID に対応するテストで検証されます。要件の原文は仕様ファイルのものをそのまま掲載しています。

ID 要件
TIP-1 WHEN a fine pointer enters the anchor and stays on it for the show delay (TIP-17) THE tooltip SHALL open; WHEN the pointer leaves the anchor THE tooltip SHALL stay open while the pointer crosses the gap to the tooltip or moves onto the tooltip itself, and SHALL close once the pointer has been off the anchor, the gap and the tooltip for the hide delay (TIP-17). The gap is the strip between the anchor's edge and the container — the offset deep (TIP-24), and comp.tooltip.arrow.height deeper while an arrow is drawn (TIP-25) — as wide as the container along that side; it counts for hover alone, is not drawn and takes no press.
TIP-2 WHEN the anchor receives keyboard focus THE tooltip SHALL open without delay, and WHEN the anchor loses focus THE tooltip SHALL close; focus gained by pointer or touch SHALL NOT open it.
TIP-3 WHEN the user long-presses the anchor by touch THE tooltip SHALL open without activating the anchor, and WHEN the finger lifts SHALL stay open for the touch timeout (TIP-17) and then close; a touch anywhere else SHALL close it sooner.
TIP-4 WHILE the tooltip is open, WHEN the user presses Escape THE tooltip SHALL close without moving focus and SHALL consume the key, so that it does not also close an enclosing dialog; it SHALL stay closed until the anchor is entered by the pointer or focused again.
TIP-5 WHILE the pointer is over the anchor or the tooltip, or the anchor has keyboard focus, THE tooltip SHALL stay open with no time limit, until Escape (TIP-4), an activation of the anchor (TIP-6) or another tooltip opening (TIP-8) closes it.
TIP-6 WHEN the anchor is activated (a click, a tap, Enter or Space) THE tooltip SHALL close, and SHALL NOT open again from the same hover until the pointer has left the anchor and entered it again.
TIP-7 WHEN the tooltip opens or closes by itself (TIP-1 to TIP-6, TIP-8) THE tooltip SHALL emit toggle with the new value; WHEN the product sets open THE tooltip SHALL show or hide accordingly without emitting toggle, and SHALL then keep following TIP-1 to TIP-6.
TIP-8 WHEN a tooltip opens while another tooltip is open THE other tooltip SHALL close at once, and the new one SHALL open without waiting for its show delay (TIP-17), because the person is already reading tooltips; at most one tooltip SHALL be open in a window.
TIP-9 THE tooltip SHALL place its container on the side of the anchor and with the alignment that placement names (TIP-22, TIP-23), the offset away from the anchor's edge and moved along the side by crossOffset (TIP-24), and SHALL keep it at least comp.tooltip.window-margin inside the window's safe area by these steps, in this order: WHEN the preferred side has no room for the container and the opposite side has, it SHALL flip to the opposite side, keeping the alignment; WHEN neither has room it SHALL use the one of the two with more room; it SHALL then shift along the side as far as the margin asks and no farther; and WHERE the container is still larger than the room on its side (a window smaller than the tooltip) it SHALL move the container back into the margin over the anchor, its top and start edges first, rather than leave the window. It SHALL follow the anchor when the anchor moves (scrolling, resizing, layout), taking these steps again from the side it is on, which it SHALL keep for as long as that side has room, so that a page that scrolls does not throw the tooltip from side to side; and it SHALL close WHEN the anchor is removed, hidden, scrolled out of the window, or scrolled out of sight inside a scrolling ancestor so that no part of it can be seen.
TIP-10 WHILE variant is plain THE tooltip SHALL draw text in comp.tooltip.plain.supporting-text.typescale and comp.tooltip.plain.supporting-text.color on a container of comp.tooltip.plain.container.color with comp.tooltip.plain.container.shape corners and comp.tooltip.plain.container.elevation — or, WHILE it is drawn on a surface, in the colors and the elevation TIP-26 names in their place — padded by comp.tooltip.plain.container.padding-inline and comp.tooltip.plain.container.padding-block, at least comp.tooltip.plain.container.min-height tall and at most comp.tooltip.plain.container.max-width wide, wrapping longer text onto further lines; it SHALL draw its shortcut as TIP-27 says and SHALL ignore title.
TIP-11 WHILE variant is rich THE tooltip SHALL draw title in comp.tooltip.rich.title.typescale and comp.tooltip.rich.title.color above text in comp.tooltip.rich.supporting-text.typescale and comp.tooltip.rich.supporting-text.color, comp.tooltip.rich.container.gap apart, on a container of comp.tooltip.rich.container.color with comp.tooltip.rich.container.shape corners and comp.tooltip.rich.container.elevation — or, WHILE it is drawn inverse, in the colors and the elevation TIP-26 names in their place — padded by comp.tooltip.rich.container.padding-inline and comp.tooltip.rich.container.padding-block and at most comp.tooltip.rich.container.max-width wide; WHILE title is empty it SHALL draw the supporting text alone. THE tooltip SHALL set the title's letter-spacing by the script of its text, as every title is set (PRS-20): no negative letter-spacing WHERE its language is Chinese, Japanese or Korean, and none WHERE its script joins its letters or stacks marks on them.
TIP-12 THE tooltip SHALL ease its container corners by sys.shape.corner-smoothing (0 is a quarter circle, 0.6 the iOS continuous corner; a platform with only the OS curve draws it for any amount above 0), SHALL grow with enlarged text without clipping it, and SHALL start-align its text by the layout direction.
TIP-13 THE tooltip SHALL NOT take focus, SHALL hold nothing focusable or pressable, SHALL NOT make anything inert or dimmed, and SHALL be drawn above every other layer of its window, including an open dialog and its scrim; a press on the tooltip SHALL close it and SHALL NOT reach what lies under it.
TIP-14 WHILE describes is true THE tooltip SHALL expose text — preceded by title for a rich tooltip — as the anchor's accessible description, whether or not the tooltip is open, and SHALL keep the drawn tooltip from being read a second time; WHILE describes is false it SHALL add nothing to the anchor's semantics. In both cases it SHALL NOT change the anchor's accessible name or role.
TIP-15 WHEN the tooltip opens THE tooltip SHALL appear as every surface appears: from sys.motion.appear.opacity, sys.motion.appear.scale, sys.motion.appear.distance toward its anchor and sys.motion.appear.blur to whole, grown from the middle of its edge that faces the anchor — from its own centre WHERE sys.motion.appear.origin is center, and WHILE it covers its anchor (TIP-9) — over comp.tooltip.motion.enter-duration with comp.tooltip.motion.enter-easing, so that it comes out of the anchor's side whichever side it is on; WHEN it closes THE tooltip SHALL take it back the same way to where it started, over comp.tooltip.motion.exit-duration with comp.tooltip.motion.exit-easing. WHERE sys.behavior.appearance is unfold the scale SHALL apply along the axis that leaves the anchor only. The motion SHALL be drawn only: the place TIP-9 gives, the gap of TIP-1 and the room the tooltip is measured for are those of the tooltip at rest. WHERE motion is reduced THE tooltip SHALL only fade in and out, over sys.motion.role.reduced, with no scale, distance or blur, or appear and disappear at once on a platform whose reduced-motion setting removes every animation (Android's). WHEN it is mounted with open true THE tooltip SHALL appear without its motion while the document has not yet been first painted, and with it when it is mounted after that. A tooltip that takes over from another SHALL NOT move at all (TIP-28).
TIP-16 WHILE text is empty THE tooltip SHALL NOT open and SHALL add nothing to the anchor's semantics, and in debug builds SHALL report the empty text; WHILE variant is plain and title is set it SHALL report the ignored title in debug builds.
TIP-17 THE tooltip SHALL take its show delay from showDelay, its hide delay from hideDelay and its touch timeout from touchTimeout, each a number of milliseconds used as given (the motion dial does not scale it), so that 0 opens or closes at once: a show delay of 0 opens as the pointer enters, a hide delay of 0 closes as the pointer leaves the anchor, the gap and the tooltip, and a touch timeout of 0 closes as the finger lifts, after the tooltip has shown for the whole long-press. WHILE one of them is null THE tooltip SHALL use comp.tooltip.motion.show-delay, comp.tooltip.motion.hide-delay or comp.tooltip.timeout.touch in its place, and IF one is negative or not a finite number THE tooltip SHALL use that token likewise and in debug builds report the value it ignored.
TIP-18 THE tooltip SHALL treat its show delay, hide delay and touch timeout as waiting times, not motion: WHERE motion is reduced it SHALL keep them, on every platform, including one whose reduced-motion setting removes every animation. The show and hide delays SHALL govern hover alone (TIP-1), and the touch timeout the time after a long-press alone (TIP-3): keyboard focus (TIP-2), another tooltip being open (TIP-8) and the product setting open (TIP-7) SHALL NOT wait for any of them, and the long-press itself SHALL take the platform's own duration. WHEN showDelay, hideDelay or touchTimeout changes while that wait is running THE tooltip SHALL finish the running wait and use the new value from the next time that wait starts.
TIP-19 THE tooltip 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 tooltip is shown THE tooltip SHALL draw itself for the new direction at once, without a motion of its own, keeping its open state, the container placed again by the new direction (TIP-20).
TIP-20 WHEN the layout direction is right-to-left THE tooltip SHALL take the side start as the anchor's right side and the side end as its left, SHALL take the alignments -start and -end of a tooltip above or below as the right and the left edges, and a positive crossOffset of such a tooltip as a move towards the left, flipping and shifting from there as TIP-9 says; it SHALL leave the sides top and bottom, the alignments -top and -bottom, a crossOffset beside the anchor and the centring on the anchor as they are, SHALL keep auto's order of sides by their names (TIP-23), SHALL start-align a rich tooltip's title and text at the right (TIP-12) and SHALL draw a plain tooltip's shortcut at the left of its text (TIP-27).
TIP-21 THE tooltip SHALL lay out its title and its text as text of its own direction, isolated from the text around it: each takes the base direction of its first strong character, or the layout direction where it has none, so that a left-to-right word, a number with its sign or unit, a telephone number and an address keep their own order in a right-to-left layout and reorder nothing beside them; THE tooltip SHALL still place each and align its lines by the layout direction, not by the text's own.
TIP-22 THE tooltip SHALL read placement as a side and an alignment along that side: top and bottom SHALL put the container above or below the anchor with its centre on the anchor's, top-start and bottom-start with its start edge on the anchor's start edge, and top-end and bottom-end with its end edge on the anchor's end edge; start and end SHALL put it beside the anchor, on the side the layout direction gives that name, with its centre on the anchor's, start-top and end-top with its top edge on the anchor's top edge, and start-bottom and end-bottom with its bottom edge on the anchor's bottom edge. WHEN placement names a value the tooltip does not define THE tooltip SHALL place itself as the default value says and in debug builds report the unknown value; WHEN placement changes while the tooltip is shown THE tooltip SHALL take its new place at once, without a motion.
TIP-23 WHILE placement is auto THE tooltip SHALL try the sides in the order top, bottom, end, start and SHALL take the first that has room for the container within comp.tooltip.window-margin, or the one with the most room WHEN none has, with its centre on the anchor's, and SHALL then shift, hold and follow as TIP-9 says, keeping the side it took for as long as that side has room.
TIP-24 THE tooltip SHALL take the distance between the anchor's edge and the nearest part of itself — the container's edge, or the arrow's tip while an arrow is drawn — from offset, a number of the platform's layout units used as given, so that 0 lets the two touch, and from comp.tooltip.offset WHILE offset is null; it SHALL then move the container along its side by crossOffset, before the shift of TIP-9, and by no more than leaves comp.tooltip.arrow.width of the container beside the anchor, so that the tooltip still stands on the anchor's side and an arrow can still reach the anchor. IF offset is negative or not a finite number THE tooltip SHALL use comp.tooltip.offset in its place, IF crossOffset is not a finite number THE tooltip SHALL use 0, and in debug builds it SHALL report the value it ignored.
TIP-25 THE tooltip SHALL draw an arrow WHILE arrow is true, and WHILE arrow is null and comp.tooltip.arrow.visible is on, and none otherwise. WHILE it draws one THE tooltip SHALL draw a triangle comp.tooltip.arrow.width wide at its base and comp.tooltip.arrow.height tall on the container's edge that faces the anchor, as one shape with the container — the container's color and elevation, with no seam and no shadow between the two — its tip pointing at the middle of the part of the anchor's edge that the container stands beside, and SHALL keep its base at least comp.tooltip.arrow.inset, and at least the corner's radius, from each end of that edge, standing at the edge's middle where the edge is too short for both; the container SHALL stand comp.tooltip.arrow.height farther from the anchor, so that the tip keeps the offset (TIP-24). WHEN the container flips, shifts or follows the anchor (TIP-9) the arrow SHALL move with it to the edge that now faces the anchor and point at the anchor again, and WHILE the container covers the anchor (TIP-9) THE tooltip SHALL draw no arrow.
TIP-26 THE tooltip SHALL draw itself inverse — in the opposite scheme's colors — WHILE inverse is true, on a surface of the scheme WHILE inverse is false, and WHILE inverse is null as its form's choice says: comp.tooltip.plain.container.inverse for a plain tooltip and comp.tooltip.rich.container.inverse for a rich one. The tokens TIP-10 and TIP-27 name are a plain tooltip's inverse look: WHILE drawn on a surface a plain tooltip SHALL take comp.tooltip.plain.surface.container.color, comp.tooltip.plain.surface.container.elevation, comp.tooltip.plain.surface.supporting-text.color and comp.tooltip.plain.surface.shortcut.color in their place. The tokens TIP-11 names are a rich tooltip's surface look: WHILE drawn inverse a rich one SHALL take comp.tooltip.rich.inverse.container.color, comp.tooltip.rich.inverse.container.elevation, comp.tooltip.rich.inverse.title.color and comp.tooltip.rich.inverse.supporting-text.color in place of those TIP-11 names; its shape, padding, sizes, typescales, arrow, placement, motion and timing SHALL be the same either way, and WHEN inverse changes while the tooltip is shown THE tooltip SHALL draw the other look at once, keeping its open state.
TIP-27 WHILE variant is plain and shortcut is not empty THE tooltip SHALL draw the shortcut after the text, comp.tooltip.plain.container.gap from it and on the text's first line, with the keyboard shortcut component hosted as KBS-13 says: in its plain form, at size small, with convention auto, its legends and separators in comp.tooltip.plain.supporting-text.typescale and comp.tooltip.plain.shortcut.color; the shortcut SHALL stay whole on one line while the text wraps beside it, and the two with the gap SHALL fit within comp.tooltip.plain.container.max-width. THE tooltip SHALL expose the shortcut as the anchor's keyboard shortcut in the form KBS-12 gives (aria-keyshortcuts on the web) where the platform has that property and the anchor does not state one itself, and otherwise, WHILE describes is true, SHALL add the text KBS-10 gives to the end of the anchor's description; it SHALL NOT make the keys work. WHILE variant is rich THE tooltip SHALL ignore shortcut and in debug builds report it.
TIP-28 WHEN a tooltip opens while another tooltip of its window is open or still fading out, or within comp.tooltip.motion.warm-duration after one closed because the pointer or the keyboard focus left its anchor THE tooltip SHALL appear whole in its place at once, without the motion of TIP-15, and the other SHALL disappear at once, without its own; WHEN it is a pointer that opens it within that time THE tooltip SHALL NOT wait for its show delay either, as TIP-8 says of a tooltip that opens while another is open. A tooltip closed by Escape (TIP-4), by an activation of its anchor (TIP-6) or by a press on it (TIP-13) SHALL start no such time, so that the next tooltip waits and moves as a first one does. The time is a waiting time: WHERE motion is reduced it SHALL still apply (TIP-18).
TIP-29 WHEN the tooltip is told to close while it is still entering, or to open while it is still leaving THE tooltip SHALL turn round from the opacity, the size and the position it has reached, without first jumping to the start or the end of either motion, and SHALL take no longer over the rest than the whole duration of the motion it turns to. WHILE the tooltip is closed or leaving THE tooltip SHALL take no pointer input — a press reaches what lies under it, and the pointer on it is not hover (TIP-1) — and WHILE it is closed it SHALL take no room in the layout and draw nothing, so that a tooltip nobody can see never stands in the way of what is under it.

プラットフォーム別の名前対応

パリティは API が同一であることを意味しません。同じ契約を、各プラットフォームの流儀で表現します。

仕様 Web SwiftUI Jetpack Compose Flutter React Native
コンポーネント tanrei-tooltip tanreiTooltip TanreiTooltip TanreiTooltip Tooltip
variant · プロパティ variant variant variant variant variant
text · プロパティ text text text message text
title · プロパティ headline title title title title
shortcut · プロパティ shortcut shortcut shortcut shortcut shortcut
placement · プロパティ placement placement placement placement placement
offset · プロパティ offset offset offset offset offset
crossOffset · プロパティ crossOffset crossOffset crossOffset crossOffset crossOffset
arrow · プロパティ arrow arrow arrow arrow arrow
inverse · プロパティ inverse inverse inverse inverse inverse
describes · プロパティ describes describes describes excludeFromSemantics describes
open · プロパティ open isPresented state controller open
showDelay · プロパティ showDelay showDelay showDelayMillis waitDuration showDelay
hideDelay · プロパティ hideDelay hideDelay hideDelayMillis exitDuration hideDelay
touchTimeout · プロパティ touchTimeout touchTimeout touchTimeoutMillis showDuration touchTimeout
toggle · イベント toggle isPresented state controller onOpenChange
備考 Wraps its anchor (the default slot). showDelay, hideDelay and touchTimeout are number properties in milliseconds, null by default, set by the show-delay, hide-delay and touch-timeout attributes; an attribute that is empty or does not parse as a finite number is not a number (TIP-17), so show-delay="" takes the token and does not open at once. The gap of TIP-1 is measured, not drawn: the document's pointer-move listener counts a point inside it as over the tooltip, so nothing in the top layer covers what lies under the gap. The drawn tooltip is a popover="manual" element in the shadow root, so it is in the top layer (above an open modal <dialog>); it is placed with CSS anchor positioning and position-try flips where supported, and by measuring otherwise. describes is a boolean property that defaults to true; its attribute is the inverse no-describe, because an HTML boolean attribute cannot default to true. toggle is a CustomEvent whose detail.open is the new value. Snapshot harness: each fixture is a stage of 320 × 200 with a medium icon-only text tanrei-button labelled after the fixture's glyph at its centre, and the tooltip open beside it. placement takes the thirteen values as written (top-start, end-bottom, auto); offset and crossOffset are number properties in CSS pixels (attributes offset, cross-offset), parsed as the delays are, so an empty attribute is not a number. arrow and inverse are properties that are true, false or null: the attribute is absent for null, present for true and ="false" for false, as the snackbar's inverse is; null reads the choice from the cascade. shortcut is an array property; its attribute holds the chords separated by spaces, as the keyboard shortcut's keys does, and the shortcut reaches the anchor's focusable element as aria-keyshortcuts unless that element already has one. The container and the arrow are one painted shape, so the elevation's ring and shadows run round both. The enter and exit motion runs on a wrapper inside the popover whose transform origin is set from the placement that was taken; the popover element itself keeps its placed box, so the measured gap and the following do not see the motion. The time tooltips stay warm is kept per window, beside the one showing tooltip. Snapshot harness: the stage sets data-tanrei-keyboard-convention="standard", so plain-shortcut reads the same on every machine. Lagging: has no delay or timeout parameters and no hover gap; owes TIP-1 (the gap), TIP-3 and TIP-8's wording, TIP-17 and TIP-18. The target: a view modifier .tanreiTooltip(_ text:, title:, variant:, placement:, describes:, showDelay: Duration? = nil, hideDelay: Duration? = nil, touchTimeout: Duration? = nil, isPresented: Binding<Bool>?); a Duration carries its own unit (.milliseconds(300)), nil takes the token, and a negative one takes the token and is reported in debug builds. The description is the anchor's accessibilityHint. The tooltip is drawn in an overlay above the window's content (anchored with anchor preferences), not with .popover, which is modal at compact widths and moves VoiceOver focus: a non-modal overlay must do neither. Hover is onContinuousHover (iPadOS and macOS pointers), keyboard focus is focused with full keyboard access, long-press is a LongPressGesture that does not fire the anchor's action. Snapshot harness: renders the anchor and the open tooltip in a fixed 320 × 200 frame. Also owed: the thirteen placement values, offset, crossOffset, arrow, inverse and shortcut; TIP-9 and TIP-15 as they read now and TIP-22 to TIP-29. offset and crossOffset are CGFloat? and CGFloat in points; arrow and inverse are Bool?, nil taking the system's choice; shortcut is [String], exposed with the anchor's keyboardShortcut semantics where it has one and in the hint otherwise. The container and the arrow are one Shape. Snapshot harness: the stage sets the standard keyboard convention for plain-shortcut. Not yet: the title's letter-spacing by the script of its text (TIP-11, PRS-20). Lagging: has no delay or timeout parameters and no hover gap; owes TIP-1 (the gap), TIP-3 and TIP-8's wording, TIP-17 and TIP-18. The target: TanreiTooltip(text, modifier, variant, title, placement, describes, showDelayMillis: Long? = null, hideDelayMillis: Long? = null, touchTimeoutMillis: Long? = null, state = rememberTanreiTooltipState()) { anchor }, shaped like Material's TooltipBox, the times named in milliseconds as ViewConfiguration.longPressTimeoutMillis is; null takes the token, a negative value takes the token and is reported in debug builds. The waits are coroutine delays, not animations, so the animator duration scale of 0 (Android's reduced motion) does not remove them (TIP-18); state.isVisible is open, and state.show() / state.dismiss() set it. The tooltip is a Popup with PopupProperties(focusable = false). Compose has no separate description semantics: the text is appended to the anchor's merged semantics after its own name, as Android reads a View's tooltip text, and a test asserts the order. Snapshot harness: renders the anchor and the open tooltip in a fixed 320 × 200 frame. Also owed: the thirteen placement values, offset, crossOffset, arrow, inverse and shortcut; TIP-9 and TIP-15 as they read now and TIP-22 to TIP-29. offset and crossOffset are Dp? and Dp; arrow and inverse are Boolean?, null taking the system's choice; shortcut is List<String>. The container and the arrow are one Shape, so the shadow follows both. The warm time is a coroutine delay, kept at an animator duration scale of 0 (TIP-28). Snapshot harness: the stage sets the standard keyboard convention for plain-shortcut. Not yet: the title's letter-spacing by the script of its text (TIP-11, PRS-20). Lagging: has no delay or timeout parameters and no hover gap; owes TIP-1 (the gap), TIP-3 and TIP-8's wording, TIP-17 and TIP-18. Named as Flutter's Tooltip: message is the text, excludeFromSemantics is the inverse of describes, the delays are waitDuration and exitDuration, and the touch timeout is showDuration, which Flutter's Tooltip uses for the time after a long-press is released (each a Duration?: null takes the token, a negative one takes the token and is reported in debug builds). The description is the anchor's Semantics(tooltip:). The tooltip is drawn through an OverlayPortal (the overlay of the nearest Overlay, so above a dialog route opened earlier). controller is a TanreiTooltipController (a ChangeNotifier with show(), hide() and isOpen). Snapshot harness: a golden of the anchor and the open tooltip in a 320 × 200 surface. Also owed: the thirteen placement values, offset, crossOffset, arrow, inverse and shortcut; TIP-9 and TIP-15 as they read now and TIP-22 to TIP-29. offset and crossOffset are double? and double in logical pixels (Flutter's own Tooltip calls the first verticalOffset; this one is not vertical only); arrow and inverse are bool?, null taking the system's choice; shortcut is List<String>. The container and the arrow are one ShapeBorder. Snapshot harness: the stage sets the standard keyboard convention for plain-shortcut. Not yet: the title's letter-spacing by the script of its text (TIP-11, PRS-20). Lagging: has no delay or timeout props and no hover gap; owes TIP-1 (the gap), TIP-3 and TIP-8's wording, TIP-17 and TIP-18. The target: showDelay, hideDelay and touchTimeout are numbers of milliseconds, as Pressable's delayLongPress is; undefined or null takes the token, and a negative or non-finite number takes the token and is reported in development builds. Wraps its anchor (children). The description is the anchor's accessibilityHint. The tooltip is drawn in the overlay layer that TanreiProvider renders at the root of the app, not in a Modal: a Modal is modal (it takes every touch and moves accessibility focus), which a non-modal overlay must not be. Hover comes from onHoverIn/onHoverOut on platforms with a pointer. Snapshot harness: renders the anchor and the open tooltip in a fixed 320 × 200 view. Also owed: the thirteen placement values, offset, crossOffset, arrow, inverse and shortcut; TIP-9 and TIP-15 as they read now and TIP-22 to TIP-29. offset and crossOffset are numbers of density-independent pixels; arrow and inverse are booleans, undefined or null taking the system's choice; shortcut is a string array. The container and the arrow are one drawn shape. Snapshot harness: the stage sets the standard keyboard convention for plain-shortcut. Not yet: the title's letter-spacing by the script of its text (TIP-11, PRS-20).

トークン

トークン 参照先 解決値(ライト)
--tanrei-comp-tooltip-arrow-height sys.space.space75 6px
--tanrei-comp-tooltip-arrow-inset sys.space.space100 8px
--tanrei-comp-tooltip-arrow-visible — 0
--tanrei-comp-tooltip-arrow-width sys.space.space150 12px
--tanrei-comp-tooltip-motion-enter-duration sys.motion.role.appear.hint.enter 250ms
--tanrei-comp-tooltip-motion-enter-easing sys.motion.easing.enter cubic-bezier(0.2, 0, 0, 1)
--tanrei-comp-tooltip-motion-exit-duration sys.motion.role.appear.hint.exit 200ms
--tanrei-comp-tooltip-motion-exit-easing sys.motion.easing.exit cubic-bezier(0.3, 0, 1, 1)
--tanrei-comp-tooltip-motion-hide-delay sys.motion.duration.short2 100ms
--tanrei-comp-tooltip-motion-show-delay sys.motion.duration.long2 500ms
--tanrei-comp-tooltip-motion-warm-duration sys.motion.duration.medium2 300ms
--tanrei-comp-tooltip-offset sys.space.space50 4px
--tanrei-comp-tooltip-plain-container-color sys.color.inverse-surface #1f1f1f
--tanrei-comp-tooltip-plain-container-elevation sys.elevation.level0
--tanrei-comp-tooltip-plain-container-gap sys.space.space100 8px
--tanrei-comp-tooltip-plain-container-inverse — 1
--tanrei-comp-tooltip-plain-container-max-width — 200px
--tanrei-comp-tooltip-plain-container-min-height — 24px
--tanrei-comp-tooltip-plain-container-padding-block sys.space.space50 4px
--tanrei-comp-tooltip-plain-container-padding-inline sys.space.space100 8px
--tanrei-comp-tooltip-plain-container-shape sys.shape.corner.small 6px
--tanrei-comp-tooltip-plain-shortcut-color sys.color.inverse-on-surface #f1f1f1
--tanrei-comp-tooltip-plain-supporting-text-color sys.color.inverse-on-surface #f1f1f1
--tanrei-comp-tooltip-plain-surface-container-color sys.color.surface-bright #ffffff
--tanrei-comp-tooltip-plain-surface-container-elevation sys.elevation.level2 0px 0px 0px 1px rgb(0 0 0 / 0.08), 0px 1px 2px 0px rgb(0 0 0 / 0.04)
--tanrei-comp-tooltip-plain-surface-shortcut-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-tooltip-plain-surface-supporting-text-color sys.color.on-surface #1b1b1b
--tanrei-comp-tooltip-rich-container-color sys.color.surface-bright #ffffff
--tanrei-comp-tooltip-rich-container-elevation sys.elevation.level2 0px 0px 0px 1px rgb(0 0 0 / 0.08), 0px 1px 2px 0px rgb(0 0 0 / 0.04)
--tanrei-comp-tooltip-rich-container-gap sys.space.space50 4px
--tanrei-comp-tooltip-rich-container-inverse — 0
--tanrei-comp-tooltip-rich-container-max-width — 320px
--tanrei-comp-tooltip-rich-container-padding-block sys.space.space150 12px
--tanrei-comp-tooltip-rich-container-padding-inline sys.space.space200 16px
--tanrei-comp-tooltip-rich-container-shape sys.shape.corner.medium 8px
--tanrei-comp-tooltip-rich-inverse-container-color sys.color.inverse-surface #1f1f1f
--tanrei-comp-tooltip-rich-inverse-container-elevation sys.elevation.level0
--tanrei-comp-tooltip-rich-inverse-supporting-text-color sys.color.inverse-on-surface #f1f1f1
--tanrei-comp-tooltip-rich-inverse-title-color sys.color.inverse-on-surface #f1f1f1
--tanrei-comp-tooltip-rich-supporting-text-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-tooltip-rich-title-color sys.color.on-surface #1b1b1b
--tanrei-comp-tooltip-timeout-touch — 1500ms
--tanrei-comp-tooltip-window-margin sys.space.space100 8px
--tanrei-comp-tooltip-plain-supporting-text-typescale sys.typescale.body-small 400 13px/1.5385
--tanrei-comp-tooltip-rich-supporting-text-typescale sys.typescale.body 400 14px/1.5714
--tanrei-comp-tooltip-rich-title-typescale sys.typescale.heading-6 500 14px/1.4286

フィクスチャ

基準となる描画パターンです。全プラットフォームが同じ ID でスナップショットを撮り、結果を比較できるようにしています。

生成元 specs/tooltip.spec.json, tokens/comp/tooltip.tokens.json, design/tokens/

ガイドライン

使う場面

グリフを見ただけでは分からない人に、アイコンのアクションの名前を伝えるときに使います。また、用語、上限、設定をその場で説明するときにも使います。ツールチップは、通りすがりに読む説明です。

タッチ操作の人は長押しするまでツールチップを見られず、スクリーンリーダーでは説明として読まれるだけです。作業を終えるのに必要な情報を、ツールチップの中だけに置いてはいけません。画面上のテキストとして見せます。リンクやボタンが必要な注記はツールチップではありません。ダイアログを使うか、説明する対象の横にテキストを置きます。

バリアントの選び方

バリアント 何か 用途
Plain 短いひとこと。既定では反転したサーフェスの上に描きます。headline は無視し、テキストの後ろにキーボードショートカットを表示できます。 アイコンのアクションの名前。「ゴミ箱に移動」「共有」など。
Rich 省略できるタイトルと、その下の数文。既定では浮き上がったサーフェスの上に描きます。shortcut は無視します。 用語や設定の説明。「ストレージ」と、何が使用量に含まれるかなど。

バリアントは構造の選択で、トークンの軸ではありません。デザインシステムは両方の見た目を変えられますが、値だけで 3 つ目の形を加えることはできません。

配置

placement は、ツールチップをアンカーのどの側に置きたいかと、その辺に沿ってどうそろえるかを指定します。既定は top です。

ボタンにポインターを載せるか、フォーカスしてください:13 の配置

toptop-starttop-endbottombottom-startbottom-endstartstart-topstart-bottomendend-topend-bottomauto
側 アンカーの中央にそろえる 1 つ目の端をそろえる 2 つ目の端をそろえる
上 top top-start top-end
下 bottom bottom-start bottom-end
アンカーの先頭側 start start-top start-bottom
アンカーの末尾側 end end-top end-bottom
  • そろえるとは、端と端を重ねること。 top-start はツールチップの先頭側の端をアンカーの先頭側の端に、end-bottom はツールチップの下端をアンカーの下端に合わせます。パネルの端に近い小さなアンカーに幅のあるツールチップを付けるときや、ツールチップをテキストの行にそろえたいときに使います。
  • start と end はレイアウト方向に従う。 右から左(RTL)のレイアウトでは、start は右側になり、top-start は右端どうしをそろえます。top と bottom は反転しません。
  • auto は top、bottom、end、start の順に試し、空きのある最初の側に、中央揃えで置きます。どの端にもドッキングできるパネルの中のコントロールのように、周りの様子が分からないアンカーに使います。
  • 空きがないとき。 そろえ方を保ったまま反対側に回り、次にウィンドウのセーフエリアに収まるよう辺に沿ってずれます。ツールチップより小さいウィンドウでは、必要ならアンカーに重なってでも、ウィンドウの中にとどまります。一度反対側に回ったら、その側に空きがある間はそこにとどまるので、ページをスクロールしてもツールチップが行ったり来たりしません。
  • offset と crossOffset。 offset はアンカーの端からの距離で、0 にすると接します。指定しなければシステムの距離になります。crossOffset はツールチップを辺に沿って動かします。アンカーの上下にあるときは末尾側へ、横にあるときは下のほうへ動き、アンカーの辺から外れることはありません。どちらもプラットフォームのレイアウト単位です。目に見える端とアンカーの領域が一致しない場合(見えないヒット領域が広いコントロールや、グラフの点など)にだけ指定します。
  • 表示中の変更。 placement やオフセットを変えると、ツールチップは動きを付けずにすぐ移ります。

矢印

ボタンにポインターを載せるか、フォーカスしてください:矢印付き

deletefilter_listinfo

arrow は、アンカーを指す小さな三角形をツールチップの端に描きます。既定のシステムでは描きません。指定しなければデザインシステムの選択に従い、true と false で 1 つのツールチップについて決められます。Web では、属性 arrow が true、arrow="false" が false です。

矢印はツールチップとひと続きの形で、同じ色です。ツールチップの中央ではなくアンカーを指し、ツールチップが反対側に回ったりずれたりした後も同じです。角にはかかりません。ツールチップは矢印の高さの分だけ離れて立つので、見える隙間は矢印があってもなくても同じです。

矢印は、どの側にあるかだけではどのコントロールのことか分からないときに使います。たとえば、込み合ったツールバーの上に利用者側から開くヒントです。ホバーで見せる名前には、ほとんど要りません。

2 つの見た目

ボタンにポインターを載せるか、フォーカスしてください:それぞれの形の、反転とサーフェスの上

Plain、反転Plain、サーフェスの上Rich、サーフェスの上Rich、反転

どちらの形も、反転して(反対のスキームの色で)描くことも、スキームの浮き上がったサーフェスの上に描くこともできます。

inverse Plain Rich
未設定(既定) デザインシステムがプレーンなツールチップに選んだとおり。既定のシステムでは反転です。 デザインシステムがリッチなツールチップに選んだとおり。既定のシステムではサーフェスの上です。
true 反転。 反転。
false サーフェスの上。 サーフェスの上。

Web では、スナックバーと同じく、属性 inverse が true、inverse="false" が false です。

変わるのは色とエレベーションだけです。形、大きさ、テキストスタイル、矢印、配置、動きはどちらの見た目でも同じで、どちらの見た目も、そのツールチップが重要だという意味にはなりません。そのままでは周りから際立たないツールチップにだけ指定します。暗い画像ビューアーや反転したバーの上の暗いラベルや、込み入った浮き上がったサーフェスの上の長めの注記などです。

キーボードショートカット

ボタンにポインターを載せるか、フォーカスしてください:名前の後ろにコマンドのキー

format_boldformat_italic

shortcut は、アンカーのコマンドのキーを、プレーンなツールチップのテキストの後ろに、その人が使っているキーボードに合わせて表示します。キーボードショートカットコンポーネントのキーの名前を、文字列 1 つにつき 1 つの組み合わせで受け取ります。Web の属性では、組み合わせを空白で区切って書きます。

ツールチップはキーを表示するだけで、キーを働かせはしません。キーの割り当ては利用者側で行います。長いテキストが横で折り返しても、キーは 1 行のまま切れません。リッチなツールチップは shortcut を無視し、デバッグビルドで報告します。

開く、フォーカス、閉じる

ツールチップは読むためのオーバーレイです。人はそこから情報を受け取るだけで、中で操作はしないので、フォーカスを受け取ることはありません。

入力 開くとき 閉じるとき
ポインター アンカーの上に表示の遅延時間だけとどまったとき。 アンカー、隙間、ツールチップのどこからも離れて、非表示の遅延時間がたったとき。読むために隙間を越えてツールチップの上へポインターを移しても閉じません。
キーボードフォーカス アンカーがキーボードフォーカスを受けたとき、すぐに。ポインターやタッチで受けたフォーカスでは開きません。 フォーカスがアンカーから離れたとき。
タッチの長押し すぐに。アンカーは実行されません。 指を離してからタッチ用の表示時間がたったとき。ほかの場所に触れたときはそれより早く閉じます。
  • 使っている間は時間切れにならない。 ポインターがアンカー、ツールチップ、その間の隙間のどこかにある間、またはアンカーがキーボードフォーカスを持っている間は開いたままです。隙間は描かれず、押しても何も起きません。ホバーとして数えるだけです。
  • Escape。 Escape でフォーカスを動かさずに閉じ、キーはそこで止まるので、外側のダイアログは閉じません。アンカーにもう一度ポインターを載せるかフォーカスするまで、閉じたままです。
  • 実行。 アンカーを実行する(クリック、タップ、Enter、Space)と閉じ、同じホバーのままでは再び開きません。
  • 一度に 1 つ。 ツールチップが開くと、同じウィンドウのほかのツールチップは閉じます。すでにツールチップを読んでいる人のために、新しいものは遅延なしで開きます。
  • ツールチップからツールチップへ。 ほかのツールチップと入れ替わるものや、ポインターかフォーカスがアンカーを離れて閉じた直後の短い時間のうちに開くものは、遅延も動きもなく、すぐに全体が現れます。そのため、ツールバーの上でポインターを滑らせると、名前を次々と読めます。Escape、アンカーの実行、ツールチップへの押下で閉じたときは、この時間は始まりません。
  • フォーカスを奪わず、何もふさがない。 フォーカスできるものも押せるものも含まず、何も操作不能にせず、スクリムも描きません。ツールチップを押すとツールチップが閉じ、押下はその下には届きません。
  • 一番上に描く。 ウィンドウのほかのどの層よりも上に描きます。開いているダイアログとそのスクリムより上なので、ダイアログの中のツールチップも見えます。
  • 配置。 placement が指定する場所に立ちます。配置を参照してください。アンカーが動けば付いていき、アンカーが取り除かれたり、隠れたり、スクロールでウィンドウの外に出たり、スクロールする領域の中で見えなくなったりすると閉じます。
  • 動き。 デザインシステムのほかのサーフェスと同じ現れ方で、アンカーの側から出てきます。既定では、アンカーに面した辺の中央から少し大きくなりながらフェードインし、同じ道を戻って消えます。途中で向きが変わるときは、いまの状態から折り返します。閉じている途中のツールチップはポインターを受け取らないので、下にあるものの邪魔になりません。動きを減らす設定では、フェードだけになります。
  • 読み上げ。 ツールチップが現れても何も読み上げません。describes が true の間は、そのテキスト(リッチならタイトルの後に続けたもの)が、開いているかどうかにかかわらずアンカーの説明になり、描かれたツールチップが重ねて読まれることはありません。

ツールチップごとの遅延

表示の遅延、非表示の遅延、タッチ用の表示時間は、デザインシステムがすべてのツールチップにまとめて決めます。1 つのツールチップだけ変えたいときは、showDelay、hideDelay、touchTimeout をミリ秒で指定します。指定しなければ、それぞれシステムの値になります。

  • 指定する場面。 名前を早く見せたい項目の多いツールバーでは表示の遅延を短くし、ポインターが点の上を次々と通るグラフでは、ツールチップがちらつかないように長くします。数文のリッチなツールチップではタッチ用の表示時間を長くし、1 語の名前では短くします。
  • 0 は「すぐに」。 表示の遅延が 0 なら、ポインターがアンカーに入った時点で開き、非表示の遅延が 0 なら、ポインターがアンカー、隙間、ツールチップから離れた時点で閉じます。それでも隙間はホバーとして数えるので、ツールチップの上へポインターを移せます。タッチ用の表示時間が 0 なら、長押しの間ずっと表示したうえで、指を離した時点で閉じます。
  • 指定した値のまま。 指定した値はモーションのダイヤルで伸縮せず、動きを減らす設定でも残ります。遅延は待ち時間で、アニメーションではないからです。負の値や数でない値はシステムの値に戻り、デバッグビルドで報告されます。Web では、空の属性も数ではありません。
  • ホバーとタッチだけ。 キーボードフォーカスでは今までどおりすぐに開きます。ほかのツールチップが表示されている間に開くツールチップも表示の遅延を飛ばし、open を設定したときもすぐに表示または非表示になります。長押しそのものの長さは、プラットフォームの決まりに従います。
  • 待っている間の変更。 すでに始まっている待ちは、始まったときの値のまま終わり、新しい値は次の待ちから使われます。

いまこれらのプロパティを受け取るのは Web と React だけです。ほかのプラットフォームが追従したときの名前は、仕様が各プラットフォームの流儀で決めています。Compose では showDelayMillis、hideDelayMillis、touchTimeoutMillis、Flutter では Tooltip と同じ waitDuration、exitDuration、showDuration、SwiftUI と React Native では仕様と同じ名前です。

どのプラットフォームも、モーダルではない表示方法を使います。Web ではトップレイヤーの popover="manual" 要素、SwiftUI ではウィンドウの上のオーバーレイ(コンパクトな幅でモーダルになる .popover は使いません)、Compose ではフォーカスを受け取れない Popup、Flutter では OverlayPortal、React Native では TanreiProvider のオーバーレイ層(すべてのタッチを受け取ってしまう Modal は使いません)です。

推奨

  • プレーンなツールチップは数語、リッチなツールチップは数文に収めます。
  • 同じ種類のアンカーは、ツールバーのアイコンをすべて下に出すように、画面の中で同じ配置にそろえます。
  • ほとんどのツールチップでは inverse と arrow をデザインシステムに任せ、必要な 1 つにだけ指定します。
  • コマンドのキーは、テキストに書き込まずに shortcut で表示します。
  • ひとまとまりのアンカーのツールチップには同じ遅延を指定し、同じ間合いで開くようにします。
  • アンカー自身のラベルを表示するツールチップでは、describes を false にします。

非推奨

  • ツールチップをアイコンボタンの名前として使わない。名前はボタンの label です。
  • 無効な理由を説明するために、無効なアンカーにツールチップを付けない。無効なコントロールはフォーカスできず、ポインターのイベントも届かないことがあります。理由は見えるテキストで書きます。
  • ツールチップにリンク、ボタンなど、操作できるものを入れない。
  • アンカーが見えるテキストですでに伝えていることを、ツールチップで繰り返さない。
  • ツールチップを重要そうに見せるために、反転の見た目や矢印を使わない。

アクセシビリティ

ロール
tooltip
ラベル付け
The tooltip names nothing. The anchor keeps its own accessible name and role; while describes is true the text (after the title, for a rich tooltip) is the anchor's description, available whether or not the tooltip is showing. An icon-only Button's label is its name (BTN-16) and is never replaced by a tooltip: a tooltip that shows the same words sets describes to false. A shortcut is the anchor's keyboard shortcut where the platform has that property, and the end of its description otherwise (TIP-27); the drawn keys and the arrow are hidden.
最小ターゲット
見た目のサイズが小さくても、ヒット領域は最小ターゲットサイズを満たします。 minimum 32px · pointer 44px · spacing 8px · touch 48px

キーボード操作

キー 動作
Tab Moving focus to the anchor opens its tooltip at once; moving focus away closes it. The tooltip itself is never a tab stop.
Escape Closes an open tooltip without moving focus, and goes no further (an enclosing dialog stays open).

備考

  • The tooltip takes no input, so it has no target of its own: minimumTarget applies to the anchor, which keeps its own hit area.
  • WCAG 1.4.13 (content on hover or focus): the tooltip can be dismissed without moving the pointer or focus (Escape, TIP-4), can be hovered (TIP-1: the gap between the anchor and the tooltip counts as hover — at any offset, with or without an arrow — so this holds at any hideDelay, 0 included), and stays until hover or focus ends or it is dismissed (TIP-5). Placement, the arrow and the motion change none of the three: a tooltip that flips, shifts or follows its anchor is the same open tooltip, and the gap is measured to wherever it stands.
  • A tooltip is invisible to touch users until they long-press, and to screen-reader users it is only a description. Nothing a person needs to finish a task may live only in a tooltip. A note that needs a link or a button is not a tooltip; that form belongs to a separate non-modal popover component.
  • Do not put a tooltip on a disabled anchor to explain why it is disabled: a disabled control cannot be focused, and on the web it may receive no pointer events. Put the reason in visible text.
  • Web: the description reaches the anchor's focusable element as aria-description, because aria-describedby cannot point across shadow roots. The anchor is the first focusable element slotted into the tooltip; a Tanrei component forwards aria-description from its host to the native element in its shadow root. The drawn tooltip has role tooltip and aria-hidden while describes carries the text.
  • The motion is short, small and never the only sign of anything; where motion is reduced nothing grows or travels (TIP-15). A tooltip that is leaving is already closed for input (TIP-29), so nothing invisible is ever in the pointer's way.
  • The inverse look and the surface look are two looks of one thing, not two meanings: neither says that a tooltip matters more. Choose by what the tooltip lands on.
  • A shortcut in a tooltip is a reminder for people who see it. It must also work for those who do not: the product binds the keys, and the anchor carries the shortcut for assistive technology (TIP-27).

生成元 specs/tooltip.spec.json, tokens/sys/measure.tokens.json

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

  • describes が true の間は、テキスト(リッチならタイトルの後に続けたもの)を、ツールチップが表示されているかどうかにかかわらず、アンカーのアクセシブルな説明にします。アンカーの名前とロールはそのままです。describes が false の間は、アンカーに何も加えません。
  • 描かれたツールチップが 2 回読まれないようにします。フォーカスを受け取ることはなく、フォーカスできるものも含みません。
  • アンカーがキーボードフォーカスを受けるとすぐに開き、フォーカスが離れると閉じます。ツールチップ自体が Tab で止まる位置になることはありません。
  • Escape でフォーカスを動かさずに閉じ、外側のダイアログまでは閉じません。
  • WCAG 1.4.13(ホバーまたはフォーカスで表示されるコンテンツ)を満たします。ポインターやフォーカスを動かさずに消せ、アンカーとツールチップの間の隙間をホバーとして数えるので非表示の遅延が 0 でもポインターを載せられ、ホバーかフォーカスが終わるか消されるまで表示し続けます。
  • 文字を大きくしても切れずに広がり、テキストはレイアウト方向の先頭側にそろえます。
  • ショートカットは、アンカーのキーボードショートカットとして伝えます(Web では、フォーカスを受け取る要素の aria-keyshortcuts)。描かれたキーは支援技術から隠します。アンカーが自分でショートカットを示しているときは、describes が true の間、ツールチップのキーを説明の最後に言葉で加えます。
  • 矢印は装飾で、支援技術から隠します。どちらの見た目も、ビルドがすべてのスキームでコントラストを確かめている、文字とコンテナの色の組を使います。
  • 動きは小さく、既定ではフェードとわずかな拡大だけです。動きを減らす設定では、大きさも位置も変えずにフェードします。ただし、すべてのアニメーションを取り除く Android の設定では、瞬時に表示と非表示を切り替えます。遅延、タッチ用の表示時間、次のツールチップがすぐに開く時間は、動きではなく待ち時間なので、どこでも残ります。
  • テキストが空のツールチップは開かず、アンカーに何も加えません。そのことと、プレーンなツールチップで無視されるタイトルは、デバッグビルドで報告します。

プラットフォームごとの既知の制約:

  • SwiftUI、Compose、Flutter、React Native は、まだ showDelay、hideDelay、touchTimeout を受け取らず、隙間もホバーとして数えません。そこではどのツールチップもシステムの遅延を使い、ポインターは非表示の遅延の間に隙間を越えます。配置も中央揃えの 4 つ(top、bottom、start、end)だけで、offset、crossOffset、arrow、inverse、shortcut はありません。ツールチップは大きさも位置も変えずにフェードします。
  • Web:タッチの長押しでツールチップが開くのは、ブラウザーが長押しをコンテキストメニューとして伝える場合だけです。iOS の Safari は伝えないので、そこではタッチでは開きません。
  • Web:リッチツールチップのタイトルは headline 属性です。<tanrei-tooltip> の title 属性はブラウザー自身のもので、ブラウザーはそれに標準のツールチップを描きます。付けないでください。
  • SwiftUI にはツールチップのロールがありません。説明はアンカーのアクセシビリティヒントになります。
  • iOS の SwiftUI:フォーカスのあるアンカーで Return や Space を押してもツールチップは閉じず、Escape で閉じるのはアンカーにフォーカスがあるときだけです。テキストフィールドのように、タップでフォーカスを受け取るアンカーでは、ツールチップが開きます。透明にされたり覆われたりしたアンカーは、隠れたものとして検出しません。
  • SwiftUI は、.tanreiTheme(_:) と Tanrei のダイアログが用意する層にツールチップを描きます。利用者側の .sheet や .popover のように SwiftUI が別に表示する内容では、.tanreiTooltipLayer() を付けないとツールチップが表示されません。
  • Compose には説明のプロパティがありません。テキストはアンカーの状態の説明(state description)になり、名前とロールの後に読まれます。Tanrei のコントロールは自分のノードにそれを付けますが、それ以外のアンカーは、マージする祖先が拾ったときだけ説明されます。トグルのアイコンボタンのようなトグルのアンカーでは、オンとオフの読み上げがテキストに置き換わります。
  • Compose:開いているダイアログの背後にあるアンカーのツールチップをコードから表示すると、ダイアログのウィンドウの下に描かれます。
  • Compose:フォーカスがウィンドウのほかの場所にあり、アンカーにはポインターを載せただけのとき、Escape がツールチップに届くのは Android 9(API 28)以降だけです。
  • Compose:実際のマウスによるホバー、外側へのタッチ、Escape を受け取る仕組み、TalkBack によるアンカーの状態の説明の読み上げは、Robolectric でしか確かめておらず、実機ではまだ確かめていません。
  • Flutter:描かれたツールチップは常にセマンティクスから除かれます。Flutter のツールチップのロールがまだ使えないためです。アンカーの説明には影響しません。
  • Flutter:Escape を止められるのは、フォーカスがアンカーの中にあるときと、Tanrei のダイアログの中だけです。Material のダイアログのルートのように、Escape を扱うほかの祖先には、フォーカスがほかの場所にあるとキーが届いてしまいます。
  • React Native:説明はアンカーの accessibilityHint になり、アンカー自身のヒントの後に続きます。
  • React Native:長押しで開いたツールチップは、ほかの場所に触れても早くは閉じません。タッチ用の表示時間がたてば閉じます。
  • React Native:スクロールするアンカーには付いていかず、アンカーがスクロールで見えなくなっても閉じません。セーフエリアの内側に収まるのは、TanreiProvider にウィンドウのインセットを渡したときだけです。
  • React Native:ホバー、ツールチップの上へのポインターの移動、長押し、Escape は、実機ではまだ確かめていません。

利用者側で行うこと

  • アンカーには、それ自身のアクセシブルネームを付けます。ツールチップがそれに取って代わることはありません。
  • テキストがその名前と同じなら、describes を false にします。
  • shortcut で表示するキーは、利用者側で割り当てます。メニューなど、そのコマンドが現れるほかの場所でも同じキーを表示します。
  • キーボードを使う人もツールチップを開けるように、アンカーはフォーカスできるものにします。
  • タッチ用の表示時間を短くするときは、残りの時間でテキストを読み切れるか確かめます。0 にすると、指を触れている間しか表示されません。
  • 作業に必要なことは、すべてツールチップの外に置きます。タッチやスクリーンリーダーを使う人は、ツールチップを見ないかもしれません。
  • React Native では、アプリのルートに TanreiProvider を置き、ウィンドウの insets を渡します。ツールチップはそのオーバーレイ層の、セーフエリアの内側に描かれます。SwiftUI では、アンカーより上位に .tanreiTheme(_:) を付け、利用者側のシートやポップオーバーの内容には .tanreiTooltipLayer() を付けます。Flutter では、ツールチップの上に Overlay が必要です。Navigator を持つ WidgetsApp なら必ずあります。