トピックを検索
見えているラベルで読み上げられないリッチテキストエディター
入力部分はcontenteditableのdivで、label要素では名前を付けられないため、名前がないか、エディターのライブラリーがどのエディターにも付ける決まった名前で読み上げられる
aria-label="Rich Text Editor. Editing area: main" 問題
スクリーンリーダーの利用者には、どの欄に書いているのかが伝わらず、音声操作の利用者は、画面に見えているラベルを話しても欄に入れない
あるショップのお問い合わせフォームに、「メッセージ」と見出しの付いた欄があります。上には太字・斜体・箇条書きのボタンが並び、書いた文章に書式を付けられます。画面では、フォームのほかの欄と同じようにラベルが付いて見えます。ところが、スクリーンリーダーでTabキーを押してこの欄に入っても、「メッセージ」とは読み上げられません。一つ目の例では、欄に名前がなく、入力欄であることも伝わりません。Chromeのアクセシビリティツリーでは、ただのgeneric要素です。二つ目の例では、エディターのライブラリーが欄に名前を付けています。名前は「Rich Text Editor. Editing area: main」で、ライブラリーが作るどのエディターにも同じ名前が付くため、エディターが二つあるフォームでは、同じ名前の欄が二つ並びます。音声操作の利用者が画面の言葉どおり「メッセージをクリック」と話しても、どちらの例でも何も起きません。
textareaにはプレーンテキストしか入らないため、リッチテキストエディターには使えません。太字や箇条書きを表示するために、エディターはcontenteditableでdivを編集できるようにします。このdivはフォームの入力欄ではありません。label要素が名前を付けられるのは、button、input、meter、output、progress、select、textareaなど、HTMLでラベル付け可能とされる要素だけです。そのため、divを指すfor="message"は何の名前にもならず、ラベルをクリックしても何も起きません。画面で見えるラベルと欄の結び付きがコードにないので、1.3.1を満たしません。またcontenteditableはdivを編集できるようにするだけで、ロールは与えません。スクリーンリーダーには入力欄であることも、何の欄かも伝わらず、ロールと名前の両方で4.1.2を満たしません(W3Cの失敗例F59とF68)。W3Cの失敗例F111は、見えるラベルがあるのに名前がないコントロールというこの場面全体を取り上げ、1.3.1、2.5.3、4.1.2のすべてを満たさないとしています。role="textbox"を足すだけでは、ロールは直っても名前は空のままです。
CKEditor 5やLexicalのように、ロールを自分で付けるエディターのライブラリーもあります。CKEditor 5は、名前が指定されていなければaria-labelも付けます。「Rich Text Editor. Editing area: main」の「main」は編集領域の名前で、ページ上のどこにもない言葉です。欄にロールと名前は付きますが、その名前は上に表示されたラベルではありません。2.5.3は、見えている言葉が名前に含まれ、その言葉を話せば操作できることを求めていますが、名前には「メッセージ」がどこにもありません。画面のラベルは欄と結び付いていないままなので、1.3.1も満たしません。
どの例も見た目は同じで、マウスを使う目の見える人には問題が見えません。ライブラリーが付ける名前の問題は、エディターが増えるほど大きくなります。商品の「説明」と「お手入れ方法」の二つのエディターがあるフォームでは、同じ名前の欄が二つになり、区別できるのは並び順だけです。
10グループ中2グループに影響
axeのaria-input-field-nameルールは、テキストボックスのロールを持つ要素を調べるため、role="textbox"があって名前のない欄は検出します。ロールのないdivはaxeにとって入力欄ではなく、どのルールも報告しません。ライブラリーの名前も名前には違いないため、二つ目の例も違反としては報告されません。この例のようにラベルのforが欄を指したままなら、ラベルの言葉が名前にないため、axeは確認が必要な項目に挙げるだけです。EaseWebの診断は、ロールのない編集可能なdivを4.1.2の問題として報告し、「Rich Text Editor…」や「Editor editing area…」という名前のエディターは、人による確認に回します。表示されたラベルを名前にすべきかは、人が見て判断することだからです。iframeの中のエディターには届きません。手作業でもAIアシスタントでも、手早い修正ではdivにidを付けてラベルのforと合わせることがあります。レビューでは正しく見えますが、何の名前にもなりません。
試してみる
Tabキーでメッセージ欄に入って名前を確かめ、音声操作で「メッセージをクリック」と話してみましょう
問題1: 欄の上のラベルはlabel要素ですが、forはdivに届かないため、欄には名前がありません。ロールもありません。
問題2: エディターのライブラリーが、画面のどこにもない言葉で欄に名前を付けています。
修正を適用します。 次に「ラベルの文言を変える」をオンにし、両方の修正をもう一度試してください。新しい言葉に合わせて名前が変わるのはどちらでしょう?
問題あり 修正できますか?
(Tabでメッセージ欄へ移動)
修正方法
欄をテキストボックスにし、横のラベルで名前を付ける。aria-labelledbyか、ライブラリーのラベル設定を使う
- 1編集できる要素に、role="textbox"とaria-multiline="true"を付ける
- 2名前に横のラベルの言葉を含める。aria-labelledbyでそのラベルを指すのが最もよい
- 3ライブラリーがどのエディターにも付ける名前を、そのまま残さない
ロールのない編集可能なdiv
<label for="message">メッセージ</label><div class="toolbar" role="toolbar" aria-label="書式"> …</div><div id="message" class="editor" contenteditable="true"> <p>届いた荷物のふたが割れていました。</p></div>ラベルを指すテキストボックス
<div id="message-label">メッセージ</div><div class="toolbar" role="toolbar" aria-label="書式"> …</div><div id="message" class="editor" contenteditable="true" role="textbox" aria-multiline="true" aria-labelledby="message-label"> <p>届いた荷物のふたが割れていました。</p></div>この方法を選ぶ理由:名前は、画面に表示された言葉を指して付ける
ラベルのforはdivのidと一致していますが、divはラベル付け可能な要素ではないため、ラベルは何の名前にもなりません。divにはロールもありません。
role="textbox"とaria-multiline="true"で欄の種類を伝え、aria-labelledbyでラベルの文字そのものを名前にします。スクリーンリーダーはロールと一緒に「メッセージ」と読み、音声操作は「メッセージをクリック」に反応します。label要素ではdivに名前を付けられないため、ラベルはただのテキストにしています。ライブラリーのaria-labelは削除しました。残してもaria-labelledbyが優先されますが、次にコードを読む人には、どちらの名前が使われるのか分かりません。
どちらも欄にロールを与え、ラベルの言葉を名前にします。aria-labelledbyでラベルを指してください。言葉の元が一つなので、ラベルが変われば名前も必ず一緒に変わります。書き写したaria-labelではそうなりません。書き写すのは、入力部分に設定できるのがライブラリーのラベル設定だけの場合に限ります。そのときも、ラベルの表示と同じ一つの変数から設定に渡して、二つがずれないようにします。
ライブラリーで設定し、ページで確かめます。 属性を付けるのは、編集の起点となる要素です。この要素はライブラリーが作るため、ライブラリーのオプションで設定します。渡した要素に付けた属性が入力部分に届くのは、CKEditor 5がaria-labelを引き継ぐように、ライブラリーがそれを残す場合だけです。バージョンを上げたら、そのたびにブラウザーで結果を確認してください。ライブラリーによっては独自のroleやaria-labelを付け、その既定値がバージョンで変わると、エディターの名前も一緒に変わります。
複数行。 aria-multiline="true"は、Enterキーでフォームを送信するのではなく改行することを伝えます。Chromeは編集可能なテキストボックスを自分で複数行として伝えますが、この属性を付ければ、ブラウザーの推測に任せずマークアップで示せます。
ラベルのクリック。 label要素はクリックすると欄にフォーカスを移しますが、エディターの名前になっているdivは移しません。デザイン上クリックで入れる必要があれば、エディターのfocus()を呼ぶ処理を足します。達成基準を満たすためには必須ではありません。
ヒント、必須、プレースホルダー。 「太字と箇条書きは保存されます」のようなヒントは独立した要素に書き、aria-describedbyで指します。divにはrequiredを付けられないため、見た目の表示と合わせて、テキストボックスにaria-required="true"を付けます。エディターがCSSで描く灰色のプレースホルダー(data-placeholder属性から描くことが多い)は名前ではなく、入力すると消えます。
書式が要らない場合。 配達員への短い伝言のように、太字も箇条書きもリンクも要らない文章なら、label要素を付けたtextareaで十分で、ここに書いた対応はどれも要りません。
iframeの中のエディター。 iframeの中に描かれるエディターには、名前を付けるものが二つあります。titleを持つフレームと、その中の編集可能なページです。アクセシビリティツリーで両方を確認してください。診断は後者には届きません。
修正の確認
4項目、マウス不要
確認方法: キーボード、スクリーンリーダー、音声入力
OS、ブラウザーと支援技術のバージョン、ビルド、実施日、各手順の実際の結果を記録してください。エディターのライブラリーを更新した際は、再度確認します。
制限
このサンプルは、どのエディターでも同じ結果になることを保証するものではありません。扱うのは入力部分のロールと名前で、エディターのほかの部分は扱いません。インデントのためにTabキーを取り込み、キーボードで抜け出せなくなるエディターは、フォーカスが抜け出せないエディターや埋め込み部品で扱っています。オンかどうかを伝えない太字ボタンは押された状態が伝わらないトグルボタンで、名前のないツールバーのアイコンは名前がないアイコンだけのボタンで扱っています。ツールバーを一つのTab位置にまとめ、矢印キーで移動できるようにすることは、「ツールバーを矢印キーで操作でき、Tabの移動は1回で済む」のトピックに属しますが、ガイドはまだありません。日本語の変換を確定するEnterキーでエディターが反応してしまう問題は、IMEの変換確定のEnterで送信される入力欄で扱っています。エディターに画像を入れるときに代替テキストを尋ねることは代替テキストを尋ねない画像のアップロードで、HTML標準の入力欄のラベルはラベルがない・表示と食い違うフォームの入力欄で扱っています。実際に使用する支援技術で、操作全体を確認してください。
よくある問題を再現した学習用のサンプルです。実在のクライアントの診断結果ではありません。コードは出発点となる実装例のため、実際の製品でも確認してください。ここでは支援技術でのテスト結果は報告していません。
参考資料
- WCAG Understanding: name-role-value(新しいタブで開きます)
- WCAG Understanding: info-and-relationships(新しいタブで開きます)
- WCAG Understanding: label-in-name(新しいタブで開きます)
- WCAG Technique F59(新しいタブで開きます)
- WCAG Technique F68(新しいタブで開きます)
- WCAG Technique F111(新しいタブで開きます)
- html.spec.whatwg.org: forms(新しいタブで開きます)
- ARIA APG: names-and-descriptions(新しいタブで開きます)
- developer.mozilla.org: textbox_role(新しいタブで開きます)
- ckeditor.com: module_core_editor_editorconfig-EditorConfig(新しいタブで開きます)
- prosemirror.net: ref(新しいタブで開きます)
- github.com: inlineeditableuiview.ts(新しいタブで開きます)
- github.com: LexicalContentEditableElement.tsx(新しいタブで開きます)
EaseWebのサービス
修正した問題を、次のリリースで再発させない
このガイドのような修正方法を、コーディングエージェントが従うルールと、CIのチェックに組み込みます。あわせて定期的に、キーボードとスクリーンリーダーで手動の再テストを行います。