論理的な連続フォーカス ナビゲーションに CSS 読み上げフローを利用する

公開日: 2025 年 5 月 1 日

CSS の reading-flow プロパティと reading-order プロパティは Chrome 137 以降で使用できます。この記事では、これらのプロパティの設計の背景と、使用を開始するための簡単な詳細について説明します。

グリッドや Flex などのレイアウト方法はフロントエンド開発を変革しましたが、その柔軟性が一部のユーザーにとって問題になる可能性があります。視覚的な順序が DOM ツリー内のソース順序と一致しない状況は簡単に発生します。このソース順序は、キーボードを使用してサイトを移動する場合にブラウザが使用する順序であるため、ページ内を移動する際に予期しないジャンプが発生することがあります。

reading-flow プロパティと reading-order プロパティは、この長年の課題を解決するために設計され、CSS ディスプレイ仕様に追加されました。

reading-flow

reading-flow CSS プロパティは、フレックス、グリッド、ブロック レイアウト内の要素がユーザー補助ツールに公開される順序と、リニア シーケンシャル ナビゲーション メソッドを使用して要素にフォーカスを合わせる方法を制御します。

キーワード値を 1 つ取ります。デフォルトは normal で、要素を DOM 順に並べ替える動作を維持します。フレックス コンテナ内で使用するには、値を flex-visual または flex-flow に設定します。グリッド コンテナ内で使用するには、値を grid-rowsgrid-columns、または grid-order に設定します。

reading-order

reading-order CSS プロパティを使用すると、読み上げフロー コンテナ内のアイテムの順序を手動でオーバーライドできます。このプロパティをグリッド、フレックス、ブロック コンテナ内で使用する場合は、コンテナの reading-flow 値を source-order に設定し、個々のアイテムの reading-order を整数値に設定します。

Flexbox の例

たとえば、3 つの要素が逆行順に並べられた Flex レイアウト コンテナがあり、order プロパティを使用してその順序を並べ替えたいとします。

<div class="box">
 <a href="#">One</a>
 <a href="#">Two</a>
 <a href="#">Three</a>
</div>
.box {
  display: flex;
  flex-direction: row-reverse;
}

.box :nth-child(1) {
  order: 2;
}

これらの要素を移動するには、TAB キーを使用して次のフォーカス可能な要素を見つけ、TAB+SHIFT キーを使用して前のフォーカス可能な要素を見つけます。ソースの順序(1、2、3)に従います。

エンドユーザーの視点から見ると、これは意味がなく、非常に混乱を招く可能性があります。ユーザー補助の空間ナビゲーション ツールを使用してページ内を移動した場合も、同じことが起こります。

この問題を解決するには、reading-flow プロパティを設定します。

.box {
  reading-flow: flex-visual;
}

フォーカス順序は 1、3、2 になりました。これは、英語で左から右に読む場合の視覚的な順序と同じです。

フォーカス順序を元の順序のまま逆順に維持する場合は、次のように設定します。

.box {
  reading-flow: flex-flow;
}

フォーカス順序は、フレックスの逆の順序(2、3、1)になりました。どちらの場合も、CSS order プロパティが考慮されます。

グリッド レイアウトの例

グリッドでこの機能がどのように機能するかを説明しましょう。12 個のフォーカス可能な領域を持つ CSS グリッド自動配置アイテムを使用してレイアウトを作成しているとします。

<div class="wrapper">
 <a href="#">One</a>
 <a href="#">Two</a>
 <a href="#">Three</a>
 <a href="#">Four</a>
 <a href="#">Five</a>
 <a href="#">Six</a>
 <a href="#">Seven</a>
 <a href="#">Eight</a>
 <a href="#">Nine</a>
 <a href="#">Ten</a>
 <a href="#">Eleven</a>
 <a href="#">Twelve</a>
</div>

5 番目の子が一番上の大きなスペースを占有し、2 番目の子がグリッドの中央に配置されるようにします。他のすべての子要素は、列テンプレートに従ってグリッド内に自動的に配置できます。

.wrapper {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: 100px;
}
.wrapper a:nth-child(2) {
  grid-column: 3;
  grid-row: 2 / 4;
}
.wrapper a:nth-child(5) {
  grid-column: 1 / 3;
  grid-row: 1 / 3;
}

Tab キーを使用して次のフォーカス可能な要素を見つけ、Tab+Shift キーを使用して前のフォーカス可能な要素を見つけてみてください。ソースの順序(1 ~ 12)で並べ替えられます。

