ツールチップ
stimeo--tooltip
ホバー/フォーカスで出る補足。表示・非表示の遅延、hoverable な橋渡し、Escape 消去を担う。
stimeo--tooltip コントローラは、WAI-ARIA の Tooltip パターンと WCAG 1.4.13 (hoverable / dismissible / persistent)を実装する。mouseenter/focusin で表示し mouseleave/focusout で非表示にし、それぞれ showDelay/hideDelay でちらつきを抑える。ツールチップ側にも show/hide を結ぶことで、トリガーからツールチップへポインタを移しても消えない。表示中は document レベルで Escape を監視するため、ホバー起動でフォーカスが別要素にあっても消せる。ツールチップ自身はフォーカスを受け取らず操作要素を持たない。 aria-describedby の関連付けはマークアップで宣言し、常に保持する。配置はこの Playground の CSS が持つ。
キーボード操作
| キー | 動作 |
|---|---|
| Esc | 表示中のツールチップを消す(document 全体で監視。フォーカス位置に依らない)。 |
<%# Markup for the tooltip demo.
Hover/focus on the trigger shows the supplementary description. The content is
role="tooltip", referenced from the trigger via aria-describedby. The show/hide delay
and the hoverable bridge between trigger and tooltip are the library's. Placement is
the consumer's CSS. %>
<span class="tooltip" data-controller="stimeo--tooltip"
data-stimeo--tooltip-show-delay-value="150"
data-stimeo--tooltip-hide-delay-value="200">
<button
class="demo-trigger"
type="button"
data-stimeo--tooltip-target="trigger"
aria-describedby="tooltip-content"
data-action="
mouseenter->stimeo--tooltip#show
mouseleave->stimeo--tooltip#hide
focusin->stimeo--tooltip#show
focusout->stimeo--tooltip#hide
keydown->stimeo--tooltip#onKeydown">
<%= t("components.tooltip.demo.trigger") %>
</button>
<span
class="tooltip__content"
id="tooltip-content"
role="tooltip"
data-stimeo--tooltip-target="content"
data-action="
mouseenter->stimeo--tooltip#show
mouseleave->stimeo--tooltip#hide"
hidden>
<%= t("components.tooltip.demo.body") %>
</span>
</span>
/*
* Presentation-only styles for the tooltip demo.
* Show/hide is the library toggling the content's hidden; data-state (open / closed)
* can also drive transitions like a fade. Placement is static — the consumer's CSS.
*/
.tooltip {
position: relative;
display: inline-block;
}
.tooltip__content {
position: absolute;
bottom: calc(100% + 0.5rem);
/* Anchor to the trigger's left for the no-JS fallback. The opt-in positioning
module (demo.js) centers it via placement: "top" and shifts it away from the
viewport edge. Do NOT use transform: translateX(-50%) here — it stacks on top
of the inline left the positioning module sets, double-shifting the tooltip off
the left edge (it gets clipped by the scrollable .content container). */
left: 0;
z-index: 10;
width: max-content;
max-width: 16rem;
padding: 0.375rem 0.625rem;
background: var(--color-text);
color: var(--surface-page);
border-radius: 0.375rem;
font-size: 0.8125rem;
line-height: 1.4;
box-shadow: 0 4px 12px rgb(15 23 42 / 0.2);
}
/* Optional fade driven by data-state (the library only toggles hidden). */
.tooltip__content[data-state="open"] {
opacity: 1;
}
// tooltip opt-in positioning demo (consumer-side JS).
//
// The core controller (stimeo--tooltip) only handles show/hide, delay, and dismiss.
// Viewport-edge flip/shift is optionally connected to the opt-in stimeo-ui/positioning
// module. We watch the content's hidden and, while shown, track the trigger's coordinates.
import { attachPositioning } from 'stimeo-ui/positioning';
document.querySelectorAll('[data-controller~="stimeo--tooltip"]').forEach((root) => {
const trigger = root.querySelector('[data-stimeo--tooltip-target="trigger"]');
const content = root.querySelector('[data-stimeo--tooltip-target="content"]');
if (!trigger || !content) return;
let detach = null;
const sync = () => {
if (!content.hidden && !detach) {
detach = attachPositioning(trigger, content, { placement: 'top', offset: 8, padding: 8 });
} else if (content.hidden && detach) {
detach();
detach = null;
content.style.position = content.style.left = content.style.top = '';
}
};
new MutationObserver(sync).observe(content, { attributes: true, attributeFilter: ['hidden'] });
sync();
});
これらのスタイルは共通のデザイントークン(ライト/ダーク両対応)を使います。 共通スタイルも一緒にコピーし、ルート要素の data-theme を切り替えればダークになります。
このコンポーネントを動かすために HTML へ記述する data-* 属性です。ルート要素に下の data-controller を付け、その内側に各 target / value / action を配置します。
ルート要素に付与
data-controller="stimeo--tooltip"
ターゲット
| 名前 | 説明 | 属性 |
|---|---|---|
trigger
必須
|
ツールチップが説明する要素(aria-describedby)。ホバー/フォーカスで表示/非表示する。 |
data-stimeo--tooltip-target="trigger" |
content
必須
|
hidden と data-state で表示制御する role="tooltip" のテキスト。フォーカスせず非対話的(ホバー可能な橋渡しで維持)。 |
data-stimeo--tooltip-target="content" |
値(Values)
| 名前 | 説明 | 属性 |
|---|---|---|
showDelay
|
ホバー/フォーカスで表示するまでの待機ミリ秒。ちらつき防止(既定 0)。 | data-stimeo--tooltip-show-delay-value |
hideDelay
|
離脱/ブラーで非表示にするまでの待機ミリ秒。ちらつき防止(既定 0)。 | data-stimeo--tooltip-hide-delay-value |
アクション
| 名前 | 説明 | アクション |
|---|---|---|
hide
|
hideDelay 後(0 なら即時)にツールチップを隠す。保留中の show を取り消す。 |
stimeo--tooltip#hide |
onKeydown
|
表示中、トリガーからの Escape でツールチップを消す。 | stimeo--tooltip#onKeydown |
show
|
showDelay 後(0 なら即時)にツールチップを表示する。保留中の hide を取り消す。 |
stimeo--tooltip#show |
状態フック
ライブラリが操作するのはこれらの ARIA / data 属性、カスタムプロパティだけです。見た目は利用側 CSS がこれらに反応して作ります([aria-selected] / [aria-expanded] / var(--stimeo--…) などのセレクタでフックします)。
| フック | 対象 | 意味 |
|---|---|---|
hidden |
コンテンツ | 非表示で付与、表示で属性なし。 |
data-state |
コンテンツ | "open" / "closed"。フェード遷移用の任意フック。 |