これはなに
W3CのARIA Authoring Practices Guide (APG) | Tooltip Pattern を読み解きながら、アクセシビリティに対応したTooltipの実装を確認したいと思います。
参考にするといった、ARIA Authoring Practices Guide (APG) | Tooltip Pattern ですが、ページの出だしから 「Work in progress」 とあるように、まだ確定はしていません。
This design pattern is work in progress; it does not yet have task force consensus.
だからこそ読み解きながら、どうすべきか考えるきっかけになればと思い記事にまとめました。
長くなるので、前編・後編に分けたいと思います。
前編では、Tooltipの基本と挙動、それについての実装方法を紹介します。
後編では、aria
属性を使った読み上げについて紹介します。
Toolipとは
Tooltipとはキーボードフォーカスまたはマウスオーバーされた時に、関連する情報を表示するポップアップです。
Qiitaでは下記の画像のようなUIをしています。
Qiitaで使われいるTooltip |
---|
Tooltipを使わずに表現できるなら使わないほうが良い
調べれば調べるほど、アクセシビリティを重視するなら、Tooltipは使わないほうが良いと感じました。
Tooltipは限られたスペースで、情報を伝える手段として用いられるUIだと思いますが、様々なユーザーに同じ情報を伝えるためにはかなり超えなくては行けない課題が多いUIだと思います。
- キーボードユーザーは製作者の意図どおりTooltipの情報を閲覧できますか?
- タッチデバイスでTooltipの情報は閲覧できますか?
- 読み上げの支援技術を使っているユーザーはTooltipの情報を認知できますか?
- 拡大表示を必要とするユーザーは、Tooltipによって他の要素が隠されていませんか?
そもそも伝えたい情報は最初から隠さずに表示しておくべきではないでしょうか?
今回Tooltipを調べてみて、Tooltipを使わずに表現できるなら使わないほうが良いと私は思わされました。
どうしてもTooltipという選択肢をとる時は、補足として使うようにすべきだと思います。
感覚的ですが、万が一読み飛ばしてもユーザーの不利益にならないような作りを考えるとちょうどよい使い方になりそうです。
Tooltipで使用するWAI-ARIA
WAI-ARIA | 用途 |
---|---|
role="tooltip" |
Tooltipのポップアップとして表示される要素のroleとして使用します |
aria-describedby |
Tooltipがフォーカスを受け取る要素に、Tooltipの内容を参照情報として渡します |
aria-labelledby |
Tooltipがフォーカスを受け取る要素に、Tooltipの内容をメインの読み上げ要素として渡します |
このあたりは読み上げに関わるので、詳しくは後編で触れたいと思います。
Tooltipの挙動
ARIA Authoring Practices Guideでは、下記のように記載がありました。
(私の解釈でまとめています。)
- キーボードフォーカス、またはマウスオーバーでTooltipのポップアップを表示
- フォーカスアウト、マウスアウト、
Escape
キーによって、表示が消える - Tooltipはフォーカスを受け取らない
- フォーカス可能な要素を含む場合は、非モーダルダイアログで実装する
より使いやすいTooltipにするために
また、実装してみて下記のページの「Best practices summary」に共感できた箇所があるので、実装時意識するとより良いTooltipにすることができると思います。
- Tooltipのトリガーはインタラクティブな要素のみにする
- キーボードフォーカスを受取らない要素では、読み飛ばされやすいです
- マウスオーバーでTooltipを開くにしても、非インタラクティブ要素にわざわざマウスを持ってくる機会は少ないので気がつきにくいです
-
aria-describedby
またはaria-labelledby
を使用して、UIコントロールをツールチップに関連付ける- くわしくは後編でふれたいとおみおます
- Tooltipに重要な情報を入れない
- そもそも重要な情報は隠さずにすべてのユーザーが閲覧できる状態を作るべきだと思います
【閑話休題】title属性をTooltipとして扱うことは非推奨
すこし話はそれますが、 title
属性でもTooltipのような挙動をします。
title
属性を持つ要素にマウスオーバーを数秒していると、Tooltipの表示が出ます。
Qiitaも記事ページのサイドバーの「いいね」ボタンにtitle属性を持っているので、数秒マウスオーバーしていると下記のようなUIが表示されます。
Tooltipのためと言うよりは、読み上げの意図で実装しています。
Qiitaのいいねボタンのtitle属性で表示されたTooltip |
---|
これをTooltipとして利用できそうですが、HTML Living Standard の title
属性についての記述には非推奨だと記述がありました。
Relying on the title attribute is currently discouraged as many user agents do not expose the attribute in an accessible manner as required by this specification (e.g., requiring a pointing device such as a mouse to cause a tooltip to appear, which excludes keyboard-only users and touch-only users, such as anyone with a modern phone or tablet).
簡単に訳すと、「マウスなどのポインティングデバイスではツールチップを表示することはできるが、キーボードユーザーやタッチデバイスのユーザーでは表示することができないため、推奨されていません」とのことです。
アクセシブルなTooltipは、作る必要があります。
実装
こちらのコードをお借りして、読み解きたいと思います。
本記事内では抜粋なので、きちんと動くコードを確認する場合、下記のページの該当のソースをご確認ください。
紹介する下記のGitHubのコードは、 Develop example of tooltip design pattern · Issue #127 · w3c/aria-practices で取り上げられた課題に対してクリアできるように作られています。
大変学びが深いと思いますので、本家をぜひご覧いただければと思います。
Tooltipのデモページは下記から閲覧できます。
HTML
Tooltipの読み上げの対応を含めるといくつかサンプルコードがあるのですが、そのあたりの詳細は後編に紹介するとし、基本形として下記を参考にしたいと思います。
button
タグにTooltipが適応される例です。
Tooltipとトリガーになる要素が div
でラップされています。
aria-describedby
、 id
role="tooltip"
は読み上げに関わってきます。
<div class="tooltip-container">
<button type="button" aria-describedby="description">
Settings
</button>
<p id="description" role="tooltip" class="hidden">View and manage settings</p>
</div>
CSS
表示位置による装飾の記述も多いのですが、基本となるスタイルはこのあたりです。
button
タグに position: relative;
を適応して、周囲に表示できるようにしてあります。
Tooltipが非表示のときは display: none;
で読み上げもフォーカスも当たらないようになっていました。
:root {
--tooltip-thingy-height: .5em;
}
...略...
/* Tooltip styles */
[role="tooltip"] {
position: absolute;
top: calc(100% + calc(var(--tooltip-thingy-height) * 2));
left: 50%;
transform: translateX(-50%);
margin: 0;
padding: .5em 1em;
border-radius: .25em;
color: white;
background: black;
min-width: max-content;
max-width: 10em;
box-shadow: 0 1px 2px hsl(0, 0%, 0%);
}
/* Hides the tooltip */
.hidden {
display: none;
}
Javascript
最後にJavascirptです。
主に2Lから82Lまでが、ARIA Authoring Practices Guide (APG) | Tooltip Pattern のTooltipの挙動に関わる記述です。
マウス、タッチ、キーボードに対する指定と、EscapeキーでTooltipを閉じる記述があります。
constructor(element) {
this.container = element
this.trigger = element.querySelector('[data-tooltip-trigger]')
this.tooltip = element.querySelector('[role=tooltip]')
this.tooltipPosition = this.getTooltipPosition()
this.globalEscapeBound = this.globalEscape.bind(this)
this.globalPointerDownBound = this.globalPointerDown.bind(this)
this.initialiseClassList()
this.bindEvents()
}
// Basic actions
openTooltip() {
this.showTooltip()
this.checkBoundingBox()
this.attachGlobalListener()
}
closeTooltip() {
this.hideTooltip()
this.resetBoundingBox()
this.removeGlobalListener()
}
// Binding event listteners
bindEvents() {
// Events that trigger openTooltip()
// Open on mouse hover
this.container.addEventListener('mouseenter', this.openTooltip.bind(this))
// Open when a touch is detected
this.container.addEventListener('touchstart', this.openTooltip.bind(this))
// Open when the trigger gets focus
this.trigger.addEventListener('focus', this.openTooltip.bind(this))
// Events that trigger closeTooltip()
// Close when the mouse cursor leaves the trigger or tooltip area
this.container.addEventListener('mouseleave', this.closeTooltip.bind(this))
// Close when the trigger loses focus
this.trigger.addEventListener('blur', this.closeTooltip.bind(this))
}
attachGlobalListener() {
document.addEventListener('keydown', this.globalEscapeBound)
document.addEventListener('pointerdown', this.globalPointerDownBound)
}
removeGlobalListener() {
document.removeEventListener('keydown', this.globalEscapeBound)
document.removeEventListener('pointerdown', this.globalPointerDownBound)
}
globalEscape(event) {
if (event.key === 'Escape' || event.key === 'Esc') {
this.closeTooltip()
}
}
// Close the tooltip if the target is anything other than the components within the tooltip widget
globalPointerDown(event) {
switch (event.target) {
case this.container:
case this.trigger:
case this.tooltip:
event.preventDefault()
break
default:
this.closeTooltip()
this.trigger.blur()
}
}
// Show or hide the tooltip
showTooltip() {
this.container.classList.add('tooltip-visible')
this.tooltip.classList.remove('hidden')
}
hideTooltip() {
this.container.classList.remove('tooltip-visible')
this.tooltip.classList.add('hidden')
}
【前編】Tooltipの基本と挙動のまとめ
- W3CのARIA Authoring Practices Guide (APG) | Tooltip Pattern でもTooltipのアクセシビリティ対応については、まだ確定されていない
- Tooltipを使わずに表現できるなら使わないほうが良い
- Tooltipの挙動は
- キーボードフォーカス、またはマウスオーバーでTooltipのポップアップを表示
- フォーカスアウト、マウスアウト、
Escape
キーによって、表示が消える - Tooltipはフォーカスを受け取らない
- フォーカス可能な要素を含む場合は、非モーダルダイアログで実装する
後半
後半に続きます