クロスプラットフォームのアクセシビリティを振り返る 2023

Accessibility

この記事は「アクセシビリティ Advent Calendar 2023」7日目の記事です。

はじめに

「モバイルアプリ」「iOS, Android」以外は Web 開発者向けに書いているので、フレームワークの説明が冗長なこともある。

モバイルアプリ

今年は Be My Eyes の GPT-4 導入が印象的だった。
Be My Eyes は、ボランティアのユーザーと対話して、画像や動画の内容を説明してもらうサービス。その説明を AI にお願いできるようになった。
実際に写真を撮って説明してもらったけど、なかなか精度が高い。
ユーザーは、AI の説明がわからなければ追加でチャットできるし、従来通りボランティアにも連絡できる。AI が説明してボランティアが補足するのは割とシナジーありそうだな。
Be My Eyes

最近は米国司法省から OK Mobile App の ADA 違反が発表された。
OK Mobile App は、オクラホマ州の公的なデジタル身分証アプリ。このアプリに必要な顔認証のフローが視覚情報に依存していて、司法省に通報があったらしい。
司法省の発表だけでなく、メディアも取り上げている。開発を取りやめて問題に対処しているらしい。
DOJ: Oklahoma mobile ID app violates Americans with Disabilities Act | KiowaCountyPress.net

iOS, Android

iOS では 17 になって Assistive Access(UI を簡素化する機能)などの新機能や API が導入された。 現状は英語圏のみだけど、パーソナルボイス(自分の声で合成音声を作る機能)が追加され、それを通話中に使えるようになった。
VoiceOver ではリグレッションも見られた。例えば Shadow-DOM 内の div に囲まれたボタンのテキストを読まなくなるバグがあり、WebKit ベースのフレームワークに影響が出たとか(ソース失念)。
UISupportsFullScreenInAssistiveAccess

Android 14 はフォントの拡大率が強化され、最大 130% から 200% まで設定できるようになった。また、拡大が非線形になったことで、既に大きい文字の拡大率は低く、小さい文字はより大きくなる。それらの変更に追従したフレームワークも見られた。
また、IME が アプリの言語を取得してキーボードの言語を設定できるようになったが、その関係で Gboard のレイアウトが変わってしまう現象も確認されている。
Android 14 の機能と変更点のリスト

Xamarin.Forms(.NET MAUI)

Xamarin.Forms は XAML, C# のクロスプラットフォーム向け UI ライブラリ。
2024 年に開発キットの提供が終わり、.NET MAUI が実質的な後継になる。
.NET MAUI はプロジェクト構造がプラットフォームごとから単一になったり、レンダラーのアーキテクチャが「ハンドラーアーキテクチャ」に変わっているらしい。
.NET MAUI、 Xamarn.Forms からの改良ポイント - 個人的なメモ

Xamarin.Forms は従来、アクセシビリティ向けのプロパティが AutomationProperties として組み込まれていた。.NET MAUI では、新たに実装された SemanticProperties の使用が推奨されている。
SemanticProperties は追加の機能も提供する。例えば XamarinCommunityToolkit から、フォーカスを強制移動する SetSemanticFocus が追加されている。
Build accessible apps with semantic properties - .NET MAUI | Microsoft Learn

Xamarin.Forms を採用するプロジェクトは悲鳴を上げてそう。

React Native

一昨年から React DOM for Native というプロポーザルが出ていて、それが今年リリースされたバージョンから少しずつ取り入れられている。 React DOM for Native とは、React Strict DOM API というものを構築し、Web(というか React DOM)と React Native の差異を減らしていく試み。
RFC: React DOM for Native (reduce API fragmentation)(GitHub)

これの何が良いのかと言えば、Web の知識でセマンティックに書けるようになること。
例えば、React Native では様々なコンポーネントが accessibilityLabel という props を持っているけど、この名前は iOS 由来のもので、React Native のメインターゲットであろう Web エンジニアにはやや馴染みが薄い。
でも aria-label という Props があれば Web の知識だけで書ける。画像だったら img 要素に、 alt 属性を指定すればいい。
現在は Props API の altsrc など、一部の属性がサポートされてきている。その様子は umbrella issue で確認できる。
☂️ Web Props (Part 1) umbrella issue(GitHub)

このプロポーザルを見た時は、微妙な気持ちだった。そもそもの課題が React Native for Web のパフォーマンスに寄ってて、React Native でわざわざそれを担う必要があるのかなと。
でも Copilot などの生成 AI が一気に普及している今では、ある意味追い風になっている気がしている。AI にちょいと頼めば Web でも iOS でも Android でも動く React コンポーネントができそう。

課題はクロスプラットフォーム特有の差異で、完全に吸収するのは難しい。Web の属性に対応するプロパティを OS が持つとは限らない。
例えば aria-labelledby に相当する accessibilityLabelledBy は、Android だと立ち位置が真逆の labelFor プロパティにマッピングする。iOS にはプロパティ自体が存在していない。実装の手立てはあっても、iOS の標準ではないのでやらない方がいい、といった具合。
feat: add accessibilityLabelledBy props(GitHub)

Capacitor

「React DOM でモバイルアプリを作ろう」という内容で React DOM for Native にジャブを放った。
Building Native Mobile apps with React DOM - Ionic Blog

Flutter

今年は新しいレンダリングランタイムの Impeller が iOS に正式対応した。
アクセシビリティ的には Material 3 への対応や、iOS での文字読み取り入力が挙げられる。
テキストの拡大率を指定する TextScaleFactor が Android 14 の非線形スケーリングに対応するため deprecated になるなど、ドラスティックなアップデートも施された。
Deprecate textScaleFactor in favor of TextScaler | Flutter

Flutter のアクセシビリティ話を知るには、前提として Semantics の知識がいる。
Flutter はネイティブの UI でなく、独自のエンジンで UI を描画する。そして、Semantics Widget から Semantics Treeを構築して、アクセシビリティ情報を支援技術に送信している。このあたりは mjhd 氏の解説が参考になるので、一回そちらを読んだ方がいいかも。
アクセシブルFlutter: Semantics入門

今年はなかなか辛そうなアクセシビリティ対応があった。 それは、iOS 16 以降の VoiceOver に発生した、致命的なバグの修正対応。
バグの内容は、Stack を使って UI を重ねた時、上の UI をタップしてもフォーカスしなくなる、というもの。
これは、UIViewAccessibility のプライベートメソッドである _accessibilityHitTest のロジックが変わったことに起因する。 hitTest はタップした場所に UI があるかを確認するもの。iOS 15 では、タップした場所に accessibilityElements(フォーカス可能な要素の配列) 内の UI があるかを見ていたのに、iOS 16 以降は 一番最初の UI しか見てくれない、というものらしい。
ワークアラウンドとして、_accessibilityHitTest の挙動をオーバーライドする、ハックな PR がマージされた。
Override _accessibilityHitTest to fix IOS 16 semantics(GitHub)

Flutter for Web では、つい最近 runApp ならぬ runPage の導入を提案する New DOM renderer for Flutter web(GitHub) という Issue が立った。
Flutter for Web は、少しクセがある。例えば Flutterkaigi 2023のページ を開いてみるとわかる。テキストのドラッグができない。スクリーンリーダーを起動すると Enable Accessibility という不可視のボタンにフォーカスされ、有効にするとテキストが読み取れるようになる。テキストを選択して読み上げたい人や、ブラウザ拡張を頼りにする人がサイトを利用するのは難しい。
テキストをドラッグ可能にするには SelectableTextSelectionArea を使うとか、ボタンを押させる前にアクセシビリティツリーに送信するには、runApp() の後で SemanticsBinding.instance.ensureSemantics を実行しておくとか、難しい tips が無限にある。なんでサンプルコードでは ensureSemantics をデフォルトで実行しないのかは、詳しい人教えてください。

Flutter for Web は Web Engine を通して、なんとも言えない HTML + Canvas か WebGL(CanvasKit) に描画する。その作りは SEO, SSR, E2E の観点から問題視されていて、各所で様々なバトルが交わされている様子。Flutter for Web は、Flutter を Web で動かすもので to HTML ではない、という意見もあって、確かに HTML が Flutter に追いついていないと言われればそうなのかもしれない。
アクセシビリティなところは、Semantics Tree があるから大丈夫なんだろう。ただ、なんとも気持ちの良いものではないので、New DOM renderer というのが実装されるならそれはそれで嬉しい。

終わりに

今年もお疲れ様でした