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-position-3] Making a stickypos in a scroller also see the viewport edges #8286

Open
tabatkins opened this issue Jan 6, 2023 · 16 comments
Labels
css-position-3 Current Work

Comments

@tabatkins
Copy link
Member

Across multiple issues (and especially in #865), a persistent complaint from authors seems to be that if a stickypos element has a scrolling ancestor, it will no longer stick to the viewport edges.

For example, a table that wants sticky headers, but is wide and thus wants to be horizontally scrollable, can't be done - the headers only care about sticking in the table's own scroller, which doesn't even scroll in the vertical axis.

(See #865 (comment) from @valtlai for an example and demo.)

Even if we can't care about arbitrary ancestor scroll containers, the special case of caring about the outermost viewport seems high-value to address.

Can we consider doing this either always, or through a new keyword?

@vexx32
Copy link

vexx32 commented Jan 6, 2023

I feel like having a sticky-x and sticky-y distinction might potentially be helpful...

Feels like half the problem is that the sticky thing is too confused about what directions are meant to be scrolled by the element vs the viewport in many cases.

@tabatkins
Copy link
Member Author

Which axis an element is sticky in is already clear, from which axis it has non-auto insets in. (That is, a sticky element with only "top: 0" is clearly only vertically sticky.)

@bramus
Copy link
Contributor

bramus commented Jan 16, 2023

In #7475 (comment) I pondered about making position a longhand, allowing authors to also specify a reference thing to calculate the positioning against.

Maybe for position: sticky this could be:

  • position: sticky / ancestor (default)
  • position: sticky / viewport
  • position: sticky / sibling <integer>

For that third item in the list above a extra<integer> is needed to specify the level of stickiness. That way, one can stick elements against other stuck siblings, to form some sort of stack. See #2496 (comment) for earlier thoughts and more details on this. One major downside though is that this would be z-index struggles all over again.

Also, when you say viewport I think you mean the Layout Viewport but maybe – referring to @flackr’s description in #7475, option 5 – the Fixed Viewport (= Visual Viewport before zoom) would make more sense?

@flackr
Copy link
Contributor

flackr commented Jan 23, 2023

I think that giving the outermost viewport special abilities could be problematic for sites which use overflow scrollers for particular parts of their content (e.g. gmail doesn't have a scrollable root view) which would need to use frames I guess to establish viewport scrollers and get this behavior?

In my ideal world sticky would be able to offset based on the nearest scroller that has scrolls in the particular axis (e.g. top/bottom find the nearest overflow-y scroller). I recognize though that the situation we have though is overflow in one axis implies overflow in the other so it's not a simple change.

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-position-3] Making a stickypos in a scroller also see the viewport edges.

The full IRC log of that discussion <dael> TabAtkins: An issue for a good while is when people use stickypos by default it sticks to viewport. Very useful. Stays on screen
<dael> TabAtkins: If you have a scrollable ancestor this stops happening. Stays in scrollport but no longer sticks to viewport. IF scrollport larger than screen you can scroll sticky thing offscreen even though intended to be always visible
<flackr> q+
<dael> TabAtkins: Request is for some mech to make sure a stickypos can see viewport edges even when it's not the closest scroller. Could go further to allow arbitary scroll containers, but important things are nearest scrollport and viewport
<Rossen_> ack flackr
<dael> flackr: Commented on issue, think viewport is too special case. I don't know if we have ability to have overflow:clip in one postion. That oculd be workable to make this composable so you can observe scroller scrolling you and not the one clipping you
<dael> TabAtkins: I don't think that solves. IN a scroller that can be scrolled vertically. You have a big table and want head to be sticky. Box of table is partially offscreen but you want thead to be sticky. Genuinely scrollable access is that thing you want to pay attention past
<dael> flackr: Concern is if we special-case viewport there's still a lot of cases where doc can't use viewport
<dael> TabAtkins: Agree. Hits on issue viewport has magic we can't reproduce. Talked in the past about being able to mark something as root scroller. Perhaps it's a matter of we should do that and it interacts well here
<dael> flackr: That would help
<dael> TabAtkins: I think you were a part of those discussion in the past, but that was years ago
<dael> flackr: It certainly stalled.
<dael> fantasai: Other prop in the issue was make it sticky to nearest scroller in the relevant axis. If you have only horz scroll something sticky in vertical axis sticks to next scroller up
<dael> TabAtkins: I thought use case listed in root is that or by what flackr mentioned
<dael> flackr: I think they are same. They could ignore nearest scroller if it desn't impact in that axis
<dael> fantasai: I feel like it's more flexible. Would it make sense?
<Rossen_> t-1 min
<dael> flackr: Tricky bit is non-scrollable direction is treated as overflow:hidden
<dael> fantasai: So can't be a scroll container
<dael> flackr: Could make non-scroll overflow:clip so it's explicitly not scrollable
<dael> flackr: Haven't fully thought if that works
<dael> TabAtkins: I don't think anything wrong with that. Clipping in 1 axis is weird visual stuff, but not if scrollable in other axis
<dael> TabAtkins: That might be it
<dael> flackr: Might be good in general to say something is clipped in one axis
<dael> TabAtkins: I want to do investigation, but that's a plausable approach
<dael> Rossen_: TabAtkins you want to do that and bring it back?
<dael> TabAtkins: Sounds good
<dael> fantasai: I wasn't expecting a resolution, but good to have progress

@tabatkins
Copy link
Member Author

I want to investigate if we can use the existing 'clip' keyword for this (changing behavior) or if we need to add a new keyword that's like hidden-but-clip.

@jonjohnjohnson
Copy link

IMHO With the narrow (relative to current desired uses) initial intent of the sticky position scheme, I'm quite interested if we could expand the features being explored by houdini's layout-api &/or animation worklet to guide this native css features design?

See also #2496

@astearns astearns removed the Agenda+ label Mar 7, 2023
@flackr
Copy link
Contributor

flackr commented Mar 10, 2023

I'd very much like to see clip in non-scrolling axis treat the box as not a scroll container for that axis for sticky position, scroll timelines and other cases where we use the nearest ancestor scroll container.

@phoenixsnake
Copy link

Can we do something like

.sp{
   sticky-origin: "sticky-parent";
}

.sp .sc{
   sticky-parent: "sticky-parent"; /* viewport | "user-defined: sticky-origin" */
}

@mantissa7
Copy link

Can we do something like

.sp{
   sticky-origin: "sticky-parent";
}

.sp .sc{
   sticky-parent: "sticky-parent"; /* viewport | "user-defined: sticky-origin" */
}

I would rather have a solution that is more generic that doesn't limit to just position: sticky.

.ancestor {
    container-root: foo;
}

/* .ancestor > .foo > .bar > .baz > .nested-child */
.nested-child {
    container-ancestor: foo;
}

@RheingoldRiver
Copy link

For cases where the sticky axis and the overflow axis are different, I would like a "just works" approach, so that there is no burden of knowledge here, and it is automatically set to viewport.

Where the axis agrees, I like this solution, and the general keyword that can be reused in other cases.

@phoenixsnake
Copy link

This problem arose many years ago and as of today, there is still no native solution available to fix it.

I believe that prioritizing generalization and reusability can be achieved afterward. Like when replacing "grid-gap" with "gap".

When it comes to addressing the problem of sticky parents, I believe a simple solution should be considered.
Rather than changing the current behavior of "position: sticky".
We could define a new set of attributes that specifically target this issue.
This approach would also prevent any disruptions to websites that have already implemented "position: sticky".

I spent several hours searching for a way to make "position: sticky" work within an "[ overflow | overflow-x | overflow-y ] : [ auto | hidden | scroll ]" parent.
I want it to be stick to the viewport. Unfortunately, none of the solutions I found met my needs.

In my experience, when people are looking to accomplish a specific task, they will search for documentation or tutorials that can guide them. If a new CSS feature becomes available, someone is likely to write a tutorial on how to use it.

@st3v0
Copy link

st3v0 commented Nov 20, 2023

Here's a crazy random thought, what about creating a new prop which targets the scrolling container and tells it how to treat descendant sticky elements. For example;

Here's some old example boilerplate first so everyone is on the same page.

<div class="table-wrapper">
    <table>
        <tr><th>Foo</th><th>Bar</th></tr>
        <tr><td>sin</td><td>cos</td></tr>
        <tr><td>tan</td><td>baz</td></tr>
        ...
    </table>
</div>
.table-wrapper {
    overflow-x: auto;
}

th {
    position: sticky;
    top: 0;
}

My thought was if we create a new prop (name is example only) like sticky | sticky-x | sticky-y. We could then do something like;

.table-wrapper {
    overflow-x: auto;
    sticky-y: inherit | initial | unset | none | ignore
}

none -> this scrollable container will not allow child sticky elements to work.
ignore -> this scrollable container will be excluded from the "nearest scrolling ancestor" lookup. This would mean table row headers could stick to the viewport top:0 and column headers could stick to the scrollable container left:0

It's not as nice as targeted sticky behaviour, however this allows the default sticky behaviour to function as is, but allow developers the ability to declare how their scrollable containers affect descendant sticky elements.

@kizu
Copy link
Member

kizu commented Feb 23, 2024

Could this be handled by position-container from #9868? I think it should be fair game to allow choosing a different ancestor from the immediate containing block as the target for the sticky positioning.

Another related thing that could potentially cover this: #8905 — if we could extend the anchor positioning to the sticky positioning (and somehow make it possible to attach things to the specific scrollports from it, compared to the elements), then we could choose a different element and not the closest scrollpost for this.

@xiaochengh
Copy link
Contributor

A "just works" solution (that sticks to nearest scroller only in the relevant axis) will break existing usage.

Imagine a container component that has a max height, and will show a scrollbar in y axis if its content exceeds that height. So it may or may not scroll in the y axis. And the container has a sticky child with top set. It will be very weird if, eg there are several of this component in the page, and then when scrolling the page, some nodes stick to the viewport while some don't.

So a new syntax seems necessary, and +1 to position-container.

@RheingoldRiver
Copy link

A "just works" solution (that sticks to nearest scroller only in the relevant axis) will break existing usage.

CMIIW but I believe the only cases where this is true is when you have an element that is sticky to the top or bottom in an overflow-y container; or to the left or right in an overflow-x container. So in the case of opposite-axis position/overflow, "just works" should work. In the case of agreeing-axis position/overflow, position-container sounds good to me too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
css-position-3 Current Work
Projects
None yet
Development

No branches or pull requests