この問題を解決するには、reading-flow プロパティを設定します。

.wrapper {
  reading-flow: grid-rows;
}

フォーカスの順序は、5、1、3、2、4、6、7、8、9、10、11、12 になりました。行ごとに視覚的な順序に従います。

読み上げフローを列の順序に従って行う場合は、代わりに grid-columns キーワード値を使用できます。フォーカス順序は 5、6、9、7、10、1、2、11、3、4、8、12 になります。

.wrapper {
  reading-flow: grid-columns;
}

grid-order を使用する方法もあります。フォーカス順序は 1 ~ 12 のままです。これは、どのアイテムにも CSS 順序が設定されていないためです。

reading-order を使用するブロック コンテナ

reading-order プロパティを使用すると、reading-flow プロパティで設定された順序を上書きして、読み上げフローでアイテムを訪問するタイミングを指定できます。有効な読み取りフロー コンテナで、reading-flow プロパティが normal でない場合のみ有効になります。

.wrapper {
  display: block;
  reading-flow: source-order;
}

.top {
  reading-order: -1;
  inset-inline-start: 50px;
  inset-block-start: 50px;
}

次のブロック コンテナには 5 つのアイテムが含まれています。要素をソース順序から並べ替えるレイアウト ルールはありませんが、最初に訪問する必要があるフロー外のアイテムが 1 つあります。

<div class="wrapper">
  <a href="#">Item 1</a>
  <a href="#">Item 2</a>
  <a href="#">Item 3</a>
  <a href="#">Item 4</a>
  <a class="top" href="#">Item 5</a>
</div>

このアイテムの reading-order-1 に設定すると、フォーカス順序で最初にこのアイテムが訪れ、残りの読み上げフロー アイテムはソース順序にフォールバックします。

その他の例については、chrome.dev のサイトをご覧ください。

tabindex とのインタラクション

従来、デベロッパーは HTML tabindex グローバル属性を使用して、HTML 要素をフォーカス可能にし、連続フォーカス ナビゲーションの相対的な順序を決定していました。ただし、この属性には多くのデメリットとユーザー補助に関する懸念事項があります。主な懸念事項は、正の tabindex を使用して作成された tabindex 順のフォーカス ナビゲーションが、ユーザー補助ツリーで認識されないことです。誤って使用すると、スクリーン リーダーでの操作と一致しない、ジャンプするフォーカス順序になる可能性があります。この問題を解決するには、aria-owns HTML 属性を使用して順序付けを追跡します。

前の flex の例で、reading-flow: flex-visual を使用する場合と同じ結果を得るには、次のようにします。

<div class="box" aria-owns="one three two">
  <a href="#" tabindex="1" id="one">One</a>
  <a href="#" tabindex="3" id="two">Two</a>
  <a href="#" tabindex="2" id="three">Three</a>
</div>

コンテナ外の別の要素にも tabindex=1 が含まれている場合はどうなりますか?次に、tabindex=1 を持つすべての要素がまとめて訪問され、次の増分 tabindex 値に移動します。このような飛び跳ねるような連続ナビゲーションは、ユーザー エクスペリエンスを損ないます。そのため、ユーザー補助の専門家は、正の tabindex は使用しないことを推奨していますreading-flow の設計時に、この問題を修正しようとしました。

reading-flow プロパティが設定されたコンテナは、フォーカス スコープのオーナーになります。つまり、コンテナ内のすべての要素に順番にフォーカスを移動してから、ウェブドキュメント内の次のフォーカス可能な要素に移動します。また、その直接の子要素は reading-flow プロパティを使用して並べ替えられ、並べ替えの目的では正の tabindex は無視されます。読み上げフローのアイテムの子孫に正の tabindex を設定することは引き続き可能です。

display: contents を持つ要素で、レイアウトの親から reading-flow プロパティを継承している要素も、有効な読み上げフロー コンテナになります。サイトを設計する際は、この点に注意してください。詳しくは、reading-flowdisplay: contents に関するフィードバック リクエストをご覧ください。

お知らせください

この投稿の例と reading-flow chrome.dev の例を試し、これらの CSS プロパティをサイトで使用してください。フィードバックがある場合は、CSS ワーキング グループの GitHub リポジトリで問題として報告してください。tabindex とフォーカス スコープの動作について具体的なフィードバックがある場合は、HTML WHATNOT GitHub リポジトリで問題として報告してください。この機能について、ぜひフィードバックをお寄せください。