Skip to content

Commit 9468285

Browse files
authored
[css-conditional-5] Draft spec for scroll-state() w3c#6402 (w3c#10798)
Per resolution in [1], add scroll-state() as a query function to query sticky, snap, and overflow scroll states with a new container-type for scroll-state queries. [1] w3c#6402 (comment)
1 parent 22558d5 commit 9468285

File tree

1 file changed

+232
-33
lines changed

1 file changed

+232
-33
lines changed

css-conditional-5/Overview.bs

+232-33
Original file line numberDiff line numberDiff line change
@@ -348,10 +348,10 @@ Container Queries</h2>
348348
[=container queries=] allow testing aspects of elements within the document
349349
(such as box dimensions or computed styles).
350350

351-
By default, all elements are [=query containers=]
351+
By default, all elements are <dfn export lt="query container">query containers</dfn>
352352
for the purpose of [=container style queries=],
353353
and can be established as [=query containers=]
354-
for [=container size queries=] by specifying
354+
for [=container size queries=] and [=container scroll-state queries=] by specifying
355355
the additional query types using the 'container-type' property
356356
(or the 'container' [=shorthand=]).
357357
Style rules applying to a [=query container=]’s [=flat tree=] descendants
@@ -385,9 +385,9 @@ Container Queries</h2>
385385
</div>
386386

387387
For the ''::part()'' and ''::slotted()'' <a>pseudo-element</a> selectors,
388-
which represent real elements in the DOM tree, query containers can be
388+
which represent real elements in the DOM tree, [=query containers=] can be
389389
established by [=flat tree=] ancestors of those elements.
390-
For other <a>pseudo-element</a>s, query containers can be established by
390+
For other <a>pseudo-element</a>s, [=query containers=] can be established by
391391
inclusive [=flat tree=] ancestors of their <a>originating element</a>.
392392

393393
<div class=note>
@@ -461,28 +461,20 @@ Creating Query Containers: the 'container-type' property</h3>
461461

462462
<pre class='propdef'>
463463
Name: container-type
464-
Value: normal | size | inline-size
464+
Value: normal | [ [ size | inline-size ] || scroll-state ]
465465
Initial: normal
466466
Inherited: no
467467
Applies to: all elements
468468
Computed value: specified keyword
469469
Animation type: not animatable
470470
</pre>
471471

472-
The 'container-type' property establishes the element
473-
as a <dfn export>query container</dfn>
474-
for the purpose of [=container queries=] that require explicit containment
475-
(such as [=container size queries=]),
476-
allowing [=style rules=] styling its descendants
477-
to query various aspects of its sizing and layout,
478-
and respond accordingly.
479-
480-
Unless otherwise noted,
481-
all elements are [=query containers=]
482-
for the purpose of [=container queries=]
483-
that do not require explicit [=containment=]
484-
(such as [=container style queries=]),
485-
regardless of the specified 'container-type'.
472+
The 'container-type' property establishes the element as a
473+
[=query container=] for certain types of queries. For size
474+
[=container queries=], which require certain types of containment, elements
475+
are explicitly made [=query containers=] through this property. For other
476+
types of [=query containers=] any element can be a [=query container=], such
477+
as for [=container style queries=].
486478

487479
Values have the following meanings:
488480

@@ -503,10 +495,13 @@ Creating Query Containers: the 'container-type' property</h3>
503495
[=style containment=],
504496
and [=inline-size containment=]
505497
to the [=principal box=].
498+
<dt><dfn>scroll-state</dfn>
499+
<dd>
500+
Establishes a [=query container=] for [=scroll-state queries=]
506501
<dt><dfn>normal</dfn>
507502
<dd>
508503
The element is not a [=query container=]
509-
for any [=container size queries=],
504+
for any [=container size queries=] or [=scroll-state queries=],
510505
but remains a [=query container=] for [=container style queries=].
511506
</dl>
512507

@@ -547,6 +542,23 @@ Creating Query Containers: the 'container-type' property</h3>
547542
</pre>
548543
</div>
549544

545+
<div class=example>
546+
Containers can also expose state that depends on scroll offset. This example
547+
styles a descendant of a sticky positioned element when it is stuck to the
548+
top edge:
549+
550+
<pre class=lang-css>
551+
#sticky {
552+
container-type: scroll-state;
553+
position: sticky;
554+
}
555+
@container scroll-state(stuck: top) {
556+
#sticky-child {
557+
background-color: lime;
558+
}
559+
}
560+
</pre>
561+
</div>
550562

551563
<h3 id="container-name">
552564
Naming Query Containers: the 'container-name' property</h3>
@@ -658,19 +670,27 @@ Container Queries: the ''@container'' rule</h3>
658670
<pre class="prod def">
659671
<dfn><<container-condition>></dfn> = [ <<container-name>> ]? <<container-query>>
660672
<dfn><<container-name>></dfn> = <<custom-ident>>
661-
<dfn><<container-query>></dfn> = not <<query-in-parens>>
662-
| <<query-in-parens>> [ [ and <<query-in-parens>> ]* | [ or <<query-in-parens>> ]* ]
663-
<dfn><<query-in-parens>></dfn> = ( <<container-query>> )
664-
| ( <<size-feature>> )
665-
| style( <<style-query>> )
666-
| <<general-enclosed>>
667-
668-
<dfn><<style-query>></dfn> = not <<style-in-parens>>
669-
| <<style-in-parens>> [ [ and <<style-in-parens>> ]* | [ or <<style-in-parens>> ]* ]
670-
| <<style-feature>>
671-
<dfn><<style-in-parens>></dfn> = ( <<style-query>> )
672-
| ( <<style-feature>> )
673-
| <<general-enclosed>>
673+
<dfn><<container-query>></dfn> = not <<query-in-parens>>
674+
| <<query-in-parens>> [ [ and <<query-in-parens>> ]* | [ or <<query-in-parens>> ]* ]
675+
<dfn><<query-in-parens>></dfn> = ( <<container-query>> )
676+
| ( <<size-feature>> )
677+
| style( <<style-query>> )
678+
| scroll-state( <<scroll-state-query>> )
679+
| <<general-enclosed>>
680+
681+
<dfn><<style-query>></dfn> = not <<style-in-parens>>
682+
| <<style-in-parens>> [ [ and <<style-in-parens>> ]* | [ or <<style-in-parens>> ]* ]
683+
| <<style-feature>>
684+
<dfn><<style-in-parens>></dfn> = ( <<style-query>> )
685+
| ( <<style-feature>> )
686+
| <<general-enclosed>>
687+
688+
<dfn><<scroll-state-query>></dfn> = not <<scroll-state-in-parens>>
689+
| <<scroll-state-in-parens>> [ [ and <<scroll-state-in-parens>> ]* | [ or <<scroll-state-in-parens>> ]* ]
690+
| <<scroll-state-feature>>
691+
<dfn><<scroll-state-in-parens>></dfn> = ( <<scroll-state-query>> )
692+
| ( <<scroll-state-feature>> )
693+
| <<general-enclosed>>
674694
</pre>
675695

676696
The keywords ''container-name/none'', ''and'', ''not'', and ''or''
@@ -1068,6 +1088,185 @@ Style Container Features</h3>
10681088
are [=computed value|computed=] with respect to the [=query container=],
10691089
the same as other values.
10701090

