◉RORK LABEN
●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SDK 58 — Expo SDK 58 が 10/15 に正式リリースされました(Beta は 09/15 から)。上げる前に依存の対応状況を確認しましょう●WIDGET — iOS のウィジェットが本番ビルドだけ真っ黒になる原因を追った報告が出ています●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SDK 58 — Expo SDK 58 が 10/15 に正式リリースされました(Beta は 09/15 から)。上げる前に依存の対応状況を確認しましょう●WIDGET — iOS のウィジェットが本番ビルドだけ真っ黒になる原因を追った報告が出ています●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…
記事一覧/開発ツール
⬡ 開発ツール/2026-05-20上級

Rork で VoiceOver と Dynamic Type を本番品質に整える実装メモ

Rork で出力した React Native アプリに VoiceOver・Dynamic Type・Reduce Motion を本番品質で組み込むための、ラベル設計・フォーカス管理・回帰テストまで含めた実装メモです。

アクセシビリティ5VoiceOverDynamic TypeRork585React Native239Reduce Motion

✦ プレミアム記事

公開して半年ほど経った壁紙アプリに、ある日「VoiceOver で読み上げると『ボタン、ボタン、ボタン』しか言ってくれません」というレビューが届いたことがありました。星は2つ。私は2014年から個人開発でアプリを出していて、当時 iOS / Android 合わせて累計5,000万ダウンロードを少し超えたあたり——にもかかわらず、アクセシビリティの仕上げを「いつかちゃんとやる」リストに入れたまま6年以上放置していたわけです。

そのレビューをきっかけに VoiceOver を実際に当てて自分のアプリを操作してみると、視覚的には完成して見えていた画面が、音声だけでは何ひとつ意味を成していないことが分かりました。アイコンボタンには accessibilityLabel がなく、カード型のリストはタップ可能領域とラベルがバラバラに読まれ、設定画面のスイッチは状態が読み上げられありません。VoiceOver は別世界のためのものではなく、自分が普段見落としている「画面が本来伝えるべきこと」を、率直に教えてくれる検査装置だったわけです。

その後、Rork で新規にアプリを立ち上げるたびに、最初の3日間を VoiceOver と Dynamic Type の対応に丸ごと使う ようになりました。設計が固まる前に組み込んだほうが圧倒的に早く、後付けで直すよりも実装の見通しが良くなるからです。以下に、Rork で出力した React Native + Expo プロジェクトに、VoiceOver・Dynamic Type・Reduce Motion を本番品質で組み込むための実装メモを残します。公式ドキュメントには断片的にしか書かれていないラベル設計・フォーカス管理・回帰テストの落とし穴を中心に、コードと実数値で示します。

アクセシビリティ対応が個人開発の収益にも効く三つの理由

「アクセシビリティ対応は理念としては正しいけれど、個人開発の限られた時間でそこまで手を回せない」——以前の私もそう思っていました。実際に着手してみると、収益の観点でも次の3つで効いてくることが分かったので、共有しておきます。

ひとつめは App Store / Google Play の審査リスクの低下 です。Apple は明示的に拒絶することは少ないものの、Google Play の Pre-launch Report ではアクセシビリティの問題が警告として出ます。私は2024年に1本、コントラスト比と読み上げ未対応の警告だけで「審査結果待ち」が48時間延びた経験があります。リリース日を決め打ちしている個人開発では、この遅延は精神的にもけっこう堪えます。

ふたつめは Dynamic Type 対応によるレビュー評価の改善 です。日本では中高年層が壁紙アプリのコアユーザーで、システム文字サイズを「最大」または「アクセシビリティ最大」に設定している方が一定数います。私のアプリの GA4 を見ると、device.user_agent に ContentSizeCategoryAccessibility を含むセッションが約 7.2 % を占めていました。この層に対してテキストが切れていると、レビューの低評価が直に増えます。Dynamic Type 対応を一通り入れたあとの3ヶ月で、平均レビュー点が 4.2 から 4.5 に上がった経験があります。

みっつめは VoiceOver 対応がそのまま「画面構造の見直し」になる ことです。読み上げ順序を整える過程で、視覚的にはなんとなく機能していた UI のロジック不備が浮き彫りになります。冗長な領域、押せそうで押せない装飾、意味不明なアイコン——これらを潰した結果、Dynamic Type 対応・国際化(多言語化)・将来の iPadOS 対応が同時に楽になります。アクセシビリティは外向きの理念であると同時に、内向きの 設計レビューの強制装置 でもあるわけです。

