[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
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

[css-overflow] Proposing scroll-start: allowing overflow scroll to not always start at 0 on 1st layout pass #6986

Open
argyleink opened this issue Jan 25, 2022 · 21 comments

Comments

@argyleink
Copy link
Contributor

📄 Explainer

Currently, JavaScript needs to show up at "never the right time" to adjust a scroll position to a specific starting place. This CSS property proposal seeks to remedy page load scroll jank by allow specifying a scroll-start position with a specific or by giving a child element scroll-start-target: auto at which point an absolute length is derived from the element and the scroll direction.


Example: set the start position to an absolute value:

:root { --nav-height: 100px }

.snap-scroll-y {
  scroll-start-y: var(--nav-height);
}

Example: set the start position to a snap child

.snap-scroll-inline {
  overflow-inline: scroll;
  scroll-snap-type: inline mandatory;
}

.snap-scroll-inline > #snap-start {
  scroll-start-target-inline: auto;
}

There's a lot more use cases and details (like interaction with fragment navigation) about the proposal in the Explainer

Would love to present the details of the explainer to the CSSWG for feedback 🙂

@scottkellum
Copy link

+1 this would make scroll up for search or scroll up for nav really easy. These are common patterns in some native apps, for example iOS uses scroll up for search in numerous places.

@jh3y
Copy link
Contributor
jh3y commented Jan 25, 2022

Hey 👋

In addition to scroll-start where I'm assuming this is short for scroll-start: <scroll-start-x> <scroll-start-y> 😎

Would be great to have keyword values like those used with the Flexbox API ✨

scroll-start: < center | auto | end | start | explicit value >;

