【テクニカル・上級編】 :dir() 方向疑似クラス – CSS実践ガイド

魂はディテールに宿る:RTL対応の決定版 `:dir()` 疑似クラスがもたらすCSSアーキテクチャの変革

多言語対応(i18n)、特にアラビア語やヘブライ語といった右から左へと文字を紡ぐ「RTL(Right-to-Left)」の世界を、皆さんはどれだけ真剣に設計したことがあるでしょうか。

「とりあえず `[dir=”rtl”]` という属性セレクタを書いて、`margin-left` と `margin-right` をひっくり返せばいい」

もしあなたがそんな風に考えているなら、今すぐその認識をアップデートする必要があります。深夜、大規模なシングルページアプリケーション(SPA)の動的レンダリングやチャットUIのスクロールパフォーマンスが謎のガタつきを見せ、スタイル再計算(Style Recalculation)のコストに悲鳴をあげる……その原因は、まさにその「泥臭い属性セレクタの乱用」にあるかもしれないからです。

今回は、モダンCSS仕様の中でも極めて強力でありながら、その真価とブラウザ内部の挙動があまり理解されていない `:dir()` 方向疑似クラス にスポットライトを当てます。属性セレクタとの決定的な違いから、ブラウザエンジンのレンダリング最適化、そして非同期DOM操作がもたらす競合の回避まで、現場の最前線で戦うアーキテクトの視点から徹底的に解剖します。

—

1. `[dir=”rtl”]` と `:dir(rtl)` の決定的な「血統の違い」

まず、私たちが長年使ってきた属性セレクタ `[dir=”rtl”]` と、疑似クラス `:dir(rtl)` は、似て非なるものです。この違いを理解することが、堅牢なCSSアーキテクチャへの第一歩となります。

セマンティクスと「継承」の罠

属性セレクタ `[dir=”rtl”]` は、HTML要素に明示的に `dir=”rtl”` という属性が書き込まれている場合のみマッチします。
しかし、HTMLの書字方向は「継承」されます。親要素(例えば `` や ``)に `dir=”rtl”` が指定されていれば、子要素は属性を持っていなくても視覚的にはRTLとしてレンダリングされます。

/ ❌ 属性セレクタ:この要素自体に `dir=”rtl”` が書かれていないとマッチしない /
.card[dir=”rtl”] {
padding-right: 2rem;
}

/ 疑似クラス:親の書字方向を継承した「実際の計算された方向」に正しくマッチする /
.card:dir(rtl) {
padding-right: 2rem;
}

「それなら `.card` の前に `[dir=”rtl”] .card` と書けばいいじゃないか」と思うかもしれません。
確かに機能はしますが、これには2つの重大な罠が潜んでいます。

1. CSSの結合子(Descendant Combinator)によるスタイル再計算コストの肥大化
2. `dir=”auto”`(自動判別)への完全な無力化

特に2番目の `dir=”auto”` は、モダンなWebアプリケーション、とりわけユーザーがコンテンツを投稿するUGC(User Generated Content)サイトにおいて致命的です。

`dir=”auto”` という魔術

ユーザー名やメッセージなど、動的に流れてくるテキストの言語がヘブライ語なのか英語なのか、事前にサーバーサイドで判定するのは困難です。HTML5では、要素に `dir=”auto”` を指定することで、ブラウザが最初の強力な方向性を持つ文字(LTR文字かRTL文字か)を検知し、自動的にその要素の書字方向を決定してくれます。

このとき、HTMLのマークアップ上の属性は `dir=”auto”` のまま変化しません。
つまり、属性セレクタ `[dir=”rtl”]` では、ブラウザが自動判定したRTL要素を絶対にキャッチできないのです。これに対し、`:dir(rtl)` 疑似クラスは、ブラウザが内部的に決定した「解決済みの方向(Resolved Direction)」をスマートに検知してマッチします。これだけでも、`:dir()` を採用する十分な理由になります。

—

2. ブラウザエンジンの内部挙動とスタイル再計算(Recalculate Style)の最適化

フロントエンド・アーキテクトとして最も関心があるのは、「パフォーマンス」でしょう。特に、数千個のノードが蠢く仮想スクロールや、コンポーネントが激しくミリ秒単位でマウント/アンマウントされるSPAにおいて、セレクタの評価コストは無視できません。

CSSルールマッチングの裏側

Blink(Chrome/Edge)やWebKit(Safari)などのブラウザエンジンは、CSSルールを右から左へと評価します(書字方向の話ではなく、セレクタの評価順序の話です)。

/ 評価:まず `.widget` をすべて探し、その祖先に `[dir=”rtl”]` があるかをDOMツリーを遡って探索する /
[dir=”rtl”] .widget { … }

この祖先探索(Ancestor Walking)は、DOMが深ければ深いほど、そして要素数が多ければ多いほど、ブラウザのメインスレッドを圧迫します。特に、ユーザーがテキストを入力して `dir=”auto”` の方向が動的に切り替わった瞬間、ブラウザは広範囲なサブツリーのスタイル再計算(Style Invalidation)を余儀なくされます。

一方、`:dir()` 疑似クラスはどうでしょうか。

/ 評価:`.widget` にマッチした要素自体が、現在LtrかRtlかの「状態」を持っているかを直接評価する /
.widget:dir(rtl) { … }

ブラウザは各要素のレイアウトオブジェクト(LayoutObject/RenderObject)を構築する段階で、すでにその要素の解決済み書字方向を内部プロパティとして保持しています。`:dir()` はその内部フラグを直接参照するため、広範なDOMツリーの遡及探索が発生しません。これは、要素の `:focus` や `:disabled` 状態をチェックするのと同等の、非常に局所的かつ高速な評価プロセスです。

リアルタイム入力での負荷検証

例えば、チャットアプリの入力欄(`

タイトルとURLをコピーしました