VoiceOver のラベルを「視覚カード = 音声カード」で揃える

VoiceOver で読み上げが破綻する一番大きな原因は、視覚的にひとつのカードに見える要素が、音声では複数の独立した要素として読まれてしまうことです。例えば下のような商品カードを Rork が生成したとします。

// Before: 音声で4回スワイプしないとカード全体が伝わらない
<Pressable onPress={() => navigate('Detail', { id })}>
  <Image source={{ uri: thumbnail }} style={styles.thumb} />
  <Text style={styles.title}>{title}</Text>
  <Text style={styles.price}>{`¥${price.toLocaleString()}`}</Text>
  <View style={styles.badge}>
    <Text style={styles.badgeText}>NEW</Text>
  </View>
</Pressable>

VoiceOver はこの中の Image・Text・Text・Text を別々の要素として認識します。スワイプ4回ぶんを使わないと「画像、タイトル、価格、NEW」と読み上げが終わりません。視覚では一目で把握できる情報なのに、音声では4ステップに分解されてしまうわけです。

これを ひとつの音声カード にまとめます。

// After: 1スワイプで意味のひと固まりとして読まれる
<Pressable
  onPress={() => navigate('Detail', { id })}
  accessible={true}
  accessibilityRole="button"
  accessibilityLabel={`${title}、価格 ${price.toLocaleString()} 円${isNew ? '、新着' : ''}`}
  accessibilityHint="ダブルタップで詳細を開きます"
>
  <Image
    source={{ uri: thumbnail }}
    style={styles.thumb}
    accessible={false} // 親が読み上げを担うので除外
  />
  <Text style={styles.title} accessible={false}>{title}</Text>
  <Text style={styles.price} accessible={false}>{`¥${price.toLocaleString()}`}</Text>
  {isNew && (
    <View style={styles.badge} accessible={false}>
      <Text style={styles.badgeText}>NEW</Text>
    </View>
  )}
</Pressable>

ポイントは3つです。親の Pressable に accessible={true} と accessibilityLabel を集約し、子の Image と Text には accessible={false} を明示します。accessibilityRole="button" を付けることで VoiceOver が「ボタン」と末尾に読み上げてくれるため、ユーザーは操作可能な要素だと認識できます。そして accessibilityHint で「ダブルタップで何が起きるか」を補足する——Hint は VoiceOver の設定で読み上げをオフにしているユーザーには読まれないので、Label の重複にはなりません。

数値表現にも一手間かけます。¥1,200 を VoiceOver にそのまま渡すと「えん、いち、てん、にひゃく」と読まれることがあります。Apple は通貨記号を見れば賢く読んでくれる場合もありますが、Android の TalkBack 含めて挙動が安定しないので、ラベル内では日本語に書き下す のが私の本番運用です。¥${price} ではなく ${price.toLocaleString()} 円 と書きます。

✦

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
✦VoiceOver の読み上げ単位を「視覚カード = 音声カード」で揃える accessibilityElements の組み立て方
✦Dynamic Type 100%〜310% の全レンジで折り返さない最小レイアウトの寸法計算とフォントスケール上限の妥協点
✦Reduce Motion / Reduce Transparency を検出して広告ビュー・パララックスを差し替える条件分岐
Stripe による安全な決済 · いつでもキャンセル可能
✦

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

⬡ 開発ツール2026-09-04
EAS の secret は「アプリに入れない」設定ではありません — 接頭辞と可視性を別々に決める
EXPO_PUBLIC_ の接頭辞は成果物に入るかを決め、EAS の可視性は誰が読めるかを決めます。二つを重ねたときに OTA 更新でだけ値が消える筋道と、出荷前に自分の手で確かめる手順をまとめました。
⬡ 開発ツール2026-08-22
一括置換は全ファイル成功しました。壊れたのは、消さなかった行のほうです
生成コードに一括置換をかけると、一致した行ではなく隣の行が壊れます。運用中のプロジェクトで実際に起きた破損と、置換の不変条件を検査する20行のガードスクリプトをまとめました。
⬡ 開発ツール2026-08-14
Expo SDK 57 に上げる前に、prebuild で消えるネイティブ変更を洗い出す
Expo SDK 57 では expo prebuild が既定で ios と android を消してから再生成します。手編集を上げる前に洗い出す方法、config plugin への移し替え、57.0.9 で解消したメモリ回帰までを実務の順序でまとめました。