w3c / csswg-drafts

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

[css-overflow] Top/bottom banners scrolling in and out as the pages scrolls (was:proposal: scroll-index (priority)) #5670

Open jeysal opened 3 years ago

jeysal commented 3 years ago

Use case

Many pages consist of a full-width (and for example 64px height) header at the top and a full-width (and for example 100vh - 64px height) main content below it. Many of those pages want the header to scroll out above the top of the viewport whenever the user starts scrolling down on the page, and scroll back in below the top of the viewport whenever the user starts scrolling up on the page. As far as I know, this is today implemented using JavaScript.

It is already possible to structure e.g. a grid layout to be able to scroll out a header. The grid has 64px + 100vh height (y-overflowing the initial containing block), the header in its top row has 64px height, and the main content in its bottom row has 100vh height (and of course its content can y-overflow).

However, this is not sufficient to achieve the experience a user would expect. If all scrollbars are in the top position and the user starts scrolling down in the main content, first the main content will scroll all the way to the bottom, and only once it can scroll no more, the top of the grid with the header is scrolled out of the viewport. Likewise, if all scrollbars are in the bottom position and the user starts scrolling up in the main content, first the main content will scroll all the way to the top, and only once it can scroll no more, the top of the grid with the header is scrolled into the viewport.

There is another problem with the layout I described - there are two scrollbars, which does not look nice, but it seems this is already being worked on in css-scrollbars.

Proposal

Note: This proposal has been discussed by the CSSWG and classified as unlikely to be implemented. Therefore, if this use case is addressed to make it possible to implement this with pure CSS, it will likely be with a different solution.

I propose a scroll-index (resembling z-index in naming) or scroll-priority property that can be used to customize the order in which the user agent will try to scroll overflowing containers (whether using a scroll wheel or touch input) and takes precedence over the 'default priority' which is based on the structure of the elements. In particular, it can also be used to scroll a parent before its child when scrolling in the child.

This of course has interactions with things like overscroll-behavior, but none come to my mind that do not have an intuitive solution.

Notes

I suppose this suggestion fits best into the overflow module of all the existing modules, so I tagged it that way for now.

I apologize if this has been suggested before; I did not find relevant issues with multiple searches.

fromaline commented 3 years ago

@jeysal very interesting proposal to me 👍 Also, I'd like to point out, that with grid layout, the desired behavior couldn't be achieved if overscroll-behavior needs to be set to contain, for example, to control pull-to-refresh behavior.

jeysal commented 3 years ago

I suppose without a spec tag this is unlikely to get any traction, so I'll tag it as css-overflow which I think is most correct although it is of course always hard to tell for a new feature.

jeysal commented 3 years ago

@frivoal @fantasai tagging you (sorry if wrongly) because I believe you have been involved in css-overflow recently and I'd be interested in opinions on whether this solution could be a feasible, and the right solution for this very common use case.

css-meeting-bot commented 3 years ago

The CSS Working Group just discussed scroll-index.

The full IRC log of that discussion <emilio> topic: scroll-index
<emilio> github: https://github.com/w3c/csswg-drafts/issues/5670
<emilio> florian: not my issue but I wanted to give it some attention
<TabAtkins> q+
<emilio> ... The use case is something we see on lots of websites, with a top banner that disappears but reappears if you scroll up again
<emilio> ... This author is thinking of this in terms of nested scrollers
<emilio> ... where your top banner is in a scroller outside the inner one
<emilio> ... we always prioritize the inner scroller
<flackr> q+
<emilio> ... but the author was proposing to prioritize the outer scroller, and only when that is exhausted scroll the inner one
<emilio> ... no strong opinion, there could be some parallels with overscroll-behavior
<emilio> ... just wanted to bring some attention to the issue
<emilio> TabAtkins: I find the use case good to address because lots of sites do this badly
<emilio> ... but I think this approach is not the way to go
<Rossen_> ack TabAtkins
<emilio> ... first this doesn't do what lots of sites do which is having a minified banner
<emilio> ... second, it seems very easy to screw up a page if you only test on desktop
<emilio> ... the current behavior is safer for that
<emilio> ... So I think the problem is good, the solution is bad
<emilio> ... we probably want another way to do this
<Rossen_> ack flackr
<emilio> flackr: I was going to point pretty much the same
<emilio> ... you only want to do this scroll priority inversion for the topmost scroller
<emilio> ... if you have nested ones then scrolling the header is probably not what the user intended
<Rossen_> q?
<emilio> ... so it seems a bit of a problematic solution
<emilio> Rossen_: sounds like the use case is well recognized, but the proposed solution is not
<argyle> agree with robert and tab
<emilio> ... I suggest to go back to the issue to discuss the solution
<emilio> florian: so the issue was proposing the solution, you're proposing to repurpose it right?
<emilio> Rossen_: sure
<emilio> florian: sounds good
<emilio> Rossen_: yeah, it seems like we should explore a good solution for this problem
frivoal commented 3 years ago

