React Native アクセシビリティ徹底解説: Text#accessibilityRole の基本と活用法
Text#accessibilityRole
は、React Native の <Text>
コンポーネントに設定できるプロパティの一つです。このプロパティは、その Text
コンポーネントがアクセシビリティサービス(スクリーンリーダーなど)に対してどのような役割を持っているかを伝えます。これにより、視覚に障がいのあるユーザーがアプリケーションのコンテンツをより効果的に理解し、操作できるようになります。
簡単に言うと、スクリーンリーダーに「このテキストは何なのか」を教えるためのものです。
なぜ重要なのか?
アクセシビリティは、すべてのユーザーがアプリケーションを公平に利用できるようにするために非常に重要です。accessibilityRole
を適切に設定することで、スクリーンリーダーは以下のような情報をユーザーに提供できます。
- 要素のタイプを認識させる
たとえば、「これはボタンです」「これは見出しです」「これはリスト項目です」といった情報を伝えます。 - 操作方法のヒントを与える
ロールに基づいて、「この要素はタップ可能です」「この要素はスワイプで操作できます」といったヒントを自動的に生成することがあります。 - ナビゲーションを改善する
ユーザーはロールに基づいて特定のタイプの要素に素早くジャンプできるようになります(例:次の見出しに移動)。
設定できる主な値とそれぞれの意味
Text#accessibilityRole
に設定できる主な値は以下の通りです。これらはアクセシビリティサービスに、そのテキストがどのようなUI要素として機能しているかを伝えます。
'switch'
: スイッチ(オン/オフを切り替えるUI)のラベルとして機能するテキストに設定します。'spinbutton'
: スピンボタン(数値を増減させるUI)に関連するテキストに設定します。'radio'
: ラジオボタンのラベルとして機能するテキストに設定します。'checkbox'
: チェックボックスのラベルとして機能するテキストに設定します。'alert'
: アラートメッセージや重要な通知を示すテキストに設定します。'text'
: 単なる静的なテキストであることを明示的に示します。'none'
と似ていますが、より明確な意図を伝えることができます。'key'
: キーボードのキーのような役割を持つテキストに設定します。'image'
: このテキストが画像の説明として機能する場合に設定します。通常、<Image>
コンポーネントのaccessibilityRole
に設定されることが多いですが、特定の状況でテキストに設定することも可能です。'search'
: このテキストが検索フィールドまたは検索結果に関連するものであることを示します。'link'
: このテキストがリンクであることを示します。スクリーンリーダーは「リンク」と読み上げ、タップ可能であることを示唆します。'header'
: このテキストが見出しであることを示します。スクリーンリーダーは通常、「見出し」と読み上げ、見出しレベルを伝えることもあります。'button'
: このテキストがボタンとして機能することを示します。スクリーンリーダーは「ボタン」と読み上げ、タップ可能であることを示唆します。'none'
: (デフォルト) 特別な役割はありません。単なるテキストとして扱われます。
これらの値は、プラットフォーム(iOSとAndroid)のネイティブのアクセシビリティAPIにマッピングされます。
使用例
import React from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
const MyScreen = () => {
const handlePress = () => {
alert('ボタンが押されました!');
};
return (
<View style={styles.container}>
{/* 1. 単なるテキスト (デフォルトまたは 'text' を明示) */}
<Text style={styles.baseText}>
これは通常のテキストです。
</Text>
{/* 2. 見出しとして設定 */}
<Text style={styles.headerText} accessibilityRole="header">
重要な情報
</Text>
{/* 3. ボタンとして機能するテキスト */}
<TouchableOpacity onPress={handlePress}>
<Text style={styles.buttonText} accessibilityRole="button">
設定を保存
</Text>
</TouchableOpacity>
{/* 4. リンクとして機能するテキスト */}
<Text style={styles.linkText} accessibilityRole="link" onPress={() => console.log('リンクがタップされました')}>
プライバシーポリシー
</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
},
baseText: {
fontSize: 16,
marginBottom: 10,
},
headerText: {
fontSize: 24,
fontWeight: 'bold',
marginVertical: 20,
},
buttonText: {
fontSize: 18,
color: 'white',
backgroundColor: '#007bff',
paddingVertical: 10,
paddingHorizontal: 20,
borderRadius: 5,
marginTop: 20,
},
linkText: {
fontSize: 16,
color: '#007bff',
textDecorationLine: 'underline',
marginTop: 10,
},
});
export default MyScreen;
上記の例では、
"プライバシーポリシー"
というテキストにはaccessibilityRole="link"
を設定し、リンクとして認識させています。"設定を保存"
というテキスト(実際にはTouchableOpacity
の子)にはaccessibilityRole="button"
を設定し、ボタンとして認識させています。"重要な情報"
というテキストにはaccessibilityRole="header"
を設定し、スクリーンリーダーに見出しであることを伝えています。
注意点
- テスト
設定後は、実際のスクリーンリーダー(iOSのVoiceOverやAndroidのTalkBackなど)を使って、意図した通りに読み上げられ、操作できるかを確認することが非常に重要です。 - ネイティブコンポーネントとの連携
<Button>
や<Switch>
のようなネイティブコンポーネントは、既に適切なaccessibilityRole
が設定されていることが多いです。カスタムコンポーネントを作成する際に、Text#accessibilityRole
を活用することが特に重要になります。 - accessibilityRole と accessibilityLabel の違い
accessibilityRole
: 要素の役割(例: ボタン、見出し、リンク)を定義します。accessibilityLabel
: 要素の説明(例: 「このボタンは設定を保存します」)を提供します。- これらは異なる目的を持ち、組み合わせて使用することでより良いアクセシビリティを実現できます。
- 適切な要素に設定する
accessibilityRole
は、そのテキストが実際にその役割を果たしている場合にのみ設定すべきです。例えば、ただの装飾的なテキストにaccessibilityRole="button"
を設定すると、ユーザーに混乱を与えます。
React Native「Text#accessibilityRole」のよくあるエラーとトラブルシューティング
ロールがスクリーンリーダーによって正しく読み上げられない
問題
accessibilityRole
を設定したにもかかわらず、スクリーンリーダー(iOSのVoiceOver、AndroidのTalkBackなど)が期待通りの役割を読み上げない、または単なるテキストとして読み上げる。
考えられる原因とトラブルシューティング
- TouchableOpacity や Button の子要素
TouchableOpacity
やButton
のようなインタラクティブなコンポーネントの子要素としてText
を使用する場合、親コンポーネントがすでにアクセシビリティの役割を持っているため、Text
にaccessibilityRole="button"
を設定する必要はありません。むしろ、重複した情報として扱われる可能性があります。- トラブルシューティング
TouchableOpacity
の中にText
を配置し、それがボタンとして機能する場合は、TouchableOpacity
自体にaccessibilityLabel
を設定し、Text
には特別なaccessibilityRole
を設定しないのが一般的です。
- トラブルシューティング
- 不適切なロールの使用
テキストの実際の機能と設定したaccessibilityRole
が一致していない場合、スクリーンリーダーが混乱することがあります。- トラブルシューティング
テキストが本当にボタンとして機能するなら'button'
、見出しなら'header'
のように、実際のUIの役割に最も合致するロールを選びます。迷った場合は、デフォルトの'none'
または'text'
に戻し、accessibilityLabel
で詳細な情報を提供することを検討します。
- トラブルシューティング
- プラットフォーム間の差異
accessibilityRole
の一部の値は、iOSとAndroidで挙動が異なる場合があります。例えば、特定のロールが一方のプラットフォームではサポートされているが、もう一方ではそうでない、あるいは読み上げ方が微妙に違うことがあります。- トラブルシューティング
両方のプラットフォームで実際にテストし、挙動の違いを把握します。場合によっては、Platform.select
を使用してプラットフォームごとに異なるaccessibilityRole
を適用する必要があるかもしれません。
- トラブルシューティング
ネストされたリンクが機能しない
問題
<Text>
コンポーネント内で複数の <Text>
コンポーネントをネストしてリンクを作成しようとすると、スクリーンリーダーが個々のリンクとして認識しない、または操作できない。
// 悪い例:ネストされたリンク
<Text>
これは <Text accessibilityRole="link" onPress={...}>リンク1</Text> と{' '}
<Text accessibilityRole="link" onPress={...}>リンク2</Text> です。
</Text>
考えられる原因とトラブルシューティング
- トラブルシューティング
- isScreenReaderEnabled の利用
スクリーンリーダーが有効になっている場合にのみ、表示を調整する方法が推奨されます。例えば、スクリーンリーダーが有効な場合はリンクを分離した行に表示したり、リンク部分を別途TouchableOpacity
で囲んでaccessibilityLabel
を設定したりします。 - リンクの分離
可能な限り、複数のリンクを含む文章を分割し、それぞれのリンクを独立したアクセシビリティ要素として扱えるようにします。 - 代替手段の提供
スクリーンリーダーユーザーのために、ネストされたリンクの代わりに、アクセスしやすい代替手段(例: 一覧表示されたリンク)を提供することも検討します。
- isScreenReaderEnabled の利用
- React Native のテキストのフラット化
React Native は、<Text>
コンポーネントのネストされた構造を、内部的に単一の文字列としてフラット化する傾向があります。このため、ネストされた<Text>
に設定された個別のonPress
やaccessibilityRole
がアクセシビリティAPIに適切に公開されないことがあります。
ロールの値の指定ミスや非推奨の値
問題
accessibilityRole
の値が間違っている、または古いバージョンのReact Nativeでは機能したが、新しいバージョンで非推奨になった。
考えられる原因とトラブルシューティング
- React Native のバージョンによる変更
accessibilityRole
に設定できる値は、React Native のバージョンアップに伴って追加、変更、または非推奨になることがあります。- トラブルシューティング
使用しているReact Nativeのバージョンに対応する公式ドキュメント(reactnative.dev/docs/accessibility
)を参照し、最新の値を把握します。コンソールに警告が表示されることもよくあるので、開発中に警告メッセージを確認する習慣をつけましょう。
- トラブルシューティング
- タイポ
単純なスペルミス。- トラブルシューティング
公式ドキュメントを参照し、正しいロール名を再確認します。
- トラブルシューティング
特定のロールが特定の状況で期待通りに動作しない
問題
例えば、accessibilityRole="radio"
を設定したテキストがiOSのVoiceOverで「ラジオボタン」として認識されない、など。
考えられる原因とトラブルシューティング
- プラットフォームの制限
各プラットフォームのネイティブアクセシビリティAPIの制約により、React Nativeの特定のaccessibilityRole
が完全にサポートされていない場合があります。特に、複雑なUI要素のロールは、完全に再現するのが難しいことがあります。- トラブルシューティング
- 関連するGitHub Issueを確認
React NativeのGitHubリポジトリで、同様の問題が報告されていないか検索します。解決策や回避策が議論されている場合があります。 - カスタムネイティブモジュール
どうしても解決できない場合は、ネイティブコードでアクセシビリティの機能を実装し、React Nativeのブリッジを介してJavaScriptから呼び出すことを検討します。これは最後の手段ですが、特定の高度なアクセシビリティ要件を満たすために必要になることがあります。 - accessibilityState や accessibilityValue との組み合わせ
単にaccessibilityRole
を設定するだけでなく、accessibilityState
(例:{ checked: true }
)やaccessibilityValue
を組み合わせて、より詳細な情報をスクリーンリーダーに提供することで、認識が改善される場合があります。
- 関連するGitHub Issueを確認
- トラブルシューティング
accessibilityRole の設定漏れ
問題
UI要素がスクリーンリーダーにとって何の役割を持つのか不明瞭で、ユーザーが操作に迷う。
- 実際のデバイスでテストする
エミュレーターやシミュレーターだけでなく、実際のスマートフォンでスクリーンリーダーを有効にしてテストすることが不可欠です。挙動が異なる場合があります。 - 公式ドキュメントを常に参照する
React Nativeのアクセシビリティに関する公式ドキュメントは、最も信頼できる情報源です。定期的に更新されるため、最新の情報を確認しましょう。 - accessibilityLabel と accessibilityHint を活用する
accessibilityRole
が期待通りに機能しない場合や、より詳細な情報を提供したい場合は、accessibilityLabel
(要素の簡潔な説明) やaccessibilityHint
(要素の操作方法のヒント) を組み合わせて使用します。 - シンプルなUIから始める
複雑なカスタムコンポーネントを開発する前に、標準のコンポーネントでText#accessibilityRole
の挙動を理解し、徐々にカスタム化を進めます。
Text#accessibilityRole
は、スクリーンリーダーなどのアクセシビリティサービスに対して、<Text>
コンポーネントがアプリケーション内でどのような役割を果たしているかを伝えるためのプロパティです。これにより、視覚に障がいのあるユーザーがアプリのコンテンツをより理解し、操作できるようになります。
以下の例では、様々な accessibilityRole
の使い方を示します。
例1: 基本的なテキストと見出し
最も基本的な使用例です。単なる静的なテキストと、見出しとして機能するテキストを区別します。
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
const BasicTextRoles = () => {
return (
<View style={styles.container}>
{/* 1. 通常のテキスト (デフォルトでは 'none' または 'text' と解釈される) */}
<Text style={styles.paragraph}>
これはアプリの紹介文の一部です。
スクリーンリーダーはこれを単なるテキストとして読み上げます。
</Text>
{/* 2. 見出しとして設定 */}
<Text style={styles.header} accessibilityRole="header">
最新情報
</Text>
<Text style={styles.paragraph}>
新機能がリリースされました!詳細はこちら。
</Text>
{/* 3. さらに大きな見出し(レベルを示すスタイルと共に) */}
<Text style={styles.subHeader} accessibilityRole="header">
アカウント設定
</Text>
<Text style={styles.paragraph}>
パスワードの変更やプロフィールの更新ができます。
</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
backgroundColor: '#f5f5f5',
},
paragraph: {
fontSize: 16,
marginBottom: 15,
textAlign: 'center',
},
header: {
fontSize: 24,
fontWeight: 'bold',
marginVertical: 10,
color: '#333',
},
subHeader: {
fontSize: 20,
fontWeight: 'bold',
marginVertical: 10,
color: '#555',
},
});
export default BasicTextRoles;
解説
"最新情報"
と"アカウント設定"
のテキストにはaccessibilityRole="header"
を設定しています。これにより、スクリーンリーダーはこれらのテキストを「見出し」として認識し、「見出し」と読み上げるか、見出し間のナビゲーション(例:VoiceOverの「見出しローター」)を可能にします。"これはアプリの紹介文の一部です。"
のような通常のテキストにはaccessibilityRole
を明示的に設定していません。これはデフォルトで適切なテキストとして扱われます。
例2: ボタンとリンクとして機能するテキスト
テキスト自体がインタラクティブな要素として機能する場合に accessibilityRole
を設定します。
import React from 'react';
import { View, Text, TouchableOpacity, StyleSheet, Alert } from 'react-native';
const InteractiveTextRoles = () => {
const handleSavePress = () => {
Alert.alert('設定を保存', '設定が正常に保存されました。');
};
const handlePrivacyPolicyPress = () => {
Alert.alert('プライバシーポリシー', 'プライバシーポリシーが開きます...');
// 実際のアプリでは、ここにWebビューを開くなどのロジックを追加します
};
return (
<View style={styles.container}>
{/* 1. ボタンとして機能するテキスト */}
{/* 注意: TouchableOpacity が既に accessible なので、Text には必須ではないですが、
より明確なセマンティクスを提供するために設定できます。
一般的には、TouchableOpacity に accessibilityLabel を設定する方が推奨されます。 */}
<TouchableOpacity onPress={handleSavePress} style={styles.button}>
<Text style={styles.buttonText} accessibilityRole="button">
設定を保存
</Text>
</TouchableOpacity>
{/* 2. リンクとして機能するテキスト */}
{/* Text に onPress を直接設定すると、Androidではタップ可能と認識されない場合があります。
そのため、TouchableOpacity で囲むか、Linking API などで処理をフックします。
ここでは、accessibilityRole="link" を設定し、onPress イベントハンドラを追加します。 */}
<Text
style={styles.linkText}
accessibilityRole="link"
onPress={handlePrivacyPolicyPress} // Text の onPress はプラットフォーム依存
>
プライバシーポリシー
</Text>
{/* 3. より確実なリンクの実装(TouchableOpacity で囲む) */}
<TouchableOpacity onPress={handlePrivacyPolicyPress} style={{ marginTop: 20 }}>
<Text style={styles.linkText} accessibilityRole="link">
利用規約を読む
</Text>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
backgroundColor: '#f5f5f5',
},
button: {
backgroundColor: '#007bff',
paddingVertical: 12,
paddingHorizontal: 25,
borderRadius: 8,
marginBottom: 20,
},
buttonText: {
color: 'white',
fontSize: 18,
fontWeight: 'bold',
},
linkText: {
color: '#007bff',
fontSize: 16,
textDecorationLine: 'underline',
},
});
export default InteractiveTextRoles;
解説
"利用規約を読む"
の例は、TouchableOpacity
で囲むことで、より確実にリンクとして機能させる方法を示しています。この場合でもText
にaccessibilityRole="link"
を設定することで、そのテキストがリンクであることを明示できます。"プライバシーポリシー"
の例では、Text
にaccessibilityRole="link"
とonPress
を設定しています。これにより、スクリーンリーダーは「プライバシーポリシー、リンク」と読み上げます。Text
に直接onPress
を設定する方法は、iOSでは機能しますが、Androidではタップ可能として認識されない場合があります(Touchable*
コンポーネントで囲むのがより確実です)。"設定を保存"
の例では、TouchableOpacity
で囲まれたText
にaccessibilityRole="button"
を設定しています。これにより、スクリーンリーダーは「設定を保存、ボタン」と読み上げ、タップ可能であることを示唆します。ただし、TouchableOpacity
自体がアクセシビリティ対応の要素なので、Text
へのaccessibilityRole
設定は必須ではありません。TouchableOpacity
にaccessibilityLabel="設定を保存ボタン"
のように設定する方が一般的で確実です。
例3: チェックボックスやラジオボタンのラベルとして機能するテキスト
テキストがUIの状態を表す要素のラベルである場合に利用します。
import React, { useState } from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons'; // アイコンライブラリを使用
const CheckboxAndRadioRoles = () => {
const [isChecked, setIsChecked] = useState(false);
const [selectedOption, setSelectedOption] = useState('optionA');
return (
<View style={styles.container}>
{/* チェックボックスの例 */}
<TouchableOpacity
onPress={() => setIsChecked(!isChecked)}
style={styles.row}
// 親にaccessibleを設定し、子に役割を与える
accessibilityRole="checkbox"
accessibilityState={{ checked: isChecked }} // チェック状態を伝える
accessibilityLabel="通知を受け取る" // ラベルを提供
>
<Icon
name={isChecked ? 'check-box' : 'check-box-outline-blank'}
size={24}
color="#007bff"
/>
<Text style={styles.label}>通知を受け取る</Text>
</TouchableOpacity>
<Text style={styles.sectionHeader}>お支払い方法を選択</Text>
{/* ラジオボタンの例 - オプションA */}
<TouchableOpacity
onPress={() => setSelectedOption('optionA')}
style={styles.row}
accessibilityRole="radio"
accessibilityState={{ checked: selectedOption === 'optionA' }}
accessibilityLabel="クレジットカード"
>
<Icon
name={selectedOption === 'optionA' ? 'radio-button-checked' : 'radio-button-unchecked'}
size={24}
color="#007bff"
/>
<Text style={styles.label}>クレジットカード</Text>
</TouchableOpacity>
{/* ラジオボタンの例 - オプションB */}
<TouchableOpacity
onPress={() => setSelectedOption('optionB')}
style={styles.row}
accessibilityRole="radio"
accessibilityState={{ checked: selectedOption === 'optionB' }}
accessibilityLabel="銀行振込"
>
<Icon
name={selectedOption === 'optionB' ? 'radio-button-checked' : 'radio-button-unchecked'}
size={24}
color="#007bff"
/>
<Text style={styles.label}>銀行振込</Text>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'flex-start', // 左寄せ
padding: 20,
backgroundColor: '#f5f5f5',
},
row: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 15,
},
label: {
fontSize: 16,
marginLeft: 10,
},
sectionHeader: {
fontSize: 18,
fontWeight: 'bold',
marginTop: 20,
marginBottom: 10,
},
});
export default CheckboxAndRadioRoles;
解説
accessibilityLabel
で、要素が何であるかを明示的に定義しています。accessibilityState
を使用して、チェックボックスやラジオボタンの現在の状態(チェックされているか否か)をスクリーンリーダーに伝えています。TouchableOpacity
にaccessibilityRole
を設定し、Text
は単なる視覚的なラベルとしています。これは非常に重要で、Text
にaccessibilityRole="checkbox"
を設定するよりも、親のインタラクティブな要素(TouchableOpacity
など)に設定する方が正しいアプローチです。- この例では、カスタムのチェックボックスとラジオボタンを作成しています。
例4: アクセシビリティのベストプラクティスを考慮した例
Text#accessibilityRole
は単体で使用するだけでなく、accessibilityLabel
や accessibilityHint
と組み合わせて使うことで、より包括的なアクセシビリティを提供できます。
import React from 'react';
import { View, Text, TouchableOpacity, StyleSheet, Alert } from 'react-native';
const AccessibilityBestPractices = () => {
const handleProfilePress = () => {
Alert.alert('プロフィール', 'プロフィール画面に遷移します。');
};
return (
<View style={styles.container}>
{/* ヘッダーとして機能するテキスト */}
<Text
style={styles.mainHeader}
accessibilityRole="header"
accessibilityLabel="ようこそ、アプリへ" // スクリーンリーダーが読み上げるラベル
accessibilityHint="アプリのメイン画面です" // 追加のヒント
>
ようこそ!
</Text>
{/* ナビゲーションリンクとしてのテキスト(ボタンとして実装) */}
<TouchableOpacity
onPress={handleProfilePress}
style={styles.navButton}
accessibilityRole="button" // ボタンとしての役割
accessibilityLabel="プロフィール" // 「プロフィール」と読み上げ
accessibilityHint="あなたのユーザー情報を表示します" // ユーザーへの操作ヒント
>
<Text style={styles.navButtonText}>プロフィールを見る</Text>
</TouchableOpacity>
{/* ニュースのタイトル(見出しとして) */}
<Text style={styles.newsTitle} accessibilityRole="header" accessibilityLevel={2}>
新機能のお知らせ
</Text>
<Text style={styles.newsContent}>
最新アップデートで新しいダークモードが利用可能になりました!
</Text>
{/* 外部リンクとしてのテキスト */}
<Text
style={styles.externalLink}
accessibilityRole="link"
onPress={() => Alert.alert('外部サイト', '公式サイトに移動します。')}
accessibilityLabel="公式サイト"
accessibilityHint="詳細情報を確認するために外部ウェブサイトを開きます"
>
公式サイトはこちら
</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
backgroundColor: '#f5f5f5',
},
mainHeader: {
fontSize: 32,
fontWeight: 'bold',
marginBottom: 30,
color: '#222',
},
navButton: {
backgroundColor: '#6c757d',
paddingVertical: 12,
paddingHorizontal: 30,
borderRadius: 25,
marginBottom: 20,
},
navButtonText: {
color: 'white',
fontSize: 18,
fontWeight: 'bold',
},
newsTitle: {
fontSize: 22,
fontWeight: 'bold',
marginTop: 30,
marginBottom: 10,
color: '#444',
},
newsContent: {
fontSize: 16,
textAlign: 'center',
marginBottom: 20,
},
externalLink: {
color: '#28a745',
fontSize: 16,
textDecorationLine: 'underline',
marginTop: 10,
},
});
export default AccessibilityBestPractices;
- accessibilityLevel (iOSのみ)
"新機能のお知らせ"
の見出しにはaccessibilityLevel={2}
を設定しています。これはiOS (VoiceOver) でのみ有効で、見出しの階層レベル(H1, H2など)をスクリーンリーダーに伝えます。Android (TalkBack) ではこのプロパティは無視されます。
- accessibilityRole と accessibilityLabel の組み合わせ
"ようこそ!"
のテキストは、accessibilityRole="header"
に加えてaccessibilityLabel="ようこそ、アプリへ"
を持っています。これにより、スクリーンリーダーは「ようこそ、アプリへ、見出し」と読み上げます。accessibilityLabel
は、UI上の見た目のテキストと異なる、より分かりやすい情報を伝えるのに役立ちます。"プロフィールを見る"
ボタンも同様にaccessibilityRole="button"
とaccessibilityLabel="プロフィール"
を組み合わせています。
Text#accessibilityRole
はテキスト要素の役割を伝える上で非常に有用ですが、それだけがアクセシビリティ対応の全てではありません。React Native には、より包括的なアクセシビリティを実装するための様々なプロパティやアプローチが存在します。
accessibilityLabel の活用
accessibilityLabel
は、要素の視覚的なテキストや内容とは別に、スクリーンリーダーに読み上げさせるための短い説明を提供します。これは accessibilityRole
と密接に連携して機能し、多くの場合、accessibilityRole
よりも重要になります。
利点
- 短縮されたテキストや専門用語を、より分かりやすい言葉に置き換える。
- アイコンのみのボタンなど、視覚的な情報だけでは分かりにくい要素に説明を与える。
- 要素の目的を明確に伝える。
使用例
import React from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons'; // 例としてアイコンを使用
const AccessibilityLabelExample = () => {
return (
<View style={styles.container}>
{/* アイコンボタンにラベルを提供 */}
<TouchableOpacity
onPress={() => alert('設定を開く')}
style={styles.iconButton}
accessibilityLabel="設定を開くボタン" // スクリーンリーダーに読み上げられる内容
// accessibilityRole はここで "button" が推測されるため、明示不要な場合が多い
>
<Icon name="settings" size={30} color="#007bff" />
</TouchableOpacity>
{/* 視覚的なテキストとは異なるラベルを提供 */}
<Text
style={styles.shortText}
accessibilityLabel="あなたのプロフィール情報" // 「プロフ」ではなく「プロフィール情報」と読み上げられる
>
プロフ
</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
},
iconButton: {
padding: 15,
borderRadius: 50,
backgroundColor: '#e0e0e0',
marginBottom: 20,
},
shortText: {
fontSize: 20,
fontWeight: 'bold',
},
});
export default AccessibilityLabelExample;
解説
- 「プロフ」という省略されたテキストには、
accessibilityLabel="あなたのプロフィール情報"
を設定し、ユーザーに完全な情報を提供しています。 - 設定アイコンのボタンには、
accessibilityLabel="設定を開くボタン"
を設定しています。これにより、スクリーンリーダーはアイコンの見た目に関わらず、「設定を開くボタン」と読み上げます。
accessibilityHint の活用
accessibilityHint
は、要素の役割やラベルだけでは伝わらない、操作方法や次のアクションに関する追加情報を提供します。
利点
- 複雑なインタラクションのヒントを提供する。
- ユーザーが要素を操作する際に何が起こるかを予測させる。
使用例
import React from 'react';
import { View, Text, TouchableOpacity, StyleSheet, Alert } from 'react-native';
const AccessibilityHintExample = () => {
return (
<View style={styles.container}>
<TouchableOpacity
onPress={() => Alert.alert('アカウント', 'アカウント設定が開きます。')}
style={styles.button}
accessibilityLabel="アカウント"
accessibilityHint="アカウント情報を編集するためにダブルタップします" // 操作ヒント
>
<Text style={styles.buttonText}>アカウント</Text>
</TouchableOpacity>
<Text
style={styles.draggableItem}
accessibilityLabel="アイテムA"
accessibilityHint="ドラッグして並べ替えることができます" // ドラッグ操作のヒント
>
アイテムA
</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
},
button: {
backgroundColor: '#28a745',
paddingVertical: 12,
paddingHorizontal: 25,
borderRadius: 8,
marginBottom: 30,
},
buttonText: {
color: 'white',
fontSize: 18,
fontWeight: 'bold',
},
draggableItem: {
fontSize: 20,
padding: 15,
borderWidth: 1,
borderColor: '#ccc',
borderRadius: 5,
},
});
export default AccessibilityHintExample;
解説
- ドラッグ可能なアイテムには、
accessibilityHint="ドラッグして並べ替えることができます"
を設定し、特殊な操作方法を提示しています。 - 「アカウント」ボタンには、
accessibilityHint="アカウント情報を編集するためにダブルタップします"
を設定し、ユーザーがどのように操作すれば良いかを具体的に伝えています。
accessibilityState の活用
accessibilityState
は、要素の現在の状態(チェックされているか、選択されているか、無効になっているかなど)をスクリーンリーダーに伝えます。これは accessibilityRole
と組み合わせて、カスタムコンポーネントの状態を正確に反映させるために非常に重要です。
利点
- 要素が展開/折りたたみ可能であるかを伝える。
- 要素が有効/無効であるかを伝える。
- チェックボックス、ラジオボタン、スイッチなどの状態を伝える。
使用例
import React, { useState } from 'react';
import { View, Text, TouchableOpacity, StyleSheet, Switch } from 'react-native';
const AccessibilityStateExample = () => {
const [isEnabled, setIsEnabled] = useState(false);
const [isExpanded, setIsExpanded] = useState(false);
return (
<View style={styles.container}>
{/* スイッチ(ネイティブコンポーネントだが概念は同じ) */}
<View style={styles.row}>
<Text style={styles.label}>通知を有効にする</Text>
<Switch
onValueChange={setIsEnabled}
value={isEnabled}
accessibilityLabel="通知のオンオフを切り替えるスイッチ"
// Switch コンポーネントは内部で適切な role と state を持っている
/>
</View>
{/* カスタム展開/折りたたみ可能なセクション */}
<TouchableOpacity
onPress={() => setIsExpanded(!isExpanded)}
style={styles.collapsibleHeader}
accessibilityRole="button" // ボタンとして操作可能
accessibilityLabel="詳細情報"
accessibilityState={{ expanded: isExpanded }} // 展開状態を伝える
accessibilityHint={isExpanded ? "折りたたむにはダブルタップします" : "展開するにはダブルタップします"}
>
<Text style={styles.collapsibleText}>詳細情報</Text>
<Text style={styles.collapsibleIcon}>{isExpanded ? '▲' : '▼'}</Text>
</TouchableOpacity>
{isExpanded && (
<View style={styles.collapsibleContent}>
<Text>ここに詳細なコンテンツが表示されます。</Text>
</View>
)}
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
},
row: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 20,
},
label: {
fontSize: 18,
marginRight: 10,
},
collapsibleHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
width: '80%',
padding: 15,
backgroundColor: '#e0e0e0',
borderRadius: 8,
marginBottom: 10,
},
collapsibleText: {
fontSize: 18,
fontWeight: 'bold',
},
collapsibleIcon: {
fontSize: 18,
},
collapsibleContent: {
width: '80%',
padding: 15,
backgroundColor: '#f9f9f9',
borderWidth: 1,
borderColor: '#eee',
borderRadius: 8,
},
});
export default AccessibilityStateExample;
解説
accessible プロパティの制御
accessible
プロパティは、要素(通常は View
や TouchableOpacity
)とその子要素を、単一のアクセシビリティ要素としてグループ化するかどうかを制御します。
利点
- カスタムコンポーネント全体を単一のインタラクティブな要素として定義する。
注意点
accessible={true}
を設定した要素の子要素は、個別のaccessibilityRole
やaccessibilityLabel
が無視されることがあります。この場合、親要素に適切なaccessibilityLabel
を設定する必要があります。
使用例
import React from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
const AccessibleGroupExample = () => {
return (
<View style={styles.container}>
{/* 複数のテキストを一つのアクセシビリティ要素としてグループ化 */}
<TouchableOpacity
onPress={() => alert('ユーザープロファイルを見る')}
style={styles.profileCard}
accessible={true} // このView全体を一つのアクセシビリティ要素にする
accessibilityLabel="ユーザー名 ジョン・ドウ、メールアドレス [email protected]、プロフィールを見るボタン"
accessibilityRole="button" // 全体をボタンとして扱う
>
<Text style={styles.profileName}>ジョン・ドウ</Text>
<Text style={styles.profileEmail}>[email protected]</Text>
<Text style={styles.viewProfileText}>プロフィールを見る</Text>
</TouchableOpacity>
{/* グループ化しない場合(冗長になる可能性) */}
<View style={styles.profileCardAlt}>
<Text style={styles.profileName}>ジョン・ドウ</Text> {/* 1. ジョン・ドウと読み上げ */}
<Text style={styles.profileEmail}>[email protected]</Text> {/* 2. [email protected]と読み上げ */}
<TouchableOpacity onPress={() => alert('ユーザープロファイルを見る')}>
<Text style={styles.viewProfileText} accessibilityRole="button"> {/* 3. プロフィールを見る、ボタンと読み上げ */}
プロフィールを見る
</Text>
</TouchableOpacity>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
},
profileCard: {
borderWidth: 1,
borderColor: '#ccc',
borderRadius: 8,
padding: 15,
alignItems: 'center',
marginBottom: 30,
},
profileCardAlt: {
borderWidth: 1,
borderColor: '#ccc',
borderRadius: 8,
padding: 15,
alignItems: 'center',
marginBottom: 30,
backgroundColor: '#f0f0f0',
},
profileName: {
fontSize: 20,
fontWeight: 'bold',
},
profileEmail: {
fontSize: 16,
color: '#666',
marginBottom: 10,
},
viewProfileText: {
fontSize: 16,
color: '#007bff',
},
});
export default AccessibleGroupExample;
解説
- 2番目の
profileCardAlt
はaccessible={true}
を設定していないため、各テキストやボタンが個別に読み上げられ、ユーザーは多くのスワイプ操作が必要になる可能性があります。 - 最初の
profileCard
はaccessible={true}
を設定し、その中に含まれるすべてのテキストを「ユーザー名 ジョン・ドウ、メールアドレス [email protected]、プロフィールを見るボタン」という単一の塊として読み上げます。これにより、ユーザーはより効率的に情報を得られます。
importantForAccessibility の活用
importantForAccessibility
は、特定の要素をアクセシビリティツリーに含めるべきか、またはスキップすべきかを制御します。(Androidのみ)
利点
- 特定の要素を優先的にアクセシビリティツリーに含める。
- 画面上に表示されているが、アクセシビリティユーザーには不要な要素をスキップさせる。
使用例
import React from 'react';
import { View, Text, StyleSheet, Platform } from 'react-native';
const ImportantForAccessibilityExample = () => {
return (
<View style={styles.container}>
<Text style={styles.mainText}>重要な情報はこちらです。</Text>
{/* Androidでのみ機能 */}
{Platform.OS === 'android' && (
<Text
style={styles.decorativeText}
importantForAccessibility="no" // スクリーンリーダーに読み上げさせない
>
これは装飾的なテキストなので、スキップされます。
</Text>
)}
{Platform.OS === 'android' && (
<Text
style={styles.liveRegionText}
importantForAccessibility="yes" // 明示的に含める(デフォルト)
>
通常はデフォルトで含められます。
</Text>
)}
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
},
mainText: {
fontSize: 20,
marginBottom: 15,
},
decorativeText: {
fontSize: 14,
color: '#888',
fontStyle: 'italic',
},
liveRegionText: {
fontSize: 16,
marginTop: 10,
},
});
export default ImportantForAccessibilityExample;
解説
importantForAccessibility="no"
を設定したテキストは、Androidのスクリーンリーダーでは読み上げられません。これは、視覚的には必要だが、アクセシビリティ上は冗長な要素(例:区切り線、単純な装飾テキスト)に便利です。
Text#accessibilityRole
はテキストの役割を明確にするために重要ですが、React Native のアクセシビリティAPIはこれに留まりません。
importantForAccessibility
: 特定のプラットフォームで要素をアクセシビリティツリーに含める/含めないを制御する。accessibilityState
: 要素の現在の状態(チェック状態、展開状態など)を伝える。accessibilityHint
: 要素の操作方法や結果をヒントとして伝える。accessibilityLabel
: 何の要素であるかを具体的に説明する。最も汎用性が高く、重要。