ブラウザのネイティブスペルチェッカーと心中するな:`::grammar-error` 擬似要素の限界と、堅牢なフロントエンドアーキテクチャ
こんにちは。日々、CSSの仕様書の行間とブラウザのレンダリングパイプラインの泥臭いバグに頭を悩ませているフロントエンド・アーキテクチャフェチの皆さん。
今日も今日とて、デザインシステムや堅牢なエンタープライズWebアプリケーションの構築において、「ブラウザが勝手にやってくれる機能」と「開発者がコントロールすべき領域」の境界線で頭を抱えていることだろう。
今回は、CSS Selectors Level 4で導入され、徐々にブラウザのネイティブ実装が進んでいる `::grammar-error` 擬似要素について、その甘い響き(「ブラウザが文法ミスを勝手にハイライトしてくれる!」)に隠されたアーキテクチャ上の致命的な罠と、本番環境で生き残るための実践的な知見を共有しよう。
—
1. `::grammar-error` とは何か、そしてなぜ「使えない」のか
まず、仕様の定義を確認しておこう。`::grammar-error` は、ユーザーエージェント(ブラウザ)のネイティブなスペル・文法チェッカーによって「文法エラー」と判定されたテキスト範囲にマッチする擬似要素だ。相棒の `::spelling-error` と共に、テキストエリアや `contenteditable` な要素内の誤りを視覚化するために設計された。
典型的なCSSの書き方はこうだ:
/ ブラウザが文法エラーと判定した箇所をスタイリングする /
input::grammar-error,
textarea::grammar-error {
color: #ff3366;
text-decoration: wavy underline #ff3366;
}
一見すると、リッチなエディター機能を自前で実装する際に非常に魅力的だ。JavaScriptで複雑なAST(抽象構文木)を構築してトークナイザーを回さずとも、ブラウザのネイティブエンジンが勝手に赤波線を引いてくれるのだから。
しかし、ここにフロントエンド・アーキテクチャの地雷が埋まっている。
圧倒的なスタイリング制限(CSSプロパティのサンドボックス化)
セキュリティとブラウザ実装の複雑性を担保するため、`::grammar-error`(および `::spelling-error`)で使用できるCSSプロパティは極めて厳格に制限されている。
具体的に言えば、DOMのレイアウトサイズやフローに影響を与えるプロパティのほとんどが無視されるか、適用が無効化される。使えるのは主に以下の装飾系プロパティのサブセットに限定される。
- `color`
- `background-color`
- `text-decoration` 系(`text-decoration-color` など)
- `text-shadow`
- `outline` 系
「文字の大きさを変えたい」「背景をパディング付きでハイライトしたい」「エラー箇所にアイコンをインラインで挿入したい」といった、プロダクトのUI/UX要件で日常茶飯事に出てくる要望は、この制限の前にはすべて無力と化す。
—
2. レンダリング・パフォーマンスと非同期の競合
スペルチェックと文法チェックの処理系は、メインスレッドの動作に微妙な影を落とす。
ブラウザの文法チェッカーは、多くの場合、ユーザーのタイピングイベント(`input` や `keyup`)に対して非同期、あるいはアイドル時間にバックグラウンドスレッドやOSのネイティブAPIを叩いて実行される。つまり、スタイリングの適用タイミングが完全に非決定論的(Non-deterministic)なのだ。
メモリ効率と再描画のコスト
大規模なテキストエリア(数万文字に及ぶコードブロックや長文ドキュメント)において、ブラウザがテキスト全体の文法解析走り、該当範囲のツリーを更新し、`::grammar-error` のマッチングを再評価するコストは、バカにならない。
特に、DOMの変更とブラウザのスペルチェックエンジンによる内部ハイライトの同期ズレが生じた場合、以下のような不具合が発生する。
1. ユーザーが高速でタイピングする。
2. JS側でバリデーション走り、DOMや状態が更新される。
3. ブラウザの遅延した文法チェッカーが「古いテキスト位置」に対して `::grammar-error` を適用しようとする。
4. ハイライトの位置がズレる、あるいは点滅する(Flickering)。
エンタープライズレベルのアプリケーションにおいて、UIの描画がブラウザの気まぐれなネイティブ機能に依存し、制御不能になることはアーキテクチャの観点から絶対に避けなければならない。
—
3. 堅牢なフロントエンドを目指すための回避策と実践的アプローチ
では、私たちはこの `::grammar-error` をどう扱うべきか? 答えはシンプルだ。「プロダクトのコアなバリデーション機能としては一切信用せず、あくまでのネイティブのオマケ(Enhancement)として割り切る」こと。
確実性と一貫性が求められるWebアプリケーションにおいては、以下のように設計するのがプロフェッショナルの流儀だ。
アーキテクチャの切り分け:ネイティブ依存からの脱却
真面目なアプリケーションを作るなら、ブラウザのスペルチェッカーに頼るのではなく、Web Worker上で動作する構文解析器(Prism.js, CodeMirror, Monaco Editor、あるいは自前のASTパーサー)を走らせ、DOMを明示的にマークアップ(またはCanvas/WebGLベースで描画)するべきだ。
しかし、どうしても標準の `input` や `textarea` で簡易的なスタイリングを施したい場合の、安全なフォールバックを含むCSS設計のサンプルを提示しよう。
/ ==========================================================================
安全なグラマラス・スタイリングの設計パターン
========================================================================== /
/ ベースとなる入力フィールドのスタイル /
.enterprise-textarea {
font-family: var(–font-mono, monospace);
font-size: 14px;
line-height: 1.6;
color: var(–text-color, #24292e);
background-color: var(–bg-color, #ffffff);
border: 1px solid var(–border-color, #e1e4e8);
border-radius: 6px;
padding: 12px;
resize: vertical;
/ レンダリングの最適化:不要なレイアウトシフトを防ぐ /
contain: layout paint;
}
/
- [重要] ::grammar-error は制限が多いため、
- カラーと波線のみの最小限の装飾に絞り、UIの破壊を防ぐ。
/
.enterprise-textarea::grammar-error {
/ 仕様で許可されたプロパティのみを指定 /
color: inherit; / テキスト自体の色は変えない方が安全な場合が多い /
text-decoration: wavy underline var(–error-color, #d73a49);
text-decoration-thickness: 2px;
}
/ 同様にスペルミス側も整合性を持たせる /
.enterprise-textarea::spelling-error {
text-decoration: wavy underline var(–warning-color, #dbab09);
text-decoration-thickness: 2px;
}
/
- [フォールバック戦略]
- ブラウザの文法チェッカーが無効化されている環境や、
- 独自のJSバリデーション結果を適用するためのカスタムクラス
- (こちらを主軸に設計することを強く推奨する)
/
.enterprise-textarea.has-custom-error-range {
/ 自前で計算したエラー位置を制御するためのフックなど /
}
/
- ブラウザのネイティブ機能の挙動を監視・ログする例
- 実際の本番環境では、spellcheck=”false” に明示的に設定し、
- すべてのバリデーションをJavaScript側でコントロールする方が
- 予測可能性(Predictability)が圧倒的に高まる。
/
const textarea = document.querySelector(‘.enterprise-textarea’);
// エンタープライズ要件では、意図しない自動翻訳やスペルチェックの誤動作を防ぐため
// 以下のように明示的に制御することが多い
textarea.setAttribute(‘spellcheck’, ‘false’);
textarea.setAttribute(‘autocorrect’, ‘off’);
textarea.setAttribute(‘autocapitalize’, ‘off’);
—
結びにかえて
モダンなCSSの仕様は日々拡張され、`::grammar-error` のような一見便利そうな機能が次々と提案される。しかし、シニアエンジニアやアーキテクトに求められるのは、新機能を無条件に飛びつくことではない。
- その機能は、すべてのターゲットブラウザで一貫して動作するか?
- スタイリングの制限が、デザインシステムの要件を殺さないか?
- 非同期のライフサイクルが、アプリケーションの状態管理(State)と矛盾を起こさないか?
これらを冷静に見極め、あえてネイティブの機能を封じ、確実性の高いアーキテクチャを選択する勇気こそが、真に堅牢なWebアプリケーションを支えるのだ。
ブラウザの魔法に頼るな。コードの主導権は、常に君たちの手の中になければならない。

コメント