As per the minutes above, the CSSWG has recognized that the use case was valid and something we wanted to address, but thinks that the proposed solution was unlikely to be the right answer (see the minutes for why). I've changed the title of the issue to focus on the use-case rather than the solution. Further discussion is welcome, and not limited to the specific proposal from the original comment.

flackr commented 3 years ago

FYI it's possible to do this sort of effect with a slightly stickier version of position: sticky (e.g. preserves its last sticky position unless pushed by sticky constraints): https://jsbin.com/jucurup/7/edit?html,css,js,output

We could consider some way of supporting a lazy sticky position / sticky position with hysteresis as one possible solution.

jeysal commented 3 years ago

I've changed the title of the issue to focus on the use-case rather than the solution.

:+1: I'll also edit the issue text to have a remark about this.

FYI it's possible to do this sort of effect with a slightly stickier version of position: sticky

Yeah position: sticky was one of the things I looked at trying to figure out whether this was possible to implement with current capabilities of pure CSS, but I came to the same conclusion as you apparently did, that it currently cannot be done with pure CSS :smile:

frivoal commented 3 years ago

Here's a very different approach, but related to the same problem: https://github.com/w3c/csswg-drafts/issues/6400

jonjohnjohnson commented 2 years ago

Just wanted to mention that though scroll-priority has so far been rejected for understandable reasons, it wouldn't have even made sense for the use case provided.

The use case would require more of a "scroll linked" relationship between the outer and inner scrollers instead of an inverse "scroll chaining" priority. In other words, while you scroll the inner you'd also the scroll the outer (and possibly re-target scrolling captured by the outer onto the inner scroller).

Example: https://jsbin.com/zefifex/edit (though this could be made cleaner if we get a houdini scroll-customization-api)

jeysal commented 2 years ago

The use case would require more of a "scroll linked" relationship between the outer and inner scrollers instead of an inverse "scroll chaining" priority. In other words, while you scroll the inner you'd also the scroll the outer (and possibly re-target scrolling captured by the outer onto the inner scroller).

Not sure I know enough about CSS internals and spec to follow correctly, but retargeting, if that means moving from scrolling the outer to the inner during a continuous scrolling motion without the user having to stop scrolling in between, I would agree that this is also a desirable behavior but seems to be a separate thing from the scroll priority.

Re scrolling the inner and the outer at the same time I don't see how this would be necessary. When you scroll the outer box of height 100vh + $topbarHeight px from the "topbar hidden" position where it goes out of the viewport by $topbarHeight px at the top to the "topbar visible" position where it goes out of the viewport by $topbarHeight px at the bottom (or scroll it back the other way around), then the inner box inside it moves as a whole, which automatically visually moves the content inside it without having to change the scroll position of the content in the inner box. I believe you may have misunderstood the outer-inner element structure in the original proposal; I maintain that the proposal would solve the topbar use case.

jonjohnjohnson commented 1 year ago

@jeysal AFAIK, no vendors guarantee continuous handoff of scrolling inputs between scrolling contexts, which I think is really what you'd need for the "inverted scroll priority" to give you the effect you want with peakaboo/hidey bars. See stuff in https://drafts.csswg.org/css-overscroll-1/ that relates to scroll boundaries and maybe you'll find headway there?

Example: Even as I scroll this textarea that I'm currently typing into, I *almost always have to let my scrolling input conclude after reaching a scroll boundary before I can start scrolling the ancestor (document element) scroller with a new scrolling input.

flackr commented 5 months ago

BTW here's an example where you use an inner scroller to show/hide a header: https://codepen.io/flackr/pen/WNWmKLw

Note that IMO it's not as good as https://github.com/w3c/csswg-drafts/issues/5670#issuecomment-887655710 as the inner scroller scrolls independently first before the main scroller but something to consider on the spectrum of ways that this problem can be solved.

bramus commented 1 week ago

Another approach/hack to solving this, relying on scroll-driven animations + style queries + a long transition-delay: https://brm.us/scroll-driven-header