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

公開日: 2025 年 5 月 1 日

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

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

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

reading-flow

reading-flow CSS プロパティは、Flex、Grid、Block レイアウトの要素がユーザー補助ツールに公開される順序と、線形順次ナビゲーション メソッドを使用してフォーカスされる方法を制御します。

キーワード値を 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 を設定することは可能です。

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

お知らせください

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