Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for CSS reading-flow #10613

Open
wants to merge 25 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9708fb7
Add support for reading-flow
dizhang168 Sep 10, 2024
45fa391
Definitions and nits
domfarolino Sep 17, 2024
35dd322
Review changes
dizhang168 Sep 18, 2024
dc70a78
Address format review changes
dizhang168 Sep 26, 2024
31b2e80
Editorial: clean up small parts of sequential focus navigation (#10632)
domfarolino Oct 2, 2024
a5656ba
Add support for reading-flow
dizhang168 Sep 10, 2024
33c0f64
Address format review changes
dizhang168 Sep 26, 2024
93ec208
Update definitions to make reading flow item a scope owner
dizhang168 Oct 28, 2024
9aeb6c2
Fix previous bad merge fixes
dizhang168 Oct 28, 2024
5b543f6
PR format review changes
dizhang168 Nov 12, 2024
9fa15ee
Define reading flow order algorithm, add example and other improvements
dizhang168 Nov 13, 2024
391d1bc
Update CSS Display links + fix ol error
dizhang168 Nov 14, 2024
adc4888
Small nits
domfarolino Nov 27, 2024
512e4fb
Small clarifications
domfarolino Nov 27, 2024
97a14d9
Review changes: refactor example
dizhang168 Dec 3, 2024
910492d
Fix wrong nesting of node
dizhang168 Dec 4, 2024
7fe3c3e
Reword a couple definitions and add a note
domfarolino Dec 17, 2024
58bd0b0
review changes
dizhang168 Dec 18, 2024
4e4a4c3
Address more review comments
dizhang168 Dec 19, 2024
1d6b374
Some clean-ups, clarification, and wording
domfarolino Dec 20, 2024
da9d18b
Example wording
domfarolino Dec 20, 2024
790976d
Match CONTRIBUTING.md file for conditions
domfarolino Dec 20, 2024
84883e8
Improve example + remove unnecessary association description
dizhang168 Dec 20, 2024
6a79572
Run `specfmt`
domfarolino Dec 20, 2024
8aacc86
center content in reading-flow-order-example.svg
dizhang168 Dec 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions images/reading-flow-order-example.svg
dizhang168 marked this conversation as resolved.
Show resolved Hide resolved
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
320 changes: 309 additions & 11 deletions source
Original file line number Diff line number Diff line change
Expand Up @@ -3818,6 +3818,10 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
<li><dfn data-x-href="https://drafts.csswg.org/css-display/#inline-formatting-context">inline formatting context</dfn></li>
<li><dfn data-x-href="https://drafts.csswg.org/css-display/#replaced-element">replaced element</dfn></li>
<li><dfn data-x-href="https://drafts.csswg.org/css-display/#css-box">CSS box</dfn></li>
<li><dfn data-x-href="https://drafts.csswg.org/css-display/#css-parent-box">CSS parent box</dfn></li>
<li><dfn data-x-href="https://drafts.csswg.org/css-display-4/#reading-flow-container">reading flow container</dfn></li>
<li><dfn data-x-href="https://drafts.csswg.org/css-display-4/#rendering-defined-sibling-reading-flow">rendering-defined sibling reading flow</dfn></li>
<li>The <dfn data-x-href="https://drafts.csswg.org/css-display-4/#propdef-reading-flow">'reading-flow'</dfn> property</li>
</ul>

<p>The following features are defined in <cite>CSS Flexible Box Layout</cite>:
Expand Down Expand Up @@ -79781,9 +79785,10 @@ dictionary <dfn dictionary>ToggleEventInit</dfn> : <span>EventInit</span> {
<hr>

<p>A node is a <dfn>focus navigation scope owner</dfn> if it is a <code>Document</code>, a
<span>shadow host</span>, a <span>slot</span>, or an element in the <span
<span>shadow host</span>, a <span>slot</span>, an element in the <span
data-x="popover-showing-state">popover showing state</span> which also has a <span>popover
invoker</span> set.</p>
invoker</span> set, a <span>reading-flow-ordered scope owner</span>, or a <span>reading flow
item</span>.</p>

<p>Each <span>focus navigation scope owner</span> has a <dfn>focus navigation scope</dfn>, which
is a list of elements. Its contents are determined as follows:</p>
Expand All @@ -79807,6 +79812,12 @@ dictionary <dfn dictionary>ToggleEventInit</dfn> : <span>EventInit</span> {
<li><p>If <var>element</var> is in the <span data-x="popover-showing-state">popover showing
state</span> and has a <span>popover invoker</span> set, then return <var>element</var>.</p></li>

<li><p>If <var>element</var> is a <span>reading-flow-ordered scope owner</span>, then return
<var>element</var>.</p></li>

<li><p>If <var>element</var> is a <span>reading flow item</span>, then return
<var>element</var>.</p></li>

<li><p>Return <var>element</var>'s parent's <span>associated focus navigation owner</span>.</p></li>
</ol>

Expand All @@ -79816,8 +79827,9 @@ dictionary <dfn dictionary>ToggleEventInit</dfn> : <span>EventInit</span> {

<p class="note">The order of elements within a <span>focus navigation scope</span> does not impact
any of the algorithms in this specification. Ordering only becomes important for the
<span>tabindex-ordered focus navigation scope</span> and <span>flattened tabindex-ordered focus
navigation scope</span> concepts defined below.</p>
<span>tabindex-ordered focus navigation scope</span>, <span>flattened tabindex-ordered focus
navigation scope</span>, and <span>reading-flow-ordered focus navigation scope</span> concepts
defined below.</p>

<p>A <dfn>tabindex-ordered focus navigation scope</dfn> is a list of <span data-x="focusable
area">focusable areas</span> and <span data-x="focus navigation scope owner">focus navigation
Expand All @@ -79836,11 +79848,25 @@ dictionary <dfn dictionary>ToggleEventInit</dfn> : <span>EventInit</span> {
is a negative integer.</p></li>
</ul>

<p>The order within a <span>tabindex-ordered focus navigation scope</span> is determined by each
element's <span>tabindex value</span>, as described in the section below.</p>
<p>By default, the order within a <span>tabindex-ordered focus navigation scope</span> is determined
by each element's <span>tabindex value</span>. This ordering criteria is sometimes overridden, as is
dizhang168 marked this conversation as resolved.
Show resolved Hide resolved
the case with <span data-x="reading-flow-ordered focus navigation scope">reading-flow-ordered focus
navigation scopes</span>.</p>

<p class="note">The <span>tabindex value</span> rules do not give a precise ordering, as they are
composed mostly of "<!--non-normative-->should" statements and relative orderings.</p>

<p class="note">The rules there do not give a precise ordering, as they are composed mostly of
"<!--non-normative-->should" statements and relative orderings.</p>
<p>Every <span>reading-flow-ordered scope owner</span>'s <span>tabindex-ordered focus navigation
scope</span> is specifically a <dfn>reading-flow-ordered focus navigation scope</dfn>. The order
within a <span>reading-flow-ordered focus navigation scope</span> is determined by the
<span>reading flow order</span> algorithm; the use of <span>tabindex value</span> only determines
an item's focusability, but does not affect order within the scope.</p>

<p class="note">The <span>tabindex-ordered focus navigation scope</span> of a <span>reading flow
item</span> — and any other <span>focus navigation scope owner</span> for that matter — is not
specifically a <span>reading-flow-ordered focus navigation scope</span>; it is a normal
<span>tabindex-ordered focus navigation scope</span> whose order is determined by <span>tabindex
value</span> as usual.</p>

<p>A <dfn>flattened tabindex-ordered focus navigation scope</dfn> is a list of <span
data-x="focusable area">focusable areas</span>. Every <span>focus navigation scope owner</span>
Expand Down Expand Up @@ -80000,7 +80026,8 @@ dictionary <dfn dictionary>ToggleEventInit</dfn> : <span>EventInit</span> {
regardless.</p>
</dd>

dizhang168 marked this conversation as resolved.
Show resolved Hide resolved
dizhang168 marked this conversation as resolved.
Show resolved Hide resolved
<dt>If the value is a zero</dt>
<dt>If the value is a zero or if the value is a greater than zero and the element is a <span>reading
flow item</span></dt>

<dd>
<p>The user agent must allow the element to be considered as a <span>focusable area</span> and
Expand All @@ -80013,7 +80040,7 @@ dictionary <dfn dictionary>ToggleEventInit</dfn> : <span>EventInit</span> {
<span>shadow-including tree order</span>.</p>
</dd>

<dt>If the value is greater than zero</dt>
<dt>If the value is greater than zero and the element is not a <span>reading flow item</span></dt>

<dd>
<p>The user agent must allow the element to be considered as a <span>focusable area</span> and
Expand Down Expand Up @@ -80067,6 +80094,273 @@ dictionary <dfn dictionary>ToggleEventInit</dfn> : <span>EventInit</span> {
</div>

dizhang168 marked this conversation as resolved.
Show resolved Hide resolved

<div w-nodev>
dizhang168 marked this conversation as resolved.
Show resolved Hide resolved

<h4>Reading flow</h4>

dizhang168 marked this conversation as resolved.
Show resolved Hide resolved
<p>A <dfn>reading-flow-ordered scope owner</dfn> is either: <ref>CSSDISPLAY</ref></p>

<ul>
<li><p>a <span>reading flow container</span>, or</p></li>

<li><p>an element <span>delegating its rendering to its children</span> whose <span>CSS parent
box</span> is a <span>reading flow container</span>.</p></li>
</ul>

<p>Every <span>reading-flow-ordered scope owner</span> has an associated <span>reading flow
container</span>.</p>
dizhang168 marked this conversation as resolved.
Show resolved Hide resolved

dizhang168 marked this conversation as resolved.
Show resolved Hide resolved
<p>A <dfn>reading flow item</dfn> is an element whose <span>parent element</span> is a
<span>reading-flow-ordered scope owner</span>.</p>

dizhang168 marked this conversation as resolved.
Show resolved Hide resolved
<p>The <dfn>reading flow order</dfn> of a <span>reading-flow-ordered focus navigation scope</span>
<var>scope</var> is the ordered list of elements constructed as follows:</p>
dizhang168 marked this conversation as resolved.
Show resolved Hide resolved

<ol>
<li><p>Let <var>output</var> be « ».</p></li>

<li><p>Let <var>owner</var> be the <span>focus navigation scope owner</span> of
<var>scope</var>.</p></li>
dizhang168 marked this conversation as resolved.
Show resolved Hide resolved
dizhang168 marked this conversation as resolved.
Show resolved Hide resolved

<li><p>Let <var>container</var> be the <span>reading flow container</span> associated with
<var>owner</var>.</p></li>
dizhang168 marked this conversation as resolved.
Show resolved Hide resolved

<li><p>Let <var>items</var> be the <span>rendering-defined sibling reading flow</span> of
domfarolino marked this conversation as resolved.
Show resolved Hide resolved
<var>container</var>.</p></li>
dizhang168 marked this conversation as resolved.
Show resolved Hide resolved

dizhang168 marked this conversation as resolved.
Show resolved Hide resolved
<li>
<p><span data-x="list iterate">For each</span> <var>item</var> of <var>items</var>:</p>

<ol>
<li>
<p><span>While</span> <var>item</var> is an element that is not the <var>container</var>:</p>
dizhang168 marked this conversation as resolved.
Show resolved Hide resolved
<ol>
<li>
<p>If <var>item</var>'s <span>focus navigation scope owner</span> is <var>owner</var>:</p>
dizhang168 marked this conversation as resolved.
Show resolved Hide resolved

<ol>
<li><p>If <var>output</var> does not <span data-x="list contains">contain</span>
dizhang168 marked this conversation as resolved.
Show resolved Hide resolved
<var>item</var>, <span data-x="list append">append</span> <var>item</var> to
<var>output</var>.</p></li>

<li><p><span>Break</span>.</p></li>
</ol>

<p class="note">Different <var>item</var>s can have the same <span>inclusive
ancestor</span>. Only add to <var>output</var> if it is not already in the <span>reading
flow order</span>. This loop stops when the <span>inclusive ancestor</span> of
<var>item</var> within the <span>flat tree</span> is in the <var>scope</var>.</p>
dizhang168 marked this conversation as resolved.
Show resolved Hide resolved
</li>

dizhang168 marked this conversation as resolved.
Show resolved Hide resolved
<li><p>Set <var>item</var> to the parent element of <var>item</var> within the <span>flat
tree</span>.</p></li>
</ol>
</li>
</ol>

<p class="note">The <span>rendering-defined sibling reading flow</span> includes <span
data-x="descendant">descendants</span> of the <span>reading flow container</span> that are not in
the <span>reading-flow-ordered focus navigation scope</span>. This step traverses the <span
dizhang168 marked this conversation as resolved.
Show resolved Hide resolved
dizhang168 marked this conversation as resolved.
Show resolved Hide resolved
data-x="inclusive ancestor">inclusive ancestors</span> of <var>item</var> within the <span>flat
tree</span> to find a potential element to include in the <var>reading flow order</var>.</p>
</li>

dizhang168 marked this conversation as resolved.
Show resolved Hide resolved
<li>
<p><span data-x="list iterate">For each</span> <var>child</var> of <var>owner</var> element's <span
data-x="concept-tree-child">children</span>, in <span>tree order</span>:</p>
dizhang168 marked this conversation as resolved.
Show resolved Hide resolved
dizhang168 marked this conversation as resolved.
Show resolved Hide resolved
<ol>
<li><p>If <var>child</var>'s <span>focus navigation scope owner</span> is <var>owner</var> and
<var>output</var> does not <span data-x="list contains">contain</span> <var>child</var>, <span
data-x="list append">append</span> <var>child</var> to <var>output</var>.</p></li>
</ol>
</li>

<li><p>Return <var>output</var>.</p></li>
</ol>

</div>

<div class="example">
<p>The following example shows how to get the <span>flattened tabindex-ordered focus navigation
scope</span> for <code data-x="">wrapper</code>.</p>

<pre><code class="html">&lt;!DOCTYPE html>
&lt;html lang="en">
&lt;head>
&lt;style>
.wrapper { display: grid; reading-flow: grid-order; }
&lt;/style>
&lt;/head>
&lt;body>
&lt;div class="wrapper" tabindex="0">
&lt;div id="A" tabindex="0" style="order: 1">A
&lt;div id="B" tabindex="1">B&lt;/div>
&lt;/div>
dizhang168 marked this conversation as resolved.
Show resolved Hide resolved
&lt;div id="DC" tabindex="0" style="display: contents">
&lt;div id="C" tabindex="0" style="order: 3">C
&lt;div id="D" tabindex="0">D&lt;/div>
&lt;div id="E" tabindex="2">E&lt;/div>
&lt;/div>
&lt;div id="F" tabindex="0" style="order: 2">F&lt;/div>
&lt;/div>
&lt;div id="PA" style="position: absolute; left: 100px;" tabindex="0">
&lt;div id="G" tabindex="0">G&lt;/div>
&lt;/div>
&lt;div id="H" tabindex="0" style="order: 4">H&lt;/div>
&lt;/div>
&lt;/body>
&lt;/html></code></pre>

<p><img src="/images/reading-flow-order-example.svg" width="500" height="350" alt=""
class="darkmode-aware"></p>

<p>In this example, <code data-x="">wrapper</code> is the <span>focus navigation scope owner</span>
of a <span>reading-flow-ordered focus navigation scope</span> and a <span>reading flow
container</span>. Its <span>reading flow order</span> is computed as follows:</p>

<ul>
<li><p>Per step 4, we get <code data-x="">wrapper</code>'s <span>rendering-defined sibling
reading flow</span> <var>items</var> as « <code data-x="">A</code>, <code data-x="">F</code>,
<code data-x="">C</code>, <code data-x="">H</code> ».</p></li>

<li>
<p>Per step 5, we loop through <var>items</var>:</p>

<ul>
<li><p><code data-x="">A</code>'s <span>focus navigation scope owner</span> is <code
data-x="">wrapper</code>, it is added to <var>output</var>.</p></li>

<li>
<p><code data-x="">F</code>'s <span>focus navigation scope owner</span> is <code
data-x="">DC</code>.</p>

<ul>
<li><p>Set <var>item</var> to be <code data-x="">F</code>'s parent element within the
<span>flat tree</span>, <code data-x="">DC</code>.</p></li>

<li><p><code data-x="">DC</code>'s <span>focus navigation scope owner</span> is <code
dizhang168 marked this conversation as resolved.
Show resolved Hide resolved
data-x="">wrapper</code>, it is added to <var>output</var>.</p></li>
</ul>
</li>

<li>
<p><code data-x="">C</code>'s <span>focus navigation scope owner</span> is <code
data-x="">DC</code>.</p>
dizhang168 marked this conversation as resolved.
Show resolved Hide resolved

dizhang168 marked this conversation as resolved.
Show resolved Hide resolved
<ul>
<li><p>Set <var>item</var> to be <code data-x="">C</code>'s parent element within the
<span>flat tree</span>, <code data-x="">DC</code>.</p></li>

<li><p><code data-x="">DC</code>'s <span>focus navigation scope owner</span> is <code
data-x="">wrapper</code>, but it is already in <var>output</var>.</p></li>
</ul>
</li>

<li><p><code data-x="">H</code>'s <span>focus navigation scope owner</span> is <code
data-x="">wrapper</code>, it is added to <var>output</var>.</p></li>

<li><p><var>output</var> is « <code data-x="">A</code>, <code data-x="">DC</code>, <code
data-x="">H</code> ».</p></li>
</ul>
</li>

<li>
<p>Per step 6, we loop through the <span data-x="concept-tree-child">children</span> of <code
data-x="">wrapper</code> in <span>tree order</span>:</p>

<ul>
<li><p><code data-x="">A</code> is already in <var>output</var>.</p></li>

<li><p><code data-x="">DC</code> is already in <var>output</var>.</p></li>

<li><p><code data-x="">PA</code>'s <span>focus navigation scope owner</span> is <code
data-x="">wrapper</code>, it is added to <var>output</var>.</p></li>

<li><p><code data-x="">H</code> is already in <var>output</var>.</p></li>

<li><p><var>output</var> is « <code data-x="">A</code>, <code data-x="">DC</code>, <code
data-x="">H</code>, <code data-x="">PA</code> ».</p></li>
</ul>
</li>
</ul>

<p>Notice that <code data-x="">DC</code> is the <span>focus navigation scope owner</span> of a
<span>reading-flow-ordered focus navigation scope</span>, but its <span>reading flow
container</span> is still <code data-x="">wrapper</code>. Its <span>reading flow order</span> is
computed as follows:</p>

<ul>
<li><p>Per step 4, we again get <code data-x="">wrapper</code>'s <span>rendering-defined sibling
reading flow</span> <var>items</var> as « <code data-x="">A</code>, <code data-x="">F</code>,
<code data-x="">C</code>, <code data-x="">H</code> ».</p></li>

<li>
<p>Per step 5, we loop through <var>items</var>:</p>

<ul>
<li>
<p><code data-x="">A</code>'s <span>focus navigation scope owner</span> is <code
data-x="">wrapper</code>.</p>

<ul>
<li><p>Set <var>item</var> to be <code data-x="">A</code>'s parent element within the
<span>flat tree</span>, <code data-x="">wrapper</code>.</p></li>

<li><p>While loop ends because <var>item</var> is the <var>container</var>.</p></li>
</ul>
</li>

<li><p><code data-x="">F</code>'s <span>focus navigation scope owner</span> is <code
data-x="">DC</code>, it is added to <var>output</var>.</p></li>

<li><p><code data-x="">C</code>'s <span>focus navigation scope owner</span> is <code
data-x="">DC</code>, it is added to <var>output</var>.</p></li>

<li>
<p><code data-x="">H</code>'s <span>focus navigation scope owner</span> is <code
data-x="">wrapper</code>.</p>

<ul>
<li><p>Set <var>item</var> to be <code data-x="">H</code>'s parent element within the
<span>flat tree</span>, <code data-x="">wrapper</code>.</p></li>

<li><p>While loop ends because <var>item</var> is the <var>container</var>.</p></li>
</ul>
</li>

<li><p><var>output</var> is « <code data-x="">F</code>, <code data-x="">C</code> ».</p></li>
</ul>
</li>

<li>
<p>Per step 6, we loop through the <span data-x="concept-tree-child">children</span> of <code
data-x="">wrapper</code> in <span>tree order</span>:</p>

<ul>
<li><p><code data-x="">C</code> is already in <var>output</var>.</p></li>

<li><p><code data-x="">F</code> is already in <var>output</var>.</p></li>

<li><p><var>output</var> is « <code data-x="">F</code>, <code data-x="">C</code> ».</p></li>
</ul>
</li>
</ul>

<p><code data-x="">A</code>, <code data-x="">C</code>, <code data-x="">F</code>, <code
data-x="">PA</code> and <code data-x="">H</code> are <span data-x="reading flow item">reading flow
items</span>. Each is the <span>focus navigation scope owner</span> of a <span>tabindex-ordered
focus navigation scope</span> that does not follow the <span>reading flow order</span>. For example,
the <span>focus navigation scope</span> of C will be « <code data-x="">E</code>, <code
data-x="">D</code> » because <code data-x="">E</code> has a higher positive <span>tabindex
value</span> than <code data-x="">D</code>.</p>

<p>Combining everything together, the <span>flattened tabindex-ordered focus navigation scope</span>
for <code data-x="">wrapper</code> is « <code data-x="">wrapper</code>, <code data-x="">A</code>,
<code data-x="">B</code>, <code data-x="">DC</code>, <code data-x="">F</code>, <code
data-x="">C</code>, <code data-x="">E</code>, <code data-x="">D</code>, <code data-x="">H</code>,
<code data-x="">PA</code>, <code data-x="">G</code> ».</p>
</div>

<div w-nodev>

<h4 id="focus-processing-model"><span id="processing-model-5"></span>Processing model</h4>
Expand Down Expand Up @@ -80528,6 +80822,10 @@ dictionary <dfn dictionary>ToggleEventInit</dfn> : <span>EventInit</span> {
scope</span>, the ordering is not necessarily related to the <span>tree order</span> of the
<code>Document</code>.</p>

<p class="note">As a <span>reading-flow-ordered focus navigation scope</span> is a
<span>tabindex-ordered focus navigation scope</span>, it is flattened in <span>reading flow
order</span> into a <span>flattened tabindex-ordered focus navigation scope</span>.</p>

<p>If a <span>focusable area</span> is omitted from the <span>sequential focus navigation
order</span> of its <code>Document</code>, then it is unreachable via <span>sequential focus
navigation</span>.</p>
Expand Down Expand Up @@ -80693,7 +80991,7 @@ dictionary <dfn dictionary>ToggleEventInit</dfn> : <span>EventInit</span> {

<li>
<p>If <var>candidate</var> is a <span>navigable container</span> with a non-null <span>content
navigable</span>, then:</p>
navigable</span>:</p>

<ol>
<li><p>Let <var>recursive candidate</var> be the result of running the <span>sequential
Expand Down