この記事は、ニフティグループ Advent Calendar 2022 16日目の記事です。
初めに
最近、アニメを見る欲が復活した会員システムグループの2年目社員の関です。
数ヶ月前に以下のブログ記事を書きました。
主催した社内勉強会の課題でアクセシビリティ的に優れているTODOリストの課題を出した話
上記はアクセシビリティ的に良いTODOリストを勉強会の課題として出したという記事でした。
その勉強会の最終課題としてアクセシビリティに優れたツールバーの課題を出したので今回はその話をしようと思います。
勉強会については前回のブログ記事に記述してあるのでそちらを確認ください。
勉強会の最終課題の概要
早速、勉強会の最終課題について説明します。
課題の概要は以下になります。
- 課題
- アクセシブルなツールバーとリッチテキストボックスを作成する
- 要件
以下の要件を満たすツールバーとテキストボックスを作成してください- 選択した文字の色を変えるボタン(任意の色で良い)
- 選択した文字を太文字に変更するボタン
- 選択した文字をコピーするボタン
- 文字をペーストするボタン
- 選択した文字をカットするボタン
- イメージ
- その他
- React、Vue、Svelte、Solid.jsなどのライブラリは使っても良いが、React-modalなどのライブラリは使わずに自分で実装を行ってください
上記が課題の概要となります。
このようなツールバーはGitHubのIssueのテキストボックスやNotionなど多数のウェブアプリケーションで使用されています。
一方で、さまざまなユーザに使いやすいように作るにはさまざまな工夫が必要になります。
それではこの課題をどのようにアクセシブルに作成するかを見ていきます。
アクセシビリティの観点から気をつけるべき点
さて上記の課題に対して、どのようなことを気をつければ良いのかを以下で述べます。
Toolbarとして認識されるようにHTMLを作成する
ツールバーはHTML要素としては存在していません。そのため、WAI-ARIAなどの技術を使用してToolbarとして認識されるようにHTMLを作成する必要があります。
具体的には以下を満たすように作成すると良いです。
ToolbarのRole要素が入っている
以下のようにrole
要素を使用して記述を行うことで、ブラウザにツールバーであることを認知してもらい、それを使用者に伝えることができます。
1 2 3 |
<div class="toolbar" role="toolbar" aria-controls="textarea-sent"> ... </div> |
Toolbarにはaria-label
によってそのツールバーの意味が付与されている
そのツールが何をするものなのか、支援ツールを介して使用者に伝えることができます。
例えば、以下の例ではtoolGroupごとにラベルを指定しています。
1 2 3 |
<div class="toolGroup" :aria-label="スタイルの変更をする"> ... </div> |
Toolbarにaria-controls
で操作対象が指定されている
そのツールバーがどのコンテンツを操作するものなのか、ブラウザに伝えるとそれが支援技術(VoiceOverなど)に伝わり、多くのユーザにも使いやすいツールバーになります。
1 2 3 4 5 6 7 8 9 10 |
<div class="toolbar" role="toolbar" aria-controls="textarea-sent"> ... </div> <div ... id="textarea-sent" ... > </div> |
toolbar内のボタンはbutton
要素で正しく記述されている
button要素を使用することで、そのツール要素がボタン操作できることをブラウザに伝えることができます。こうすることで、さまざまなユーザがボタンの認識がしやすくなり、ツールバーを使いやすくなります。
1 2 3 |
<button class="tool tooltip" :area-pressed="isPressed" :area-disabled="isDisabled" @click="props.onClick(isDisabled)"> ... </button> |
以上のようにツールバーを作成することで、支援技術がツールバーをツールバーとして認識するようにHTMLを作成することができます。
操作対象などを指定することで、音声でWebページを操作するユーザやキーボード操作するユーザにもツールバーの操作がしやすいようにすることが可能になります。
キーボード操作
HTMLとWAI-ARIAを使用してWebページを構築するだけではなく、JavaScriptなどを使ってキーボード操作しやすいようにするのも重要です。
キーボード操作を行う一定数のユーザはTabで操作を行いますが、ツールバー要素全てをTabでフォーカス可能にすると辿り着きたい要素まで時間がかかってしまいます。
そのため、矢印でツールバーの要素を操作できるようにし、タブではツールバーの前後の要素に移動するようになどを実装するとキーボードで操作するユーザにも使いやすい作りになります。
実際に作成する操作は以下になります。
- HOMEを押すとToolbarの一番初めのToolに移動する
- Endを押すとToolbarの最後のToolに移動する
- 左矢印を押すと左のToolに移動する
- 右矢印を押すと右のToolに移動する
- ツールバーに戻ると前にフォーカスがあった場所にフォーカスされる
- 最初の場合は左で最後に、最後の場合は右で最初にフォーカスが移る
実際の実装は以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
const changeTool = (event) => { const elements = document.getElementsByClassName('tooltip') const index = [].findIndex.call(elements, e => e === event.target) const moveFocus = (nowIndex:number, afterIndex: number) => { opacity.value = 1 changeTabindex(nowIndex, - 1); elements[afterIndex].focus() changeTabindex(afterIndex, 0); } switch(event.key){ case "ArrowLeft": // 矢印キーが押されたら、フォーカスとtabindexを変更する。 if(!elements[index - 1]) { moveFocus(index, elements.length - 1) } else { moveFocus(index, index - 1) } break; case "ArrowRight": if(!elements[index + 1]) { moveFocus(index, 0) }else { moveFocus(index, index + 1) } break; case "Home": moveFocus(index, 0) break; case "End": moveFocus(index, elements.length - 1) break; case "Escape": console.log("event", opacity.value) opacity.value = 0 break; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// tabindexを変更する関数。 const changeTabindex = (index:number, tabindex:number) => { let indexTmp = index; for(let tool of toolBarList.value){ if(indexTmp < tool.tooltipsGroupList.length){ let tooltmp = tool.tooltipsGroupList[indexTmp] tooltmp.tabindex = tabindex tool.tooltipsGroupList.splice(indexTmp, 1, tooltmp); return } indexTmp -= tool.tooltipsGroupList.length; } } |
changeTool
でイベントを受け取り、イベントの内容に合わせてmoveFocus
関数を使用してフォーカスを移動します。
moveFocus
関数内ではchangeTabindex
という関数を読んでおり、changeTabindex
関数がDOM要素のtabindex
を変更することでフォーカスを受け取れる対象を操作しています。
tabindexとはtabindexは要素が入力フォーカスを持てることとキーボードナビゲーションに加わるかどうかを指定できるHTMLのグローバル属性です
https://developer.mozilla.org/ja/docs/Web/HTML/Global_attributes/tabindex
こうすることで、ユーザがツールバーの外に移動した後にツールバーに戻ってきたときに、前操作していたボタンにフォーカスが当たるようになります。
音声で伝える
音声を使用してウェブ操作をするユーザがツールバーを使用するために、アラートなどを使用して操作の内容を伝えることは重要です。
例えば以下の実装のようにすることでカット操作などをユーザに伝えることが可能です。
1 2 3 4 5 6 7 8 |
<!-- roleとしてdivをtextarea要素にする --> <div role="textarea" ... > </div> <!-- アラートするために以下のようなspan要素を透明で作成して、アラートされるようにする --> <span role="alert" aria-live="assertive" class="alert">{{alertText}}</span> |
1 2 |
// JavaScriptなどでは以下のようにalertTextを書き換えてアラートする alertText.value = `${selectText.value}をカットしました` |
様々な色覚特性に適応した配色にする
筆者である私は色弱持ちですが、色のコントラストがはっきりしていないと色が正しく認識できないことが本当に多いです。
私のような色覚に障害がある方々のために色のコントラストをはっきりさせることはとても重要です。
Googleの開発者ツールを使用すると以下のようにコントラストの確認が可能です。
操作の可不可を伝える
ユーザが操作に対して可能か不可能かを認知できるように、ツールバーにDisable属性を設けて押せないようにすると親切です。また、CSSでそのことが見分けがつくように色の変更を行います。
isDisabledなどを用意してHTMLのプロパティを書き換え、操作が可能かどうかを切り替えます。また、こうすることで音声で操作をしているユーザにも操作が不可能かどうかを伝えることができます。
以下はVueで行う例です。属性要素を状態として保持しておきます。
1 2 3 4 5 6 7 8 9 10 11 |
const tooltipsListOfFixStyle = ref([ { ... //以下のように属性を用意します areaPropaties: { isPressed: false, isDisabled: true } }, ... ]) |
次に以下のような関数を用意して操作します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
const disableFunc = async () => { const text = await navigator.clipboard.readText() if(selectText.value === '') { const templist = tooltipsListOfCCP.value.map((item)=>{ if(item.iconName === "paste" && text !== '') return item item.areaPropaties.isDisabled = true return item }) const tempFixList = tooltipsListOfFixStyle.value.map((item) => { item.areaPropaties.isDisabled = true return item }) tooltipsListOfCCP.value = templist tooltipsListOfFixStyle.value = tempFixList }else{ const templist = tooltipsListOfCCP.value.map((item)=>{ if(item.iconName === "paste" && text === '') return item item.areaPropaties.isDisabled = false return item }) const tempFixList = tooltipsListOfFixStyle.value.map((item) => { item.areaPropaties.isDisabled = false return item }) tooltipsListOfCCP.value = templist tooltipsListOfFixStyle.value = tempFixList } } |
上記の関数では
- クリップボードにテキストがない場合はペーストボタンをdisableにする
- 文字が選択されていないときはカットとコピーボタンをdisableにする
という二つの処理を行なっています。
tooltipの表示
ツールバーの要素はアイコンで作成されていますが、アイコンだけだとユーザの文化の違いや慣れの違いなどから意味がわからない可能性があります。そのため、操作名を表示するようにすることでツールバーのボタン操作が文字でわかるようにすると良いです。
ツールチップは以下のようにspan
要素などでHTMLで記述し、CSSでフォーカス時などに表示されるようにします。
1 2 3 4 5 |
<button class="tool tooltip" :tabindex="buttonTabIndex" :area-pressed="isPressed" :area-disabled="isDisabled" @click="props.onClick(isDisabled)"> <slot></slot> </button> <!--以下にツールチップのテキストを入れる--> <span class="tooltip-text">{{tooltipText}}</span> |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
.tooltip-text { ... } /* ホバー時にツールチップの非表示を解除 */ .tool:hover + .tooltip-text { opacity: 1; visibility: visible; } .tool:focus + .tooltip-text { opacity: v-bind(opacity); visibility: visible; } |
まとめ
アクセスフルなツールバーとリッチテキストボックスを作成するときに気をつけなければいけない点を紹介しました。
これ以外にも
- スマートフォンや低スペックなPCなどのユーザの機器の違い
- 言語の違い
- 文化の違い
などなど、使うユーザによって、もっと考慮する点はあるように思えます。
これからもアクセシビリティについてもっと学んで、全てのユーザが使いやすいシステムを構築できるようなエンジニアになれるように頑張っていきたいです。
明日は、@takenokoroidさんの記事です。
お楽しみに!!