w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.5k stars 664 forks source link

[css-scroll-snap-2] How should competing scroll-start-targets be resolved? #10774

Open DavMila opened 2 months ago

DavMila commented 2 months ago

The scroll snap 2 spec says that when there are multiple elements that could be scroll-start-targets for a scroll container "user-agents should select the one which comes first in tree order".

Selecting the first element in tree-order seems like a natural way to resolve competition between multiple targets which would be scrolled to in one particular axis but is perhaps not as flexible as might be needed for the 2d case where an author wants to scroll to one item in one axis and another item in the other axis.

An alternative that might be more flexible would be to compute the intersection of the relevant scroll-start-targets in each axis (originally suggested by @flackr ). This could prove useful for the 2d case where an author has a table/grid and wishes to specify a row and a column as the scroll-start-target in the vertical and horizontal axes respectively (which was the main use case for having scroll-start-target set separate inline and block values, though that has since been removed from the spec). User agents could handle the case where none of the scroll-start-targets end up in the scrollport by giving preference to the scroll-start-target that is first in tree order or some other resolution mechanism or simply leave that for the author to avoid. Since the current scroll-start-target syntax (scroll-start-target: [auto|none]) takes only one value that should apply to both axes, this alternative raises the question of how to associate one scroll-start-target with a particular axis. I can think of 3 options for associating a scroll-start-target with an axis:

  1. Use the scroll-snap-align properties of the scroll-start-targets, first prioritizing the presence of scroll-snap-align and then tree order.
  2. Have scroll-start-target accept keywords like 'x', 'y', 'both', 'inline', 'block'.
  3. The previously mentioned 2-param syntax, making scroll-start-target a shorthand for scroll-start-target-{x,y,inline,block}.

Perhaps we could adopt the current spec as-is for the moment, i.e. pick the first-in-tree-order target and scroll to it in both axes, and leave more complicated syntax/logic for a future version of the spec?

flackr commented 2 months ago

I thought of a possible option that would support a lot of the 2d cases fairly simply. We could scroll to every specified item in order, then if you combine this with nearest behavior it will try to scroll to a position that includes all of the specified elements. Elements that are wider than the viewport (e.g. scrolling to a row in a table) won't move the horizontal position as its already fully in view and thus implicitly supports the scrolling to a specific row and column use case.

I also wonder if we should consider re-using scroll-snap-align for this. We already use scroll-margin and scroll-padding to affect scrolling behaviors beyond snapping, it seems reasonable that we may want a scroll-align which affects the alignment used for anchor scrolls or other scrolls even when not using scroll snapping.

argyleink commented 2 months ago

great idea to leverage snap-align, that way the scroll-start-target when scrolled to can be positioned optimally 👍🏻

flackr commented 2 months ago

Some use cases to consider where multiple scroll start targets might be specified for a single scrolling container could be:

  1. A long list where the author specifies all checked items to start scrolled into view (e.g. start scrolled to the items you have selected so you can quickly review them and unselect them if desired).
  2. A navigation map view where the start, end, and possibly intermediate stops on your journey are scroll start targets so that the map starts scrolled to the planned route.
  3. A table where a particular row and column start scrolled into view.
  4. A table where multiple arbitrary cells are requested to start scrolled into view (e.g. maybe they point to key values the viewer is expected to be interested in).
flackr commented 2 months ago

To address the use cases and resolve this issue as well as #8832, I propose the following:

  1. scroll-start-target does not have per-axis values, i.e. no change to the spec as it is currently written.
  2. When multiple elements are requested to be scroll start targets, determine the scroll position by scrolling to each one in reverse-dom order. Without point 3, this would result in scrolling to the first item. Alternately we could define a mechanism whereby we scroll to the first item, then only as far as necessary to bring the next item into view while keeping the previous in view.
  3. Define scroll-align, a generalized version of scroll-snap-align that works outside of snap containers and possibly in snap containers without establishing a snap point. The former is currently a MAY in the spec:

    If a page is navigated to a fragment that defines a target element (e.g. one that would be matched by :target, or the target of scrollIntoView()) ... the user agent must snap to one of that element’s snap positions if its nearest scroll container is a scroll snap container. The user agent may also do this even when the scroll container has scroll-snap-type: none.

    This property should also accept nearest values to allow specifying all of the ScrollLogicalPosition values of the scrollIntoView API. It may make sense for scroll snapping to support nearest as well to support making items that snap such that they're not partially in view but don't have a particular affinity for aligning only on one side of the scrollport.