ʕ´•ᴥ•`ʔ

argyleink added a commit to argyleink/ScrollSnapExplainers that referenced this issue Jan 25, 2022
@argyleink
Copy link
Contributor Author

Would be great to have keyword values like those used with the Flexbox API ✨

center, start and end added to the explainer! great suggestions thanks!

@MadeByMike
Copy link
MadeByMike commented Jan 26, 2022

If this is a < length > value I assume percentages would work and you might not need keywords. Although you could always have both.

I also assume if you set it to a value like 50vh this would scroll down to half the height of the viewport on initial render but not be relative going forward? Similar with percentage values? Or do they remain relative until the scroll is interacted with thus changing the scroll position?

@argyleink
Copy link
Contributor Author

If this is a < length > value I assume percentages would work and you might not need keywords. Although you could always have both.

they're already specced as <length-percentage> 🙂


Or do they remain relative until the scroll is interacted with thus changing the scroll position?

they're active until interacted with yep, at which point they not longer apply. from the explainer "Once the user has interacted with the scroll area, scroll-start and scroll-start-target has no effect. The styles must be present during scrollport creation or they have otherwise missed their timing."

@Sheraff
Copy link
Sheraff commented Jan 26, 2022

For people who use the keyboard to navigate : would this impact the starting position of the focus ? Otherwise the first time you press TAB you'll be taken back to the beginning of the scroll area.

@argyleink
Copy link
Contributor Author

For people who use the keyboard to navigate : would this impact the starting position of the focus ? Otherwise the first time you press TAB you'll be taken back to the beginning of the scroll area.

Excellent consideration. I imagine a developer using this new prop could/would have already been managing tabindex properly on their markup so tabbing into the (carousel, tabs, testimonials, etc) didnt scroll them to the beginning.

tab/focus management should pair closely with this prop. it'd be a good idea to mention that in the explainer/docs though thanks! 👍🏻

@SebastianZ
Copy link
Contributor

The idea itself is great! Thank you @argyleink for bringing that up!

I have some notes, though.

  1. scroll-start-target looks like a longhand for scroll-start.
  2. Talking of setting the scroll position with x and y coordinates, why not use a <position> value for that?
  3. What happens when multiple scroll container children have scroll-start-target defined?
  4. Both properties do the same thing, except one is set on the container, the other on a child?

To answer those questions here's my counter-proposal:

scroll-position = <position> || selector(<id-selector>)

Examples (rewritten from the initial comment):

:root { --nav-height: 100px; }

.snap-scroll-y {
  scroll-position: 0 var(--nav-height);
}
.snap-scroll-inline {
  overflow-inline: scroll;
  scroll-snap-type: inline mandatory;
  scroll-position: selector(#snap-start);
}

Explanation:

We concentrate the functionality in one property set on the scroll container. As this initial scroll position only has effect on scrollport creation before any user interaction, it means the DOM already needs to be fully available at that point in time. So setting it on the container with a selector for the child should be fine.
If the selector matches multiple elements, the first one in document order is considered to be the target.
Also, allowing to set both the selector and the position allows to define offsetting from that element.

Additional note: The property is independent from the things defined in CSS Scroll Snap 1. I.e. you can define a scroll position without having any scroll snap containers.


Having said all that, another approach could be to align with CSS Scroll Snap 1, and there specifically with scroll-padding-* and/or scroll-margin-*. You'd then just need to have a way to specify the initial scroll snap position.

Sebastian

@argyleink
Copy link
Contributor Author
  1. Talking of setting the scroll position with x and y coordinates, why not use a value for that?

i suppose this could get discussed more, but i do still feel like <length-percentage> works better here? curious what the tradeoffs are.

  1. What happens when multiple scroll container children have scroll-start-target defined?

Explainer says it's the 1st in the DOM order with the property. So competition is resolved by 1st node found. Matches your proposal too it looks like.

  1. Both properties do the same thing, except one is set on the container, the other on a child?

Like scroll-snap, some of the style is specified by the scrollview and some by a child. The original explainer used selector() but I agree with other CSSWG members that selector() circumvents CSS's natural selection mechanism. So, the workflow maps closer to scroll-snap, where a child needs to specify the property on itself. No new functions need required for the feature, which is nice.


Thanks for the thoughts! Hopefully my answers seem reasonable 🙂

@SebastianZ
Copy link
Contributor
  1. Talking of setting the scroll position with x and y coordinates, why not use a <position> value for that?

i suppose this could get discussed more, but i do still feel like <length-percentage> works better here? curious what the tradeoffs are.

The tradeoff using <position> is that it forces the order to be horizontal first then vertical, which might not be the order you'd expect in this case. Though it provides every functionality related to positioning and additional features to just <length-percentage> (which is included, btw.) like offsetting from the center, for example.

  1. What happens when multiple scroll container children have scroll-start-target defined?

Explainer says it's the 1st in the DOM order with the property. So competition is resolved by 1st node found. Matches your proposal too it looks like.

I don't see that in the explainer, though if it's meant to match my proposal, it's fine for me. 😄

  1. Both properties do the same thing, except one is set on the container, the other on a child?

Like scroll-snap, some of the style is specified by the scrollview and some by a child.

My point was that it's not strictly necessary to have two properties for that because the use cases can be covered by a single one. Though I don't have a strong opposition against adding two.

The original explainer used selector() but I agree with other CSSWG members that selector() circumvents CSS's natural selection mechanism. So, the workflow maps closer to scroll-snap, where a child needs to specify the property on itself. No new functions need required for the feature, which is nice.

Just for the record, selector() is not new. See the related issue #5811.

My first point still stands, though. The names should be chosen so that it's clear that they don't have a shorthand-longhand relation.

Sebastian

@tabatkins
Copy link
Member

To answer those questions here's my counter-proposal:

Using a selector() is a last-resort kind of thing, when there's no way to scope the element you're associating with. Requiring an ID is onerous, and still doesn't actually give you uniqueness; allowing more general selectors gives you the same non-uniqueness issues you're trying to avoid.

Here, it's straightforward - you want a descendant of the scroll container. Having the target self-select is a pretty straightforward and normal way to handle this.

When there's more than one element advertising itself as the target, you just take the first in tree order or something.

@fantasai
Copy link
Collaborator
fantasai commented Feb 3, 2022

Related problem: baseline alignment to arbitrary child #1339
Should use a consistent method model in both cases.

@chrishtr
Copy link
Contributor
chrishtr commented Feb 3, 2022

I think this is also related to #6225, which is about integration with history and navigations.

@smfr
Copy link
Contributor
smfr commented Feb 3, 2022

Two issues that the explainer should address:

  1. If the target element moves around (or the scroller gets resized), does the scroll position track it?
  2. If the target element is close to the bottom of the scrolled content, you need to apply constraining logic to avoid over-scrolling.

@smfr
Copy link
Contributor
smfr commented Feb 3, 2022

Also need to spec how this interacts with scroll anchoring: https://drafts.csswg.org/css-scroll-anchoring/

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-overflow] Proposing scroll-start: allowing overflow scroll to not always start at 0 on 1st layout pass, and agreed to the following:

  • RESOLVED: Accept the proposal for scroll-start and add to scroll snap L2
  • RESOLVED: Add argyle as a co-editor of scroll snap L2
The full IRC log of that discussion <dael> Topic: [css-overflow] Proposing scroll-start: allowing overflow scroll to not always start at 0 on 1st layout pass
<dael> github: https://github.com//issues/6986
<dael> argyle: Goal, we did survey asking devs about experience in scroll. Starting to write explainers
<dael> argyle: Scroll-start has overlap with frag navigation conceptually. But that is more magical b/c has more work to do and integrations to things like page refresh
<smfr> q+
<dael> argyle: This wants to bring a portion of that power to move someone. Example is tabs to swipe between and want tos tart on a midway tab so it laysout at that tab
<dael> argyle: All solutions to do this now take extra time and additional work
<dael> argyle: Tucking a search bar above the fold is common. Spotify does this where you don't see search until you scroll up
<dael> argyle: Proposal is advocating for way to do that in css using links as default. scroll-start 20px from top. Could also use %
<dael> argyle: This would only apply if frag nav isn't used and user hasn't interacted with scroll. Waiting for checks listed in explainer to make sure it's untouched
<dael> argyle: If so on first layout layout at this position
<dael> argyle: Adds scroll-start:target which is where you can have a child as a target for scroll. Akin to scroll-snap where you can snap on first layout. But after that you can't scroll anywhere
<dael> argyle: People hack around this by setting a keyframe. Have a snap child, wait a keyframe, and then remove an animation to remove the snap so you can move
<dael> argyle: Offering something simple for this
<dael> argyle: Property on the child where child in scroll container says snap to me if user hasn't ineracted with page
<Rossen_> q?
<dael> argyle: Additional details int he proposal, but how is that for a first pass?
<dael> smfr: I like proposal. Useful functionality. Like that it's element-based target
<dael> smfr: question is if we can define first layout pass. If page toggles display none and then goes back does that trigger first layout? If the layout tree is distroyed is that first layout?
<dael> argyle: Nice. I hadn't articulated the proper timing. Makes sense as a request for the explainer. Thank you
<Rossen_> ack smfr
<Rossen_> ack fantasai
<dael> fantasai: I think the idea of doing this makes a lot of sense.
<dael> fantasai: Being able to choose and element and have it be focus makes sense
<heycam> q+
<dael> fantasai: Agree with TabAtkins should be property on element not scroll container
<dael> fantasai: Have a similar problem with baseline alignment and alining to child. 1339 issue
<dael> fantasai: Should make them consistent in how they lookup
<dael> fantasai: Makes sense overall and I support working on it
<flackr> q+
<dael> argyle: So in favor of child property but don't agree with scroll port property of scroll-start?
<dael> fantasai: NOt sure if we need opt in for scroller, but scroller shouldn't pick position
<dael> argyle: Absolutely
<dael> fantasai: In terms of figuring out how element aligns in port we should reuse snap. I think alignment question is handled by scrollsnap. You do h ave question of first or last element that qualifies
<dael> fantasai: Another interaction, content alignment can effect initial scroll position. Have to figure out how that interacts
<dael> argyle: Thank you. Taken notes
<dael> argyle: In explainer does say if parent and child both have or multiple children have snap target it has how to resolve. Currenlty first in dom
<dael> fantasai: Good answer. First in dom makes a lot of sense. Want to think about if an element can block it's children from participating in that
<dael> argyle: Thank you
<Rossen_> ack heycam
<dael> heycam: Wondering what should happen when the page is loading slowly or content is added to scroll container and first layout happens before target offset is reached. Keep reevaluating to determine if we do a scroll until condition is met?
<dbaron> Not just lazy addition from script, but also a large page that does the first layout of the container when only some of its content has been loaded.
<dael> argyle: Current mentality is this is easy to interrupt. It's a request. In case of something being lazy I would assume...say it's scroll-start:left 200px. If it did first layout and nothing has happened I would say if a scroll-start shows up later it's ignored. Scroll position is set
<dael> heycam: A little worried authors would test on fast connection, get desired behavior, and then publish and scroll position isn't updated so looks weird
<Rossen_> q
<dael> fantasai: I think we should use logic for [missed] and this would be the feature for how that works
<Rossen_> ack fantasai
<Zakim> fantasai, you wanted to reply to that
<dbaron> The logic for scrolling to targeted elements isn't very good -- I really wanted to try to improve it about 15 years ago but never got around to it. :-/
<fantasai> s/[missed]/scrolling to :target/
<dael> TabAtkins: Going to say the same
<chrishtr> q+
<dael> argyle: And point of this is prevent janka nd be there first. Misses it's mark if containing scroll port has been created and there's no children.
<fantasai> s/feature for/feature defining/
<dael> argyle: I'm assuming Java is appending, but maybe even a really long document. I'm inclined to say trying to prevent jank and we want to do it all before a user sees it
<dael> iank_: It's possible that your html can slowly trickle in. I think that's heycam slow connection
<Rossen_> ack flackr
<dael> flackr: I wanted to reply to smfr and heycam points. In my mind see this as substitute for assumed 0 defaults scroll. So should reapply any time we would start at position 0. Probably not same as fragmenetion because that waits
<dael> flackr: For css any time something introduced with scroll it shouldn't jump
<dael> flackr: When an element becomes a scroll container we should look for this and jump to that unless doing other frag navigation
<Rossen_> q?
<fantasai> s/fragmenetion/fragment navigation/
<TabAtkins> +1 to flackr's points
<Rossen_> ack dbaron
<dael> dbaron: Related to flackr just said. My opinion is frag navigation is not very good. Had plan to fix around 2007 but kept postponing
<dael> dbaron: Thing that happened 3 or 4 years ago that had some improvements. I would have liked to see frag nav try to scroll to frag more frequently and have astate to say if it should keep trying
<dael> dbaron: Not what happened and might not be web compat
<dael> dbaron: Maybe doing same as frag nav is okay, but I think it's not that great and could be better for users
<Rossen_> ack chrishtr
<fantasai> +1 to not specifying an offset; should choose an element, and align it based on properties in css-scroll-snap
<dael> chrishtr: Had a thought similar to flackr and others. What if instead of spec some default offset you spec what scroll anchor target is at the beginning. And that remains until user does something. If page takes a while to load browser knows what target should be and as it loads browser centers it on screen
<dael> argyle: Sounds interesting. Sound like overlap with scroll start target. Maybe target should behave as desc
<dael> fantasai: I think if we go with idea that scroll-snap-target aligns the same as scroll snap properties. Scroll padding and margin and align can determine the position. I think that should handle the offsets. Not sure there's a use case for offset at start not being the same as if you were targetting to the element
<iank_> What about for a large <canvas> within a scroller where you don't have an element to target?
<dael> argyle: Hearing advocacy for scroll-start:target and no scroll-start b/c in general want to scroll an item into view
<dael> argyle: There are people that wanted keywords or end and center that are 100% or 50%
<Rossen_> q?
<dael> argyle: See what you're saying that length would be less used.
<TabAtkins> Agree we *sometimes* want an explicit offset.
<dael> argyle: Does seem like ti should be an offering. Like i know by hearder is 50 px and what to be under that
<smfr> maybe this should also cover the “always scrolled to the bottom“ case for chats etc
<dael> fantasai: You want to target the thing under the heading so that you can change the font on your heading and have it still work
<dael> fantasai: You can already to top of bottom with align content
<dael> TabAtkins: There's a use case in chat where you can't do offset. Large canvas in a scroller
<dael> fantasai: Can see that. Can also see if doing fancy with canvas you want to define snap areas. more to be done with canvas
<chrishtr> Re large canvas: there should be a way to specify a position relative to that element
<dael> argyle: Another is shopping where image takes up show screen. Start with that in center and then you can pan.
<Rossen_> q?
<dael> fantasai: I believe align content is defined to do that
<fantasai> https://www.w3.org/TR/css-align-3/#overflow-scroll-position
<dael> Rossen_: Seems like have positive consensus to adopt this proposal
<dael> Rossen_: Sure there are details to work out
<dael> Rossen_: I see smfr is adding some of those to the issue
<dael> Rossen_: Assuming you're looking for resolution to adopt?
<dael> argyle: Correct. Want way forward to prototype. Have demos and stuff for scrollsnap we're hoping to add scroll start
<dael> fantasai: rec to draft this as level 2. it's going to work closely with properties to determine position
<fantasai> s/level 2/scroll snap level 2/
<dael> Rossen_: Prop 1: Accept hte proposal for scroll-start and add to scroll snap L2
<dael> Rossen_: Objections?
<dael> fantasai: No obj but would like to hold off on adding the offset and start with the element. Should look at use cases
<dael> Rossen_: If we have this in its own draft much easier to tease out issues
<dael> Rossen_: Didn't hear objections
<dael> RESOLVED: Accept the proposal for scroll-start and add to scroll snap L2
<dael> Rossen_: Question 2, who will do this? Do we need to add argyle as an editor?
<dael> TabAtkins: Good idea if he's okay
<dael> argyle: Happy to join in
<dael> Rossen_: Obj to add argyle as a co-editor of scroll snap l2
<dael> RESOLVED: Add argyle as a co-editor of scroll snap L2
<dael> argyle: Plenty more to come

@johannesodland
Copy link

When there is multiple scroll-start-targets, can we let the author choose the selection mechanism?

Say that we had a property called scroll-start-target-selection with possible values ['safe' | 'unsafe']? && ['first' | 'last']. first and last would select the first or last start target in DOM order, while safe would make a start-target invalid if scrolling to that start-target would result in a previous start-target being partially or fully scrolled out of the scrollport.

One use case is a scroll snap image gallery.

If the container is narrow, you’d want to start at the snap position of the first image. But, if the scrollport have space enough, you’d want to start at the snap position of the second image so that more images are visible from the start.

.snap-scroll-inline {
  overflow-inline: scroll;
  scroll-snap-type: inline mandatory;
  scroll-start-target-selection: safe last;
}

.snap-scroll-inline > :nth-child(-n+2) {
  scroll-start-target-inline: auto;
}

@argyleink
Copy link
Contributor Author

cool use case, totally makes sense.

to me though, sounds like container queries are the perfect way to handle this:

.snap-scroll-inline {
  overflow-inline: scroll;
  scroll-snap-type: inline mandatory;
  container-type: inline-size;
  container-name: carousel;
}

@container carousel (width >= 720px) {
  .snap-scroll-inline > :nth-child(-n+2) {
    scroll-start-target-inline: auto;
  }
}

So mobile does no scroll start adjusting, aligns inline-start nicely with the rest of a column layout design. but if width goes above 720, then set the scroll start target as to "peek" and "hint" at the carousel breadth of content by setting the scroll start target to something other than the first item. thoughts on using container queries to manage a carousels available space and dynamic scroll start positioning?

@johannesodland
Copy link

to me though, sounds like container queries are the perfect way to handle this:

Container queries can definitively be used to handle this in many cases. However it requires that you know the approximate size and scroll snap alignment of the target elements, and possibly the scroll padding and margins as well.

Say that you have a user editable gallery, where users can add images of any aspect-ratio. The images are sized so that that have the same area, so that a 16:9 and a 9:16 image are the same “size”.

The only way to know if you can safely scroll to the second image is to calculate all the snap positions, and check them against the border-box of the first image to see if it is partly or fully scrolled out of the scroll container.

I quite like how ‘overflow-position’ in css-align-3 works, and would love a similar approach here: Try to align the initial scroll with the last scroll-start-target, but only if it can be done without obscuring any previous elements.

@cdoublev
Copy link
Collaborator

I agree with Sebastian that <position> can replace the current value definition:

[ auto | start | end | center | left | right | top | bottom | <length-percentage [0,∞]> ]{1,2}

<position> is more powerfull. But it would need to accept flow-relative keywords start and end (like in CSS Backgrounds 4, cf. #549).


I am a bit confused by start as the second value when it is omitted in the shorthand, and auto (not defined) as the initial value of the longhands.

CSS is not very consistent with default values in shorthands. Sometimes they resolve to a previous value (margin, border-color, etc), or the initial longhand value (animation, font, etc), or an arbitrary value (background-position, there does not seem to be many examples in this category).

The behavior of the first category feels more natural to me when specifying a logical shorthand value, ie. end would expand to end end instead of end start. This could also apply to scroll-start-target (the second value defaults to none).


I am not sure scroll-start and scroll-start-target can map to both their physical and flow-relative longhands. Which of the physical longhand should the first/second value map to?

All logical shorthands currently map to their physical longhands.

Related: #1282

flackr pushed a commit to flackr/csswg-drafts that referenced this issue May 12, 2023
This was marked as UD, however, all of the features have been discussed.
Previous resolutions:
scroll-start: w3c#6986 (comment)
:snapped, snapChanged and snapChanging and creation of css-scroll-snap-2 ED: w3c#6985 (comment)
svgeesus pushed a commit that referenced this issue May 12, 2023
This was marked as UD, however, all of the features have been discussed.
Previous resolutions:
scroll-start: #6986 (comment)
:snapped, snapChanged and snapChanging and creation of css-scroll-snap-2 ED: #6985 (comment)
@kizu
Copy link
Member
kizu commented Nov 17, 2023

Just in case: today I wrote a post where I shared a workaround for this that uses scroll-snap — https://blog.kizu.dev/snappy-scroll-start/

That workaround is not very convenient, mostly due to the interop issues with the current scroll-snap (I'll investigate them at a later time), but if someone needs scroll-start today — maybe this workaround would be useful to you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests