From 6b08d4af75749a86e0ee1fd3d760ca0fdb497e5c Mon Sep 17 00:00:00 2001 From: Rune Lillesveen Date: Mon, 5 Aug 2024 12:47:39 +0200 Subject: [PATCH 1/6] [css-conditional-5] Draft spec for scroll-state() #6402 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] https://github.com/w3c/csswg-drafts/issues/6402#issuecomment-1812973013 --- css-conditional-5/Overview.bs | 231 +++++++++++++++++++++++++++++----- 1 file changed, 202 insertions(+), 29 deletions(-) diff --git a/css-conditional-5/Overview.bs b/css-conditional-5/Overview.bs index bc8e240f780..4c998a2b4fd 100644 --- a/css-conditional-5/Overview.bs +++ b/css-conditional-5/Overview.bs @@ -460,7 +460,7 @@ Creating Query Containers: the 'container-type' property
 		Name: container-type
-		Value: normal | size | inline-size
+		Value: normal | [ [ size | inline-size ] || scroll-state ]
 		Initial: normal
 		Inherited: no
 		Applies to: all elements
@@ -468,20 +468,12 @@ Creating Query Containers: the 'container-type' property
 		Animation type: not animatable
 	
- The 'container-type' property establishes the element - as a query container - for the purpose of [=container queries=] that require explicit containment - (such as [=container size queries=]), - allowing [=style rules=] styling its descendants - to query various aspects of its sizing and layout, - and respond accordingly. - - Unless otherwise noted, - all elements are [=query containers=] - for the purpose of [=container queries=] - that do not require explicit [=containment=] - (such as [=container style queries=]), - regardless of the specified 'container-type'. + The 'container-type' property establishes the element as a + query container for certain types of queries. For size + [=container queries=], that require certain types of containment, elements + are explicitly made [=query containers=] through this property. For other + types of [=query containers=] any element can be a query container, such as + for [=container style queries=]. Values have the following meanings: @@ -502,10 +494,13 @@ Creating Query Containers: the 'container-type' property [=style containment=], and [=inline-size containment=] to the [=principal box=]. +
scroll-state +
+ Establishes a [=query container=] for [=scroll-state queries=]
normal
The element is not a [=query container=] - for any [=container size queries=], + for any [=container size queries=] or [=scroll-state queries=], but remains a [=query container=] for [=container style queries=]. @@ -546,6 +541,23 @@ Creating Query Containers: the 'container-type' property +
+ Containers can also expose state that depend on scroll offset. Here is an + example of how to style a descendant of a sticky positioned element when it + is stuck to the top edge: + +
+		#sticky {
+			container-type: scroll-state;
+			position: sticky;
+		}
+		@container scroll-state(stuck: top) {
+		  #sticky-child {
+				background-color: lime;
+		  }
+		}
+		
+

Naming Query Containers: the 'container-name' property

@@ -657,19 +669,27 @@ Container Queries: the ''@container'' rule
 	<> = [ <> ]? <>
 	<> = <>
-	<>     = not <>
-	                      | <> [ [ and <> ]* | [ or <> ]* ]
-	<>     = ( <> )
-	                      | ( <> )
-	                      | style( <> )
-	                      | <>
-
-	<>         = not <>
-	                      | <> [ [ and <> ]* | [ or <> ]* ]
-	                      | <>
-	<>     = ( <> )
-	                      | ( <> )
-	                      | <>
+	<> = not <>
+	                  | <> [ [ and <> ]* | [ or <> ]* ]
+	<> = ( <> )
+	                  | ( <> )
+	                  | style( <> )
+	                  | scroll-state( <> )
+	                  | <>
+
+	<>     = not <>
+	                  | <> [ [ and <> ]* | [ or <> ]* ]
+	                  | <>
+	<> = ( <> )
+	                  | ( <> )
+	                  | <>
+
+	<>     = not <>
+	                         | <> [ [ and <> ]* | [ or <> ]* ]
+	                         | <>
+	<> = ( <> )
+	                         | ( <> )
+	                         | <>
 	
The keywords ''container-name/none'', ''and'', ''not'', and ''or'' @@ -1067,6 +1087,159 @@ Style Container Features are [=computed value|computed=] with respect to the [=query container=], the same as other values. +

+Scroll State Container Features

+ + A container scroll-state query allows to query a container for various + state that depends on scroll position. The ''scroll-state'' [=query container=] can either + be the scroller itself, or an element that is affected by the scroll position of an ancestor + [=scroll container's=] [=scrollport=]. + +

+Updating Scroll State

+
+ Note: This section is subject to change as a result of resolving + issue #10796 +
+ + Scroll state may cause layout cycles since queried scroll state may cause style changes, + which may lead to scroll state changes as a result of layout. The same issue exists for + [=scroll progress timelines=], and scroll state is handled in a similar manner. + + To avoid such layout cycles, ''scroll-state'' [=query containers=] update their + current state once as the last step of [=run the scroll steps=]. Then, after the + resizeObserver loop in the + HTML event loop processing model, + the scroll state of every ''scroll-state'' [=query container=] is updated. + If that state has changed since the scroll state update in [=run the scroll steps=], + re-run the style and layout update if necessary. + +

+Sticky positioning: the '@container/stuck' feature

+ +
+		Name: stuck
+		For: @container
+		Value: top | right | bottom | left | block-start | inline-start | block-end | inline-end
+		Type: discrete
+	
+ + The '@container/stuck' [=container feature=] queries whether a + ''position/sticky'' positioned container is visually shifted to stay inside + the [=sticky view rectangle=] for the given edge. The logical edges map to + physical based on the direction and writing-mode of the [=query container=]. + + In the boolean context, the query matches if visual shift is applied in any + direction. + +
+
top +
+ The ''position/sticky'' container is shifted to stay inside the top edge. +
right +
+ The ''position/sticky'' container is shifted to stay inside the right edge. +
bottom +
+ The ''position/sticky'' container is shifted to stay inside the bottom edge. +
left +
+ The ''position/sticky'' container is shifted to stay inside the left edge. +
block-start +
+ The ''position/sticky'' container is shifted to stay inside the block-start edge. +
inline-start +
+ The ''position/sticky'' container is shifted to stay inside the inline-start edge. +
block-end +
+ The ''position/sticky'' container is shifted to stay inside the block-end edge. +
inline-end +
+ The ''position/sticky'' container is shifted to stay inside the inline-end edge. +
+ +

+Scroll snapping: the '@container/snapped' feature

+ +
+		Name: snapped
+		For: @container
+		Value: x | y | block | inline
+		Type: discrete
+	
+ + The '@container/snapped' [=container feature=] queries whether a [=snap target=] + is snapped to its [=snap container=] in the given axis. It matches in the boolean + context if it is snapped in at least one of the directions. + +
+
x +
+ '@container/snapped' [=container feature=] matches ''x'' + if the container is a horizontal [=snap target=] for its [=scroll container=] +
y +
+ '@container/snapped' [=container feature=] matches ''y'' + if the container is a vertical [=snap target=] for its [=scroll container=] +
block +
+ '@container/snapped' [=container feature=] matches ''block'' + if the container is a [=snap target=] for its [=scroll container=] + in the block direction of the [=snap container=]. +
inline +
+ '@container/snapped' [=container feature=] matches ''inline'' + if the container is a [=snap target=] for its [=scroll container=] + in the inline direction of the [=snap container=]. +
+ +

+Sticky positioning: the '@container/overflowing' feature

+ +
+		Name: overflowing
+		For: @container
+		Value: top | right | bottom | left | block-start | inline-start | block-end | inline-end
+		Type: discrete
+	
+ + The '@container/overflowing' [=container feature=] queries whether a + [=scroll container=] has [=scrollable overflow=] in the given direction for the + current scroll progress. The logical values map to physical based on the direction + and writing-mode of the [=query container=]. + + In the boolean context, the query matches if there is scrollable overflow in any + direction. + +
+
top +
+ The [=scroll container=] has [=scrollable overflow=] past the top edge. +
right +
+ The [=scroll container=] has [=scrollable overflow=] past the right edge. +
bottom +
+ The [=scroll container=] has [=scrollable overflow=] past the bottom edge. +
left +
+ The [=scroll container=] has [=scrollable overflow=] past the left edge. +
block-start +
+ The [=scroll container=] has [=scrollable overflow=] past the block-start edge. +
inline-start +
+ The [=scroll container=] has [=scrollable overflow=] past the inline-start edge. +
block-end +
+ The [=scroll container=] has [=scrollable overflow=] past the block-end edge. +
inline-end +
+ The [=scroll container=] has [=scrollable overflow=] past the inline-end edge. +
+ +

Container Relative Lengths: the ''cqw'', ''cqh'', ''cqi'', ''cqb'', ''cqmin'', ''cqmax'' units

From 78f5cc881947faf26a0db28ea1166fcee7721e8b Mon Sep 17 00:00:00 2001 From: Rune Lillesveen Date: Mon, 2 Sep 2024 14:44:36 +0200 Subject: [PATCH 2/6] Fixed some review issues --- css-conditional-5/Overview.bs | 87 ++++++++++++++++++++++------------- 1 file changed, 56 insertions(+), 31 deletions(-) diff --git a/css-conditional-5/Overview.bs b/css-conditional-5/Overview.bs index 4c998a2b4fd..9685c832a65 100644 --- a/css-conditional-5/Overview.bs +++ b/css-conditional-5/Overview.bs @@ -470,7 +470,7 @@ Creating Query Containers: the 'container-type' property The 'container-type' property establishes the element as a query container for certain types of queries. For size - [=container queries=], that require certain types of containment, elements + [=container queries=], which require certain types of containment, elements are explicitly made [=query containers=] through this property. For other types of [=query containers=] any element can be a query container, such as for [=container style queries=]. @@ -542,18 +542,18 @@ Creating Query Containers: the 'container-type' property
- Containers can also expose state that depend on scroll offset. Here is an - example of how to style a descendant of a sticky positioned element when it - is stuck to the top edge: + Containers can also expose state that depends on scroll offset. This example + styles a descendant of a sticky positioned element when it is stuck to the + top edge:
 		#sticky {
-			container-type: scroll-state;
-			position: sticky;
+		  container-type: scroll-state;
+		  position: sticky;
 		}
 		@container scroll-state(stuck: top) {
 		  #sticky-child {
-				background-color: lime;
+		    background-color: lime;
 		  }
 		}
 		
@@ -1090,17 +1090,22 @@ Style Container Features

Scroll State Container Features

- A container scroll-state query allows to query a container for various - state that depends on scroll position. The ''scroll-state'' [=query container=] can either - be the scroller itself, or an element that is affected by the scroll position of an ancestor + A container scroll-state query allows querying a container for + state that depends on scroll position. It is a boolean combination of + individual scroll-state features (<>) + that each query a single feature of the [=query container=]. The syntax of a + <> is the same as for a [=media feature=]: + a feature name, a comparator, and a value. + + The ''scroll-state'' [=query container=] can either be the scroller itself, + or an element that is affected by the scroll position of an ancestor [=scroll container's=] [=scrollport=].

Updating Scroll State

-
- Note: This section is subject to change as a result of resolving - issue #10796 -
+ + Issue(10796): This section is subject to change as a result of resolving + the issue of unifying scroll-snapshotting layout state across several specifications. Scroll state may cause layout cycles since queried scroll state may cause style changes, which may lead to scroll state changes as a result of layout. The same issue exists for @@ -1128,6 +1133,22 @@ Sticky positioning: the '@container/stuck' feature ''position/sticky'' positioned container is visually shifted to stay inside the [=sticky view rectangle=] for the given edge. The logical edges map to physical based on the direction and writing-mode of the [=query container=]. + None of the values match if the [=query container=] is not [=sticky positioned=]. + + It is possible for two values from opposite axes to match at the same time, + but not for opposite edges along the same axis. + +
+ May match: +
+		  @container scroll-state((stuck: top) and (stuck: left)) { ... }
+		
+ + Will never match: +
+		  @container scroll-state((stuck: left) and (stuck: right)) { ... }
+		
+
In the boolean context, the query matches if visual shift is applied in any direction. @@ -1147,16 +1168,16 @@ Sticky positioning: the '@container/stuck' feature The ''position/sticky'' container is shifted to stay inside the left edge.
block-start
- The ''position/sticky'' container is shifted to stay inside the block-start edge. + The ''position/sticky'' container is shifted to stay inside the [=block-start=] edge.
inline-start
- The ''position/sticky'' container is shifted to stay inside the inline-start edge. + The ''position/sticky'' container is shifted to stay inside the [=inline-start=] edge.
block-end
- The ''position/sticky'' container is shifted to stay inside the block-end edge. + The ''position/sticky'' container is shifted to stay inside the [=block-end=] edge.
inline-end
- The ''position/sticky'' container is shifted to stay inside the inline-end edge. + The ''position/sticky'' container is shifted to stay inside the [=inline-end=] edge.

@@ -1177,25 +1198,25 @@ Scroll snapping: the '@container/snapped' feature

x
'@container/snapped' [=container feature=] matches ''x'' - if the container is a horizontal [=snap target=] for its [=scroll container=] + if the [=query container=] is a horizontal [=snap target=] for its [=scroll container=]
y
'@container/snapped' [=container feature=] matches ''y'' - if the container is a vertical [=snap target=] for its [=scroll container=] + if the [=query container=] is a vertical [=snap target=] for its [=scroll container=]
block
'@container/snapped' [=container feature=] matches ''block'' - if the container is a [=snap target=] for its [=scroll container=] + if the [=query container=] is a [=snap target=] for its [=scroll container=] in the block direction of the [=snap container=].
inline
'@container/snapped' [=container feature=] matches ''inline'' - if the container is a [=snap target=] for its [=scroll container=] + if the [=query container=] is a [=snap target=] for its [=scroll container=] in the inline direction of the [=snap container=].

-Sticky positioning: the '@container/overflowing' feature

+Overflowing: the '@container/overflowing' feature
 		Name: overflowing
@@ -1206,11 +1227,15 @@ Sticky positioning: the '@container/overflowing' feature
 
 	The '@container/overflowing' [=container feature=] queries whether a
 	[=scroll container=] has [=scrollable overflow=] in the given direction for the
-	current scroll progress. The logical values map to physical based on the direction
-	and writing-mode of the [=query container=].
+	current scroll progress. Content not reachable through user scrolling, like
+	content overflowing to the left of an ltr container, or a ''overflow/hidden''
+	container, does not make a query match.
 
-	In the boolean context, the query matches if there is scrollable overflow in any
-	direction.
+	The logical values map to physical based on the direction and writing-mode of
+	the [=query container=]. None of the values match if the container is not a
+	[=scroll container=].
+
+	In the boolean context, the query matches if any of the values match.
 
 	
top @@ -1227,16 +1252,16 @@ Sticky positioning: the '@container/overflowing' feature The [=scroll container=] has [=scrollable overflow=] past the left edge.
block-start
- The [=scroll container=] has [=scrollable overflow=] past the block-start edge. + The [=scroll container=] has [=scrollable overflow=] past the [=block-start=] edge.
inline-start
- The [=scroll container=] has [=scrollable overflow=] past the inline-start edge. + The [=scroll container=] has [=scrollable overflow=] past the [=inline-start=] edge.
block-end
- The [=scroll container=] has [=scrollable overflow=] past the block-end edge. + The [=scroll container=] has [=scrollable overflow=] past the [=block-end=] edge.
inline-end
- The [=scroll container=] has [=scrollable overflow=] past the inline-end edge. + The [=scroll container=] has [=scrollable overflow=] past the [=inline-end=] edge.
From c7bda8f4c49793404ba58660b88bc067f8a29616 Mon Sep 17 00:00:00 2001 From: Rune Lillesveen Date: Tue, 3 Sep 2024 11:52:57 +0200 Subject: [PATCH 3/6] Fixed issues in "Updating Scroll State" --- css-conditional-5/Overview.bs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/css-conditional-5/Overview.bs b/css-conditional-5/Overview.bs index 9685c832a65..05f93fe9856 100644 --- a/css-conditional-5/Overview.bs +++ b/css-conditional-5/Overview.bs @@ -1115,9 +1115,9 @@ Updating Scroll State current state once as the last step of [=run the scroll steps=]. Then, after the resizeObserver loop in the HTML event loop processing model, - the scroll state of every ''scroll-state'' [=query container=] is updated. + update the current state of every ''scroll-state'' [=query container=]. If that state has changed since the scroll state update in [=run the scroll steps=], - re-run the style and layout update if necessary. + re-run the style and layout update a single time if necessary.

Sticky positioning: the '@container/stuck' feature

From cb73856ad221351e9b79b8cfe2293fe5196a1d68 Mon Sep 17 00:00:00 2001 From: Rune Lillesveen Date: Tue, 3 Sep 2024 12:33:11 +0200 Subject: [PATCH 4/6] Move definition of "query container" --- css-conditional-5/Overview.bs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/css-conditional-5/Overview.bs b/css-conditional-5/Overview.bs index 05f93fe9856..1159d74c1a1 100644 --- a/css-conditional-5/Overview.bs +++ b/css-conditional-5/Overview.bs @@ -347,10 +347,10 @@ Container Queries [=container queries=] allow testing aspects of elements within the document (such as box dimensions or computed styles). - By default, all elements are [=query containers=] + By default, all elements are query containers for the purpose of [=container style queries=], and can be established as [=query containers=] - for [=container size queries=] by specifying + for [=container size queries=] and [=container scroll-state queries=] by specifying the additional query types using the 'container-type' property (or the 'container' [=shorthand=]). Style rules applying to a [=query container=]’s [=flat tree=] descendants @@ -384,9 +384,9 @@ Container Queries For the ''::part()'' and ''::slotted()'' pseudo-element selectors, - which represent real elements in the DOM tree, query containers can be + which represent real elements in the DOM tree, [=query containers=] can be established by [=flat tree=] ancestors of those elements. - For other pseudo-elements, query containers can be established by + For other pseudo-elements, [=query containers=] can be established by inclusive [=flat tree=] ancestors of their originating element.
@@ -469,11 +469,11 @@ Creating Query Containers: the 'container-type' property
The 'container-type' property establishes the element as a - query container for certain types of queries. For size + [=query container=] for certain types of queries. For size [=container queries=], which require certain types of containment, elements are explicitly made [=query containers=] through this property. For other - types of [=query containers=] any element can be a query container, such as - for [=container style queries=]. + types of [=query containers=] any element can be a [=query container=], such + as for [=container style queries=]. Values have the following meanings: From 9a66863cbfb26ebc7e5ac282082d04c004d1ee19 Mon Sep 17 00:00:00 2001 From: Rune Lillesveen Date: Tue, 3 Sep 2024 12:53:08 +0200 Subject: [PATCH 5/6] Talk about scroll-state features instead of scroll-state containers --- css-conditional-5/Overview.bs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/css-conditional-5/Overview.bs b/css-conditional-5/Overview.bs index 1159d74c1a1..b35766f956d 100644 --- a/css-conditional-5/Overview.bs +++ b/css-conditional-5/Overview.bs @@ -1092,14 +1092,15 @@ Scroll State Container Features A container scroll-state query allows querying a container for state that depends on scroll position. It is a boolean combination of - individual scroll-state features (<>) - that each query a single feature of the [=query container=]. The syntax of a - <> is the same as for a [=media feature=]: - a feature name, a comparator, and a value. + individual scroll-state features + (<>) that each query a single feature of the + [=query container=]. The syntax of a <> is the + same as for a [=media feature=]: a feature name, a comparator, and a value. - The ''scroll-state'' [=query container=] can either be the scroller itself, + [=Scroll-state features=] can either match state of the scroller itself, or an element that is affected by the scroll position of an ancestor - [=scroll container's=] [=scrollport=]. + [=scroll container's=] [=scrollport=]. An example of the former is the + ''overflowing'' feature, ''snapped'' the latter.

Updating Scroll State

From 1a924140ce9bee5ec0a38d62b276a5a49af0c26b Mon Sep 17 00:00:00 2001 From: Rune Lillesveen Date: Wed, 4 Sep 2024 19:30:35 +0200 Subject: [PATCH 6/6] Re-worded 'overflowing' --- css-conditional-5/Overview.bs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/css-conditional-5/Overview.bs b/css-conditional-5/Overview.bs index b35766f956d..0fe2d42a85d 100644 --- a/css-conditional-5/Overview.bs +++ b/css-conditional-5/Overview.bs @@ -1227,10 +1227,10 @@ Overflowing: the '@container/overflowing' feature The '@container/overflowing' [=container feature=] queries whether a - [=scroll container=] has [=scrollable overflow=] in the given direction for the - current scroll progress. Content not reachable through user scrolling, like - content overflowing to the left of an ltr container, or a ''overflow/hidden'' - container, does not make a query match. + [=scroll container=] has clipped [=scrollable overflow rectangle=] content + in the given direction which is reachable through user initiated scrolling. + That is, '@container/overflowing' does not match for a ''overflow/hidden'' + container, nor for a [=negative scrollable overflow region=]. The logical values map to physical based on the direction and writing-mode of the [=query container=]. None of the values match if the container is not a