The combination of nearest values in point 3 and attempting to scroll to multiple items in point 2 would provide a reasonable best-effort attempt to scroll all of the requested items into view while still ensuring that the first one (the current specified behavior) is in view at the end.

DavMila commented 2 months ago

This property should also accept nearest values to allow specifying all of the ScrollLogicalPosition values of the scrollIntoView API. It may make sense for scroll snapping to support nearest as well to support making items that snap such that they're not partially in view but don't have a particular affinity for aligning only on one side of the scrollport.

This proposal makes sense to me overall but I could use some help clarifying the concept of "items that snap such that they're not partially in view but don't have a particular affinity for aligning only on one side of the scrollport." Is this similar to scrollIntoView's concept of nearest where if the element is already partially in view, no scrolling happens?

flackr commented 2 months ago

I could use some help clarifying the concept of "items that snap such that they're not partially in view but don't have a particular affinity for aligning only on one side of the scrollport." Is this similar to scrollIntoView's concept of nearest where if the element is already partially in view, no scrolling happens?

Yes, that is exactly right. scroll-snap-align: nearest would try to snap the element to the start and end alignment so that the scroll does not rest in a position with the element partially clipped by the scrolling container.

DavMila commented 2 months ago

Yes, that is exactly right. scroll-snap-align: nearest would try to snap the element to the start and end alignment so that the scroll does not rest in a position with the element partially clipped by the scrolling container.

Okay thanks, I think your proposal would be a great way to solve this. Shall we agenda+ this issue?

css-meeting-bot commented 1 month ago

The CSS Working Group just discussed [css-scroll-snap-2] How should competing scroll-start-targets be resolved?, and agreed to the following:

The full IRC log of that discussion <chrishtr> DavidA: we have a property we're adding called scroll-start-target that indicates if an element within a scroll container, then the scroll should start with that element onscreen
<chrishtr> DavidA: question is what happens if there are multiple targets
<chrishtr> DavidA: propose to do it in reverse-DOM order,
<chrishtr> DavidA: this would result in the first one applied last and then be on screen
<chrishtr> DavidA: also, should only change the scroll position if you have to
<Rossen3> ack fantasai
<chrishtr> fantasai: several questions. first: why do we need to add a new property rather than re-using scroll-snap-align?
<chrishtr> flackr: setting the scroll alignment for items that don't necessarily become snaps
<chrishtr> flackr: scroll-snap-align would also force the developer to set scroll snap areas
<chrishtr> fantasai: in that case, should there be a relationship between the two properties?
<chrishtr> fantasai: might want to do both behaviors
<chrishtr> flackr: I agree the properties should be strongly linked, possibly with a shorthanding relationship
<chrishtr> fantasai: suggest thinking about this more on the issue
<chrishtr> fantasai: so that we can figure out how the two properties interact
<chrishtr> fantasai: there was a bunch of discussion about regular vs reverse-DOM order. where did we end up and why?
<chrishtr> flackr: currently, we expect that it scrolls to the first item in DOM order. We probably want that to still happen. That is why the proposal is to scroll to each item in sequence in reverse-DOM order.
<chrishtr> flackr: there is also the issue of nearest...
<chrishtr> fantasai: can you explain nearest?
<chrishtr> flackr: same as scroll into view
<chrishtr> fantasai: ?
<chrishtr> flackr: this is needed with you scroll multiple things into view and want to find a good position (?)
<chrishtr> fantasai: you scroll in reverse-DOM order...when you add the spec can you make it really clear that this is the end result of the algorithm?
<chrishtr> flackr: yes absolutely
<chrishtr> fantasai: otherwise it seems to make sense
<chrishtr> fantasai: have questions in general about the feature but not this issue
<chrishtr> flackr: seems we can resolve that the general thing is good?
<flackr> Proposed resolution: Add scroll-align property to specify scroll alignment without adding a snap area - details TBD
<chrishtr> fantasai: would like to see the interaction with snapping
<flackr> Proposed resolution 2: When scroll-start-target targets multiple elements, scroll to each in reverse DOM order with text to specify priority is the first item
<chrishtr> rossen: we'll postpone the first resolution until we have more details
<chrishtr> RESOLVED: When scroll-start-target targets multiple elements, scroll to each in reverse DOM order with text to specify priority is the first item
<chrishtr> flackr: third thing: having a nearest align value
<chrishtr> flackr: not sure if we can resolve without details for resolution #1
<chrishtr> rossen: maybe we should wait for more details
<chrishtr> fantasai: suggest a separate issue be filed about the nearest issue
<chrishtr> fantasai: agree we should do something in that direction, just need to figure out all the details