Skip to content

Commit 2297f0e

Browse files
committed
[css-scroll-anchoring] Import Scroll Anchoring, per WG resolution, and convert to Bikeshed. Still some linking errors, will fix later.
1 parent 3652e8b commit 2297f0e

1 file changed

Lines changed: 262 additions & 0 deletions

File tree

css-scroll-anchoring/Overview.bs

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
<pre class=metadata>
2+
Title: CSS Scroll Anchoring Module Level 1
3+
Shortname: css-scroll-anchoring
4+
Level: 1
5+
Group: csswg
6+
Status: ED
7+
Work Status: Revising
8+
ED: https://drafts.csswg.org/css-scroll-anchoring
9+
Editor: Steve Kobes, Google
10+
Editor: Tab Atkins-Bittner, Google, http://xanthir.com/contact/, w3cid 42199
11+
Abstract: Changes in DOM elements above the visible region of a <a>scrolling box</a> can result in the page moving
12+
Abstract: while the user is in the middle of consuming the content.
13+
Abstract:
14+
Abstract: This spec proposes a mechanism to mitigate this jarring user experience
15+
Abstract: by keeping track of the position of an anchor node and adjusting the scroll offset accordingly.
16+
Abstract:
17+
Abstract: This spec also proposes an API for web developers to opt-out of this behavior.
18+
</pre>
19+
20+
<pre class=link-defaults>
21+
spec:css22; type:property;
22+
text:max-height
23+
text:min-height
24+
text:height
25+
text:max-width
26+
text:min-width
27+
text:width
28+
</pre>
29+
30+
<h2 id=intro>
31+
Introduction</h2>
32+
33+
Today, users of the web are often distracted by content moving around
34+
due to changes that occur outside the viewport.
35+
Examples include script inserting an iframe containing an ad,
36+
or non-sized images loading on a slow network.
37+
38+
Historically the browser's default behavior has been
39+
to preserve the absolute scroll position when such changes occur.
40+
This means that to avoid shifting content,
41+
the webpage can attempt to reserve space on the page
42+
for anything that will load later.
43+
In practice, few websites do this consistently.
44+
45+
Scroll anchoring aims to minimize surprising content shifts.
46+
It does this by adjusting the scroll position
47+
to compensate for the changes outside the viewport.
48+
49+
The <a href="https://github.com/WICG/ScrollAnchoring/blob/master/explainer.md">explainer document</a>
50+
gives an informal overview of scroll anchoring.
51+
52+
<h2 id='description'>
53+
Description</h2>
54+
55+
Scroll anchoring works by selecting a DOM node (the <dfn export for=scroll-anchoring>anchor node</dfn>)
56+
whose movement is used to determine adjustments to the scroll position.
57+
58+
<h3 id='anchor-node-selection'>
59+
Anchor Node Selection</h3>
60+
61+
Each <a>scrolling box</a> aims to select an <a>anchor node</a>
62+
that is deep in the DOM
63+
and close to the block start edge of its <a>optimal viewing region</a>.
64+
65+
Note: If the user agent does not support the 'scroll-padding' property,
66+
the optimal viewing region of the scrolling box is equivalent to its <a>content area</a>.
67+
68+
The anchor node is either a non-anonymous <a>block box</a> or a <a>text node</a>.
69+
The anchor node is always a <a>descendant</a> of the <a>scrolling box</a>.
70+
In some cases, a scrolling box may not select any anchor node.
71+
72+
<div algorithm>
73+
The <dfn id="anchoring-algorithm">anchor node selection algorithm</dfn>
74+
for a scrolling box |S| is as follows:
75+
76+
1. If |S| is associated with an element
77+
whose computed value of the 'overflow-anchor' property is ''overflow-anchor/none'',
78+
then do not select an anchor node for |S|.
79+
2. Otherwise, for each DOM child |N| of the element or document associated with |S|,
80+
perform the <a>candidate examination algorithm</a> for |N| in |S|,
81+
and terminate if it selects an anchor node.
82+
</div>
83+
84+
<div algorithm>
85+
The <dfn id="candidate-examination">candidate examination algorithm</dfn>
86+
for a candidate DOM node |N| in a scrolling box |S| is as follows:
87+
88+
1. If |N| is an <a>excluded subtree</a>,
89+
or if |N| is <a>fully clipped</a> in |S|,
90+
then do nothing (|N| and its descendants are skipped).
91+
2. If |N| is <a>fully visible</a> in |S|,
92+
select |N| as the anchor node.
93+
3. If N is <a>partially visible</a>:
94+
1. For each DOM child |C| of |N|,
95+
perform the <a>candidate examination algorithm</a> for |C| in |S|,
96+
and terminate if it selects an anchor node.
97+
2. For each <a>absolutely positioned</a> element |A|
98+
whose <a>containing block</a> is |N|,
99+
but whose DOM parent is not |N|,
100+
perform the <a>candidate examination algorithm</a> for |A| in |S|,
101+
and terminate if it selects an anchor node.
102+
3. Select |N| as the anchor node.
103+
(If this step is reached,
104+
no suitable anchor node was found among |N|’s descendants.)
105+
106+
Note: Deeper nodes are preferred to minimize the possibility of content changing
107+
inside the anchor node but outside the viewport, which would cause visible
108+
content to shift without triggering any scroll anchoring adjustment.
109+
</div>
110+
111+
Conceptually, a new anchor node is computed for every scrolling box
112+
whenever the scroll position of any scrolling box changes.
113+
(As a performance optimization,
114+
the implementation may wait until the anchor node is needed before computing it.)
115+
116+
<div algorithm>
117+
A DOM node |N| is an <dfn>excluded subtree</dfn>
118+
if it is an element and any of the following conditions holds:
119+
120+
* |N|’s computed value of the 'display' property is ''display/none''.
121+
* |N|’s computed value of the 'position' property is ''position/fixed''.
122+
* |N|’s computed value of the 'position' property is ''position/absolute''
123+
and |N|’s <a>containing block</a> is an ancestor of the scrolling box.
124+
* |N|’s computed value of the 'overflow-anchor' property is ''overflow-anchor/none''.
125+
</div>
126+
127+
<div algorithm>
128+
A DOM node |N| is <dfn>fully visible</dfn> in a scrolling box |S|
129+
if |N|’s <a>scroll anchoring bounding rect</a> is entirely within the <a>optimal viewing region</a> of |S|.
130+
</div>
131+
132+
<div algorithm>
133+
A DOM node |N| is <dfn>fully clipped</dfn> in a scrolling box |S|
134+
if |N|’s <a>scroll anchoring bounding rect</a> is entirely outside the <a>optimal viewing region</a> of |S|.
135+
</div>
136+
137+
<div algorithm>
138+
A DOM node |N| is <dfn>partially visible</dfn> in a scrolling box |S|
139+
if |N| is neither <a>fully visible</a> in |S| nor <a>fully clipped</a> in |S|.
140+
</div>
141+
142+
<div algorithm>
143+
The <dfn>scroll anchoring bounding rect</dfn> of a DOM node |N|
144+
is |N|’s <a>scrollable overflow rectangle</a> if |N| is a <a>block box</a>,
145+
or the bounding rect of its <a>line boxes</a> if |N| is a text node.
146+
</div>
147+
148+
<h3 id="scroll-adjustment">
149+
Scroll Adjustment</h3>
150+
151+
If an anchor node was selected,
152+
then when the anchor node moves,
153+
the browser computes the previous offset <code>y0</code>, and the current offset <code>y1</code>,
154+
of the block start edge of the anchor node's <a>scroll anchoring bounding rect</a>,
155+
relative to the block start edge of the scrolling content in the <a>block flow direction</a> of the scroller.
156+
157+
It then queues an adjustment to the scroll position of <code>y1 - y0</code>,
158+
in the block flow direction,
159+
to be performed at the end of the <a>suppression window</a>.
160+
161+
The scroll adjustment is a type of [[cssom-view-1#scrolling-events#scrolling]] as defined by [[!CSSOM-VIEW]],
162+
and generates <a>scroll events</a> in the manner described there.
163+
164+
<h4 id="suppression-windows">
165+
Suppression Window</h4>
166+
167+
Every movement of an anchor node occurs within a window of time
168+
called the <dfn>suppression window</dfn>,
169+
defined as follows:
170+
171+
* The suppression window begins at the start of the current iteration of the
172+
<a href="https://html.spec.whatwg.org/multipage/webappapis.html#processing-model-8">HTML Processing Model</a> event loop,
173+
or at the end of the most recently completed suppression window,
174+
whichever is more recent.
175+
* The suppression window ends at the end of the current iteration of the
176+
<a href="https://html.spec.whatwg.org/multipage/webappapis.html#processing-model-8">HTML Processing Model</a> event loop,
177+
or immediately before the next operation whose result or side effects
178+
would differ as a result of a change in the scroll position
179+
(for example, an invocation of {{Element/getBoundingClientRect()}}),
180+
whichever comes sooner.
181+
182+
Note: The suppression window boundaries should be incorporated into the HTML standard once the
183+
scroll anchoring API is stabilized.
184+
185+
More than one anchor node movement may occur within the same suppression window.
186+
187+
At the end of a suppression window,
188+
the user agent performs all scroll adjustments that were queued during the window
189+
and not suppressed by any <a>suppression trigger</a> during the window.
190+
191+
<h4 id="suppression-triggers">
192+
Suppression Triggers</h4>
193+
194+
A <dfn>suppression trigger</dfn> is an operation
195+
that suppresses the scroll anchoring adjustment for an anchor node movement,
196+
if it occurs within the suppression window for that movement.
197+
These triggers are:
198+
199+
* Any change to the computed value of any of the following properties,
200+
on any element in the path from the anchor node to the scrollable element (or document),
201+
inclusive of both:
202+
203+
* 'top', 'left', 'right', or 'bottom'
204+
* 'margin' or its longhands
205+
* 'padding' or its longhands
206+
* 'width', 'height', 'min-width', 'max-width', 'min-height', or 'max-height'
207+
* 'position'
208+
* 'transform'
209+
210+
* Any change to the computed value of the 'position' property
211+
on any element within the scrollable element (or document),
212+
such that the element becomes or stops being <a>absolutely positioned</a>.
213+
Note that this trigger applies regardless of whether the modified element is
214+
on the path from the anchor node to the scrollable element.
215+
216+
Note: Suppression triggers exist for compatibility with existing web content that has negative
217+
interactions with scroll anchoring due to shifting content in scroll event handlers.
218+
219+
<h2 id="exclusion-api">
220+
Exclusion API</h2>
221+
222+
Scroll anchoring aims to be the default mode of behavior when launched,
223+
so that users benefit from it even on legacy content.
224+
'overflow-anchor' can disable scroll anchoring in part or all of a webpage (opt out),
225+
or exclude portions of the DOM from the anchor node selection algorithm.
226+
227+
<pre class=propdef>
228+
Name: overflow-anchor
229+
Value: auto | none
230+
Initial: auto
231+
Inherited: yes
232+
</pre>
233+
234+
Values are defined as follows:
235+
236+
<dl dfn-type=value dfn-for=overflow-anchor>
237+
: <dfn>auto</dfn>
238+
::
239+
Declares that the DOM subtree rooted at the element
240+
is eligible to participate in the <a>anchor node selection algorithm</a>
241+
for any scrolling box created by the element or an ancestor.
242+
: <dfn>none</dfn>
243+
::
244+
Declares that the DOM subtree rooted at E
245+
is <em>not</em> eligible to participate in the <a>anchor node selection algorithm</a>
246+
for any scrolling box created by the element or an ancestor.
247+
</dl>
248+
249+
Note: The <code>overflow-anchor</code> property was also proposed (with different values)
250+
for <a href="http://tabatkins.github.io/specs/css-sticky-scrollbars/">CSS Sticky Scrollbars</a>,
251+
which has now been <a href="https://tabatkins.github.io/specs/css-sticky-scrollbars/#intro">superseded</a>.
252+
253+
Issue: The spec originally said that 'overflow-anchor' isn't inherited,
254+
but, uh, I don't know how that works,
255+
given that the values are defined as affecting their descendants.
256+
257+
<h2 id='priv-sec'>
258+
Privacy and Security Considerations</h2>
259+
260+
This specification,
261+
as it only adjusts how we compute scroll positions,
262+
introduces no new privacy or security considerations.

0 commit comments

Comments
 (0)