1091+
<h3 id="scroll-state-container">
1092+
Scroll State Container Features</h3>
1093+
1094+
A <dfn export>container scroll-state query</dfn> allows querying a container for
1095+
state that depends on scroll position. It is a boolean combination of
1096+
individual <dfn lt="scroll-state feature">scroll-state features</dfn>
1097+
(<<scroll-state-feature>>) that each query a single feature of the
1098+
[=query container=]. The syntax of a <dfn><<scroll-state-feature>></dfn> is the
1099+
same as for a [=media feature=]: a feature name, a comparator, and a value.
1100+
1101+
[=Scroll-state features=] can either match state of the scroller itself,
1102+
or an element that is affected by the scroll position of an ancestor
1103+
[=scroll container's=] [=scrollport=]. An example of the former is the
1104+
''overflowing'' feature, ''snapped'' the latter.
1105+
1106+
<h4 id="updating-scroll-state">
1107+
Updating Scroll State</h4>
1108+
1109+
Issue(10796): This section is subject to change as a result of resolving
1110+
the issue of unifying scroll-snapshotting layout state across several specifications.
1111+
1112+
Scroll state may cause layout cycles since queried scroll state may cause style changes,
1113+
which may lead to scroll state changes as a result of layout. The same issue exists for
1114+
[=scroll progress timelines=], and scroll state is handled in a similar manner.
1115+
1116+
To avoid such layout cycles, ''scroll-state'' [=query containers=] update their
1117+
current state once as the last step of [=run the scroll steps=]. Then, after the
1118+
resizeObserver loop in the
1119+
<a href="https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model">HTML event loop processing model</a>,
1120+
update the current state of every ''scroll-state'' [=query container=].
1121+
If that state has changed since the scroll state update in [=run the scroll steps=],
1122+
re-run the style and layout update a single time if necessary.
1123+
1124+
<h4 id="stuck">
1125+
Sticky positioning: the '@container/stuck' feature</h4>
1126+
1127+
<pre class="descdef mq">
1128+
Name: stuck
1129+
For: @container
1130+
Value: top | right | bottom | left | block-start | inline-start | block-end | inline-end
1131+
Type: discrete
1132+
</pre>
1133+
1134+
The '@container/stuck' [=container feature=] queries whether a
1135+
''position/sticky'' positioned container is visually shifted to stay inside
1136+
the [=sticky view rectangle=] for the given edge. The logical edges map to
1137+
physical based on the direction and writing-mode of the [=query container=].
1138+
None of the values match if the [=query container=] is not [=sticky positioned=].
1139+
1140+
It is possible for two values from opposite axes to match at the same time,
1141+
but not for opposite edges along the same axis.
1142+
1143+
<div class=example>
1144+
May match:
1145+
<pre class="lang-css">
1146+
@container scroll-state((stuck: top) and (stuck: left)) { ... }
1147+
</pre>
1148+
1149+
Will never match:
1150+
<pre class="lang-css">
1151+
@container scroll-state((stuck: left) and (stuck: right)) { ... }
1152+
</pre>
1153+
</div>
1154+
1155+
In the boolean context, the query matches if visual shift is applied in any
1156+
direction.
1157+
1158+
<dl dfn-type=value dfn-for="@container/stuck">
1159+
<dt><dfn>top</dfn>
1160+
<dd>
1161+
The ''position/sticky'' container is shifted to stay inside the top edge.
1162+
<dt><dfn>right</dfn>
1163+
<dd>
1164+
The ''position/sticky'' container is shifted to stay inside the right edge.
1165+
<dt><dfn>bottom</dfn>
1166+
<dd>
1167+
The ''position/sticky'' container is shifted to stay inside the bottom edge.
1168+
<dt><dfn>left</dfn>
1169+
<dd>
1170+
The ''position/sticky'' container is shifted to stay inside the left edge.
1171+
<dt><dfn>block-start</dfn>
1172+
<dd>
1173+
The ''position/sticky'' container is shifted to stay inside the [=block-start=] edge.
1174+
<dt><dfn>inline-start</dfn>
1175+
<dd>
1176+
The ''position/sticky'' container is shifted to stay inside the [=inline-start=] edge.
1177+
<dt><dfn>block-end</dfn>
1178+
<dd>
1179+
The ''position/sticky'' container is shifted to stay inside the [=block-end=] edge.
1180+
<dt><dfn>inline-end</dfn>
1181+
<dd>
1182+
The ''position/sticky'' container is shifted to stay inside the [=inline-end=] edge.
1183+
</dl>
1184+
1185+
<h4 id="snapped">
1186+
Scroll snapping: the '@container/snapped' feature</h4>
1187+
1188+
<pre class="descdef mq">
1189+
Name: snapped
1190+
For: @container
1191+
Value: x | y | block | inline
1192+
Type: discrete
1193+
</pre>
1194+
1195+
The '@container/snapped' [=container feature=] queries whether a [=snap target=]
1196+
is snapped to its [=snap container=] in the given axis. It matches in the boolean
1197+
context if it is snapped in at least one of the directions.
1198+
1199+
<dl dfn-type=value dfn-for="@container/snapped">
1200+
<dt><dfn>x</dfn>
1201+
<dd>
1202+
'@container/snapped' [=container feature=] matches ''x''
1203+
if the [=query container=] is a horizontal [=snap target=] for its [=scroll container=]
1204+
<dt><dfn>y</dfn>
1205+
<dd>
1206+
'@container/snapped' [=container feature=] matches ''y''
1207+
if the [=query container=] is a vertical [=snap target=] for its [=scroll container=]
1208+
<dt><dfn>block</dfn>
1209+
<dd>
1210+
'@container/snapped' [=container feature=] matches ''block''
1211+
if the [=query container=] is a [=snap target=] for its [=scroll container=]
1212+
in the block direction of the [=snap container=].
1213+
<dt><dfn>inline</dfn>
1214+
<dd>
1215+
'@container/snapped' [=container feature=] matches ''inline''
1216+
if the [=query container=] is a [=snap target=] for its [=scroll container=]
1217+
in the inline direction of the [=snap container=].
1218+
</dl>
1219+
1220+
<h4 id="overflowing">
1221+
Overflowing: the '@container/overflowing' feature</h4>
1222+
1223+
<pre class="descdef mq">
1224+
Name: overflowing
1225+
For: @container
1226+
Value: top | right | bottom | left | block-start | inline-start | block-end | inline-end
1227+
Type: discrete
1228+
</pre>
1229+
1230+
The '@container/overflowing' [=container feature=] queries whether a
1231+
[=scroll container=] has clipped [=scrollable overflow rectangle=] content
1232+
in the given direction which is reachable through user initiated scrolling.
1233+
That is, '@container/overflowing' does not match for a ''overflow/hidden''
1234+
container, nor for a [=negative scrollable overflow region=].
1235+
1236+
The logical values map to physical based on the direction and writing-mode of
1237+
the [=query container=]. None of the values match if the container is not a
1238+
[=scroll container=].
1239+
1240+
In the boolean context, the query matches if any of the values match.
1241+
1242+
<dl dfn-type=value dfn-for="@container/overflowing">
1243+
<dt><dfn>top</dfn>
1244+
<dd>
1245+
The [=scroll container=] has [=scrollable overflow=] past the top edge.
1246+
<dt><dfn>right</dfn>
1247+
<dd>
1248+
The [=scroll container=] has [=scrollable overflow=] past the right edge.
1249+
<dt><dfn>bottom</dfn>
1250+
<dd>
1251+
The [=scroll container=] has [=scrollable overflow=] past the bottom edge.
1252+
<dt><dfn>left</dfn>
1253+
<dd>
1254+
The [=scroll container=] has [=scrollable overflow=] past the left edge.
1255+
<dt><dfn>block-start</dfn>
1256+
<dd>
1257+
The [=scroll container=] has [=scrollable overflow=] past the [=block-start=] edge.
1258+
<dt><dfn>inline-start</dfn>
1259+
<dd>
1260+
The [=scroll container=] has [=scrollable overflow=] past the [=inline-start=] edge.
1261+
<dt><dfn>block-end</dfn>
1262+
<dd>
1263+
The [=scroll container=] has [=scrollable overflow=] past the [=block-end=] edge.
1264+
<dt><dfn>inline-end</dfn>
1265+
<dd>
1266+
The [=scroll container=] has [=scrollable overflow=] past the [=inline-end=] edge.
1267+
</dl>
1268+
1269+
10711270
<h2 id="container-lengths">
10721271
Container Relative Lengths: the ''cqw'', ''cqh'', ''cqi'', ''cqb'', ''cqmin'', ''cqmax'' units</h2>
10731272

0 commit comments

Comments
 (0)