
はじめに
こんにちは、スタメンでプロダクトエンジニアをしている おしん (@38Punkd) です。
5月9日、ウインクあいちで開催された フロントエンドカンファレンス名古屋 2026 にLT枠で登壇させていただきました。 その発表内容をブログ形式でご紹介できればと思います。
WebViewの文字サイズ、固定されていませんか?
この問題は、ネイティブとWebの境界に起因する「実装責務の曖昧さ」から生じがちです。
モバイルアプリ開発において、ユーザーのアクセシビリティへの配慮は不可欠です。特に、スマートフォンの設定で文字サイズを大きくしているユーザーにとって、アプリ内のテキストがその設定に追従するかどうかは、使いやすさに直結します。
私たちのアプリ TUNAG は、モバイルアプリではアプリネイティブとWebView(モバイルアプリでHTMLを表示できる機能)を併用しています。ネイティブUIはOSの文字サイズ設定に追従する一方で、アプリ内のWebViewだけが標準サイズのままでした。文字のリサイズが画面ごとに適用されたりされなかったりすると、ユーザーにとって読みにくいコンテンツになってしまうため、WebViewへの適用を本格的に開始しました。
本記事では、iOSとAndroidそれぞれのWebViewにおいて、OSの文字サイズ設定を適切に反映させるための具体的な実装方法と、その際の注意点について解説します。
文字サイズ設定は「特別対応」ではない
文字サイズ調整は、多くのユーザーが日常的に利用する標準機能です。ある調査によると、モバイルユーザーの約33%が文字サイズ調整を有効化しているというデータもあります *1。iOSのDynamic Type *2 やAndroid 14以降の200%フォントスケーリング *3 など、OSもこの機能を重視しています。
結論としては、ネイティブアプリ側のごくわずかな修正と、Web開発時のただ1点のポイントをおさえるだけで、WebViewでもiOS, Android共に文字サイズ設定が反映されるようになります。 対応工数に対してUX改善のインパクトが大きい領域であると言えそうです。
iOS WebView:JavaScript注入による動的なフォントサイズ反映
iOSのWebViewでDynamic Typeを反映させる最も柔軟で推奨される方法は、ネイティブ側でOSのフォントサイズを取得し、JavaScriptを介してWebViewに注入するアプローチです。
① Swift:Dynamic Type 変更を検知してフォントサイズを JS で注入
NotificationCenter.default.addObserver( self, selector: #selector(dynamicTypeDidChange), name: UIContentSizeCategory.didChangeNotification, object: nil ) @objc private func dynamicTypeDidChange() { applyFontSize() } func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { applyFontSize() // ページロード完了時にも適用 } private func applyFontSize() { let size = UIFont.preferredFont(forTextStyle: .body).pointSize webView?.evaluateJavaScript( "document.documentElement.style.fontSize = '\(size)px'", completionHandler: nil ) }
UIContentSizeCategory.didChangeNotification を受信したら、Swift側でフォントサイズを取得し、evaluateJavaScript でWebViewの :root 要素の font-size プロパティに直接書き込みます。この方式はリロードが不要で即時反映され、独自のスケールにも柔軟に対応できる点が利点です。
② CSS:font-size は rem 単位で指定
Swift側で :root の font-size を動的に書き換えるため、Webコンテンツ側では rem 単位でフォントサイズを指定することで、すべてのテキスト要素が連動してスケールするようになります。
:root { font-size: 17px; /* フォールバック。Swift が evaluateJavaScript で上書きする */ } .title { font-size: 1.143rem; } /* ✅ :root 基準でスケールする */ .description { font-size: 0.857rem; } /* ✅ :root 基準でスケールする */
参考:よりシンプルな代替案(-apple-system-body)
CSSに :root, body { font: -apple-system-body; } を指定し、Dynamic Typeの変更検知時に webView.reload() を呼ぶ方法もあります。この方法は手軽ですが、WebViewのリロードが発生する点と、Apple提供のスケールに固定される点がデメリットです。
Android WebView:「対応できている風」に注意
AndroidのWebViewでは、iOSとは異なる挙動と注意点があります。特に「文字サイズ設定に対応できているように見えるが、実は問題がある」ケースに注意が必要です。
| 見た目 | 実際に起きていること |
|---|---|
| 文字が大きく見える | Activity再生成によりWebViewが作り直される |
| 全体が拡大される | 文字だけでなく画像・余白もズームされる場合がある |
| 設定変更後に戻る | スクロール位置・入力中状態が失われる可能性がある |
android:configChanges に fontScale が含まれていない場合、OSのフォントスケール変更時にActivityが再生成され、WebView全体がズームされます。これは文字以外の要素も拡大し、レイアウト崩れや状態喪失の原因となります。文字だけを適切に反映できているかを見極める必要があります。
AndroidはOS値をJSで橋渡しする
Androidで文字サイズ設定をWebViewに適切に反映させるには、ネイティブ側でOSのフォントスケール値を取得し、JavaScriptを介してWebViewに伝えるアプローチが有効です。
① AndroidManifest:fontScale を configChanges に追加
<activity android:configChanges="fontScale|uiMode|density" ... />
AndroidManifest.xml の <activity> タグに android:configChanges="fontScale" を追加し、フォントスケール変更時のActivity再生成を防ぎます。
② Kotlin:フォントスケール変化を検知して WebSettings.setTextZoom(int) で反映
// 起動時の初期化 applyFontScale(resources.configuration.fontScale) // フォントスケール変化を検知(iOS の didChangeNotification に相当) override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) applyFontScale(newConfig.fontScale) } private fun applyFontScale(fontScale: Float) { // fontScale 1.0 → 100(標準), 2.0 → 200(200%) // px・em 問わず WebView 内のテキスト全体をスケールする webView.settings.textZoom = (fontScale * 100).roundToInt() }
onConfigurationChanged で newConfig.fontScale を取得し、WebSettings.setTextZoom(int) でWebViewのテキストズームレベルを設定します。
③ CSS:テキストは単位を問わずスケールされる
WebSettings.setTextZoom(int) はレンダリングエンジンレベルで適用されるため、HTML/JS の変更は不要です。px、em、rem のいずれの単位で指定されたテキストもスケールされます。ただし、非テキスト要素はスケールされないため、Web側で柔軟な設計が必要です。
共通の考慮事項:拡大しても壊れないレイアウトにする
OSの文字サイズ設定をWebViewに反映させるだけでなく、Webコンテンツ側で、文字が拡大されてもレイアウトが崩れないような柔軟な設計が求められます。
| 避けたい実装 | 推奨する実装 |
|---|---|
| 固定高さ | 内容量に応じて伸びる高さ |
| 1行前提 | 折り返し・複数行を許容 |
| アイコンと文字の密結合 | gap・flex-wrap・min-widthで逃がす |
文字サイズ対応の本質は、値の反映だけでなく「拡大を許容するUI」を構築することにあります。
まとめ
本記事では、iOSおよびAndroidのWebViewにおいて、OSの文字サイズ設定を適切に反映させるための具体的な実装方法と、その際の注意点について解説しました。
iOSでは、Swift側でDynamic Typeのフォントサイズを取得し、JavaScriptでWebViewの:root要素のfont-sizeを動的に書き換えるアプローチが最も柔軟です。これにより、リロードなしで即時反映が可能となります。Androidでは、AndroidManifestでfontScaleの変更を検知し、WebSettings.setTextZoom(int)でWebView全体のテキストズームレベルを設定します。
そして、iOSとAndroidの両方でWebView内のテキストをOSの文字サイズ設定に追従させるための共通の鍵となるのが、CSSでのrem単位の活用です。
| デフォルトの文字サイズ | 拡大した文字サイズ |
|---|---|
![]() |
![]() |
OSではJavaScriptによる:root操作と連動させるためにrem指定が必須となります。一方で、AndroidのsetTextZoomは単位を問わず追従してくれます。つまり、Web側の実装をiOSに合わせてremに統一しておけば、Android側でも一切の不都合なく自然に拡大縮小が行われ、両OSに矛盾なく対応できるのです。
スマートフォンのOS設定を尊重し、より多くのユーザーに読みやすいWebコンテンツを届けることは、ユーザーの満足度向上に繋がりやすく重要です。本記事が参考になりましたら幸いです。
サンプルリポジトリ
本記事で紹介した実装の詳細は、以下のサンプルリポジトリでご確認いただけます。
- iOSアプリ:GitHub - iOS WebView Dynamic Type Sample
- Androidアプリ:GitHub - Android WebView Font Scale Sample
*1:Ian Savchenko, “Designing for Accessibility: How Text Resizing Works in Different Web Browsers,” PayPal Technology Blog. https://medium.com/paypal-tech/designing-for-accessibility-how-text-resizing-works-in-different-web-browsers-bed9e424e071
*2:Apple Developer Documentation, “Scaling fonts automatically.” https://developer.apple.com/documentation/uikit/scaling-fonts-automatically
*3:Android Developers, “Features and APIs Overview — Non-linear font scaling to 200%.” https://developer.android.com/about/versions/14/features

