本文へスキップ
ウェブエンジニア問題集
第12章

MUIとアクセシビリティ — v9の構造変更とハイコントラスト対応

約9分
この章の目次開く

MUIを選ぶ理由として「アクセシビリティが担保されるから」と語られることがあります。半分は正しく、半分は誤解です。

MUIが引き受けるのは「部品の内部の作法」であって、「アプリ全体の設計」ではありません。この章では、その境界をはっきりさせます。

学習者学習者

MUIを使ってれば、アクセシビリティは考えなくていいと思ってた…。

考えるべきことは減りますが、ゼロにはなりません。まず「減る部分」から見ていきます。


MUIが最初から面倒を見ている範囲

領域MUIがやっていること
フォーカス管理ダイアログを開くとフォーカスを内部へ移し、閉じると元の要素へ戻す
キーボード操作メニュー・タブ・セレクトの矢印キー移動、Escでの閉じる
ロールと状態role や aria-expanded などの属性を自動で付与
ラベルの関連付けFormControlLabel や InputLabel が for と id を結ぶ
コントラスト比palette の色からコントラストを計算し、WCAGの3:1を下回ると警告を出す

自分で実装すると手間のかかる部分ばかりです。ここに手を出しすぎない(autoFocus や独自のキーハンドラを足しすぎない)ことが、むしろ正しい使い方になります。


v9で変わった構造 — Stepper

v9では、いくつかのコンポーネントのHTML構造が見直されました。代表がステッパーです。

v8以前v9
Stepper<div><ol>
Step<div><li>

実装でも、既定のタグが 'ol' と 'li' になっていることが確認できます。

これは見た目のための変更ではありません。順序付きリストにすることで、スクリーンリーダーが「5項目中の3番目」と読み上げられるようになります。意味のあるマークアップに寄せた変更です。


キーボード移動がroving tabindexに

Menu と MenuList、Tabs のキーボード操作方式が変わりました。roving tabindex と呼ばれる方式です。

考え方はこうです。

  • グループの中で「いまフォーカスできる項目」は常に1つだけ
  • 項目のあいだの移動は矢印キー
  • Tabキーを押すと、グループ全体を飛び越えて次の要素へ移る

タブが10個あるとき、Tabキーを10回押さないと次へ進めない、という状況を避けるための作法です。WAI-ARIAが推奨する一般的な方式に揃えた変更になります。

利用する側のコードを書き換える必要はありません。ただし、キーボード操作をテストで書いている場合は、期待する挙動が変わっている可能性があります。

誰でも使えるようにするイメージ

Windowsハイコントラストモード

WindowsにはOS側で色を強制的に置き換える「ハイコントラストモード」があります。ブラウザではCSSの forced-colors として扱われ、サイトが指定した色の多くが無視されます。

このとき問題になるのが、背景色で区別していた要素です。枠線のないボタンは背景が塗り潰され、どこがボタンなのか分からなくなります。

v9.1.0で、この対応を一括で入れる enhanceHighContrast が追加されました。

import { createTheme, enhanceHighContrast } from '@mui/material/styles';
 
export const theme = enhanceHighContrast(
  createTheme({
    palette: { primary: { main: '#0b5cad' } },
  }),
);
tsx

作成済みのテーマを包むだけです。内部では、CSSのシステムカラーキーワードを使ったスタイルがコンポーネントに追加されます。

トークン既定のキーワード用途
buttonBorderButtonBorder操作できる要素の枠線
buttonTextButtonTextボタンの文字とアイコン
disabledGrayText無効状態
selectedBackgroundSelectedItem選択中の項目の背景
canvasCanvasページの背景

これらはブラウザがOSの設定に応じて実際の色に置き換える特別な値です。個別に変えたい場合は第2引数で指定できます。

enhanceHighContrast(createTheme(), { disabled: 'ButtonText' });
tsx

アニメーションを控える設定

「動きを減らす」設定をOSで有効にしている利用者がいます。前庭障害などで、画面の動きが体調に影響する場合があるためです。

v9.1.0から、テーマでこの設定に追従できます。

createTheme({
  motion: { reducedMotion: 'system' },
});
tsx
値動き
'never'(既定)OSの設定を見ない。常にアニメーションする
'system'OSの設定に従う
'always'常にアニメーションを抑える

自分で書く必要があること

MUIが面倒を見てくれない領域が残ります。ここが冒頭の「半分は誤解」の部分です。

アイコンだけのボタンには名前を付ける

// これだけだと、読み上げは「ボタン」としか言わない
<IconButton><DeleteIcon /></IconButton>
 
// 何のボタンか伝える
<IconButton aria-label="削除"><DeleteIcon /></IconButton>
tsx

IconButton のラベル漏れは、実務でもっとも多いアクセシビリティ上の不備です。MUIは中身のアイコンが何を意味するか知らないため、ここは自動化できません。

見出しの意味と見た目を分ける

5章で触れたとおり、variant は見た目のプリセットにすぎません。

<Typography variant="h4" component="h2">セクション見出し</Typography>
tsx

見出しレベルが飛んでいると、スクリーンリーダーでの拾い読みがしづらくなります。

色だけで情報を伝えない

エラーを赤字だけで示すと、色の区別が難しい利用者に伝わりません。TextField の helperText にエラー内容を文字で書く、アイコンを添える、といった補助が必要です。

先生先生

「部品の中はMUI、部品の使い方は自分」と覚えてください。ボタンのフォーカスリングはMUIの仕事、そのボタンに何という名前を付けるかは書く人の仕事です。


よくあるハマりどころ

コンソールにコントラスト比の警告が出る

palette に設定した色が薄く、その上に乗る文字とのコントラストがWCAGの3:1を下回っています。色を濃くするか、contrastText を明示します。無視すると読みにくい画面になります。

Stepperのデザインが崩れた

v9で <ol> / <li> になったためです。プロジェクト共通のリストスタイルが当たっていないか確認します。

ハイコントラストモードでボタンが消える

enhanceHighContrast を適用していない状態で、背景色だけでボタンを表現しているケースです。

アニメーションを止める設定が効かない

motion.reducedMotion の既定は 'never' です。'system' を明示しているか確認してください。


ちゃんと使うためのポイント

  • MUIが担保するのは部品の内部。使い方の設計は書く人の責任
  • フォーカス管理とキーボード操作には手を出しすぎない
  • v9で Stepper は <ol> / <li> になり、Menu と Tabs はroving tabindexになった
  • Windowsハイコントラスト対応は enhanceHighContrast でテーマを包む
  • reducedMotion の既定は 'never'。対応するなら 'system' を明示する
  • IconButton には aria-label を必ず付ける

次の章では、MUIと他のCSSを共存させる方法を扱います。


参考リンク