-
Notifications
You must be signed in to change notification settings - Fork 668
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-selectors] Proposal for allowing selectors that depend on layout (:stuck, :snap, :on-screen, etc) #5979
Comments
I don't think this restriction is enough. Being able to select the contents means that you can affect the size of the sticky element through auto sizes, and thus whether it's stuck or not. See demo, which uses JS to crudely polyfill This circularity may be more obvious for similar proposals like |
agree that demo makes sense, |
What about adding the additional constraint that |
Also, I guess that there should be a child or descendant combinator (or pseudo-element?) after And mixing with |
@Loirooriol interested in your thoughts regarding ways to make a child not snap given this proposal? if |
No idea, I know very little about snapping |
No, in fact the spec is pretty explicit about staying snapped to an element as the page mutates; only user action or unrecoverable mutations (like the snapped element being removed) should change the snap. |
The containment requirement seems promising: containment spec quote
Sounds like this proposal (adding pseudo classes that can only be a selector member) in combination with
Those conditions seem like they do remove the infinite potentials, complimenting each other. Creates an effect similar to how This would also unlock |
I wonder if instead of a pseudo-class with some syntax restrictions, this could work with container queries? See #5989 "What container features can be queried?". Maybe whether the container is a sticky element in a stuck state? If |
How about using JavaScript Events? myStickyElement.addEventListener('stuck', function () {
this.classList.add('is-stuck');
});
myStickyElement.addEventListener('unstuck', function () {
this.classList.remove('is-stuck');
}); This was discussed in #1660 (comment) back in 2017, and it's still open |
Later in that thread, @upsuper pointed out that events are working synchronously and would therefore slow down the layout. The suggestion back then was to use something asynchronous like an Observer. Anyway, the discussion here is about a pure CSS way to handle those use cases. And the proposal @argyleink and @Loirooriol came up with sounds very promising to me so far. Also, it is orthogonal to any solution that involves JavaScript, i.e. they don't exclude each other. Sebastian |
@SebastianZ I see. Thx for the simple explanation! |
Very clever idea, indeed this addresses most use cases! However, do note that this is based on the fact that compound selectors before the subject can never target the same element as the subject. This is true today, but may not be true in the future. If we start adding pseudo-classes that avoid circularity by depending on this fact, it could severely restrict the types of new combinators we can add. For example, there have been discussions about a preceding sibling combinator possibly being implementable (i.e. the opposite of Also, remember the reference combinator? Among other things, it allowed authors to target labels of elements, i.e. We may well decide that the value these pseudo-classes add outweighs the value of combinators that could go backwards. However, it should be a conscious decision to accept that tradeoff, and we should first explore variations of this proposal that don't force us into adding this restriction. |
To handle that case it could be defined that the layout depending selector excludes the element from being the subject. In other words, Sebastian |
@SebastianZ Excludes what element from being the subject? I like where this is going, but it's a little hand-wavy at the moment. |
The elements that are in a layout dependent state, meaning those that would be matched by the layout depending selector, i.e. Taking your example As UAs match from right to left, this would be:
So the layout depending selectors need to be remembered and the elements matching them have to be excluded in the last step. This last step is of course only needed in situations where the subject could be part of the compound selectors before it. As those are presumably special cases, it wouldn't be needed in most situations. Sebastian |
Talked with @argyleink, @una, and @flackr today about this again. While the cyclic-ness is somewhat problematic, all the attempts to get around it are doing more harm to the proposal than good, I believe. Plus, @scroll-timeline also has closely-related issues. Ultimately, it's tight cycles (within the layout engine) that are a strict no-go; we want to avoid cycles that are loose enough to span a painting frame, like what So, I think we can deal with this entire bag of scrolling-related pseudos and styling by saying, essentially, that scrolling is snapshotted at the start of styling, stuck-ness or snapped-ness (or progress on a scroll timeline) is determined at that time, and then selectors are matched and styling is applied. If you do something that causes a stuck/snapped element to become unstuck/snapped, it'll show up next frame, but not affect selector resolution this frame. (To avoid a "flash of unsnapped content" effect, we can probably specify that the first frame does a style to figure out what is possible to stick/snap, then does another style to resolve the pseudos appropriately.) At worst, then, you can produce a @flackr believes this is fine implementation-wise. Thoughts? |
I don't know, hover cycles are annoying but ultimately they can be worked-around by the user by moving their mouse somewhere else. Some of the cycles we're talking about here might not have any reasonable workaround / way for the user to see the content if the page is flickering. |
Yeah, that's right. But I think it's also relatively hard to trigger on accident for these cases, and I think attempting to avoid it entirely results in cures worse than the disease. |
I'm not sure I agree, most of the work I had to do to implement scroll anchoring in Firefox involved diagnosing and fixing similar issues, and they are definitely not hard to trigger, see all the heuristics the scroll anchoring spec has to avoid them. |
(That said, happy to be proven wrong of course :)) |
Quick note that I've started working on these features as part of container queries (see #5989). If this selector approach is viable, of course, it's certainly a less restrictive path for authors. I don't think there is any reason we need both, right? If we do create the selector, then the query would be redundant? I guess this is a "subscribe" comment, to keep an eye on this conversation before going too deep on the other. |
I'm def excited about the container queries approach to some of these selectors! I have though started ideating a spec for |
This isn't super graceful, but most users can't even select enough tracks that this'd be an issue in most cases. I guess keep an eye on w3c/csswg-drafts#5979 for a potentially more graceful solution.
I wanted to add the following link here from the Chrome Developers Blog as it did help me to current workaround this issue about the missing :stuck selector: https://developer.chrome.com/blog/sticky-headers/. In combination with CustomStateSet API which is sadly not yet supported by firefox it even could a |
Am I correct in my assumption that this is the style of syntax suggested if we go the container query route? .header {
position: sticky;
top: 0;
}
.header__content {
font-family: sans-serif;
padding: 0.6rem 1rem;
}
@container state(stuck) {
.header__content {
background-color: teal;
color: white;
}
} <header class="header">
<div class="header__content">Page Title</div>
</header>
<main>
...
</main> If so it feels like a good compromise and opens up the potential for lots of states to exist on a container (for better or worse). |
I wanted |
@lilles What is the status of your state queries explainer/proposal and prototype? Is that something we could try to get agreement on, and begin to specify? (I am querying the status of a state query) |
in Canary (with cli flag dt {
container-type: scroll-state;
position: sticky;
> header {
transition: box-shadow .5s ease;
@container scroll-state(stuck: top) {
box-shadow: 0 5px 5px #0003;
}
}
} |
It would be great to get these behind a UI flag, for author testing. |
Yes, we resolved to create css-contain-4 and add scroll-state(), so we can start. I've just been very busy with anchor pos the last months. My next step is to finish the 'snapped' prototype.
After the resolution in #6402 (comment) I moved the explainer to and renamed the function to scroll-state(). Reducing container-type to a single one (scroll-state) was discussed, but not resolved. But since I changed it in the prototype I should reflect that in the explainer.
Sticky queries are enabled if you enable chrome://flags/#enable-experimental-web-platform-features |
Spec and WG writings:
Relevant proposals/issues:
Background:
Proposals like
:snap
and:stuck
have been rejected because the layout could be changed on the matched element, thus undoing what was done, which then redoes the effect.. forever. This can be seen in:hover
effects that change layout and cause the mouse to go out and in of the element as fast as the browser can draw it all. Not good. Reasonable reason to reject the ideas.Proposal:
Layout dependent selectors can only be selector participants and never the subject.
The cycle is heavily mitigated or entirely eliminated by not allowing the layout relevant selector to target the element with the desired layout pseudo class. We provide the hook, but prevent the hook from changing itself. Thoughts?
Conclusion:
Instead of attempting some list of approved styles for layout dependent pseudo classes, 1 rule could help prevent tons of footguns while simultaneously unlocking many new effects. The catch is that developers will need an extra node in the component so the layout effect and the styles are separated (aka: the element being stuck isn't what has the box-shadow).
Update 1
as suggested by Oriol
pseudo-class
cannot be part of a selector with+
or~
pseudo-class
cannot be mixed with:has()
The text was updated successfully, but these errors were encountered: