-
Notifications
You must be signed in to change notification settings - Fork 672
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] Ability to set a positioned element's containing block to another element #5952
Comments
How does this relate to the top layer that |
@emilio This is not about allowing arbitrary elements to escape their containing block, that's what #5699 is about. it's about positioning elements relative to another element, so that popups can display relative to the element they are anchored to regardless of where that is, via common In fact, these containing blocks could often be smaller than the popup that is positioned relative to them. For example, take a look at this popup for editing an |
Well, yeah, I ask because being at the top layer doesn't prevent your position being anchored to another element I guess, and the use cases look similar in terms of whether you want stuff like overflow clips to apply or what not. Anyhow, so... some amount of this can already be done by abspos + hypothetical position (so <!doctype html>
<style>
.editor {
display: none;
position: absolute;
width: min-content;
border: 1px solid black;
background: white;
padding: .3em;
margin-left: -3em;
}
a:hover + .editor,
.editor:hover {
display: inline;
}
</style>
Doesn't really matter much where this text wraps, here's some <a href="#" title="Hover to edit URL">cool link.</a> <span class="editor">Url: <input type=text></span> Of course that's not great in lots of ways (it doesn't guarantee it ends up in the viewport, etc etc), but "choose an arbitrary containing block" also has this issue, doesn't it? |
@emilio This does work for some cases, but not all (or even most), and currently the complexity of the required solution explodes once you need to go past that. Also, this comes up a lot in web components, in which case the popup might be in Shadow DOM and the anchor element in light DOM, and thus they cannot be siblings. I should have elaborated when pinging you that I was primarily asking about which constraints would make this implementable, so I could propose a syntax with those in mind. @fantasai thinks it should be implementable with the constraints listed in the original post, but suggested I confirm with you and @dholbert. |
I edited the first post to add some rough thoughts on possible syntaxes. |
One possible constraint / concern here: the "search" here might need to be blocked from crossing boundaries of elements that form containing blocks for their fixed-position descendants (e.g. an element with These properties ( Similarly, it's worth thinking about how this would interact with / traverse |
(Though... is the intent here that the actual box-tree parenting of the positioned thing would be changed? i.e. does it just entirely "escape" any filters, transforms, |
Would it help if one box, foo, could declare itself a named anchor, foobar, at its live position relative to the viewport with its box-sizing edges and another box, bar, could reference these edges in its positioning properties? foo {
anchor: "foobar";
}
bar {
position: absolute;
top: bottom("foobar");
left: calc(left("foobar") - 10px);
} |
So I'll start out by saying there is a lot of complexity that the proposed (I believe that the popup is dismissed when something scrolls etc, which means that you don't need to synchronise this with composited scrolling). @tabatkins & I wrote up some thoughts about something very similar to what @Crissov has just described a few months ago. And while this doesn't solve what (@tabatkins also has a blog post from 2010? with very similar ideas). See here: Effectively it is the same as proposed by @Crissov above. The "restriction" that makes this work from our perspective is that the "anchor" needs to be contained within the same containing-block subtree. E.g. <div class="position: relative;"> <!-- containing block -->
<div style="position: absolute; top: top(foobar) 10px;"></div>
<div>
<div style="position: relative;">
<div style="anchor: foobar;"></div> <!-- this is ok -->
</div>
</div>
</div>
<div style="anchor: foobar2;"></div> <!-- can't reference this block --> The other restrictions are this needs to be "pre-scroll", "pre-transform" similar to how other things in abspos works today. (This is what makes it not immediately useful for the Restricting to an However with layering with ScrollTimeline etc, you could achieve some smooth linked effects (there may be a way to make this "magically happen" but its super complex to get into right now). |
Do they need to be blocked when going up (i.e. the positioned element is a descendant of an element that forms a containing block for their fixed-position descendant) or also going down, i.e. when the positioned element is outside that subtree, but wants to be positioned relative to an element within that subtree? Although, I seem to recall
Yup, #5699 is about exactly that.
Ideally, yes. Whether it needs to or we can still cover enough use cases without this is yet unknown. |
Could you elaborate on what you mean by "placing in the top layer"?
This is unclear. It's not mentioned in the explainer nor in the Open UI definition of "light dismiss".
Thank you for putting this up, that's a very interesting proposal. One issue I see with the anchor declaring itself as an anchor instead of the popup referring to it via a selector is when multiple libraries want to position popups relative to elements in the host page. They can set
Why would that make it not immediately useful for the
I think that could be workable, combined with a solution for visually escaping any clipping (discussed separately in #5699). |
If each box could be known by many anchor names, but each anchor at any time only refers to a single box, assigning an anchor would not need to overwrite existing assignments of an affected box. To make that clearer, this could be split into two properties: foo {
anchor-set: "bar", "baz";
anchor-clear: "quuz";
} |
This. In particular, top layer elements are hoisted to the top of the DOM and paint in their own stacking context, so all of the complexity of whatever filters, clips, scrolls, transforms and so on above the element that is placed in the top layer is avoided. There are a whole lot of complexities of those containing block, stacking contexts, effects, compositing and threaded scrolls/animations which would explode in complexity as a result if the element was not in the top layer. And even if it is in the top layer, getting scrolling and animations right relative to something not in the top layer is complex. For Here is an example: load this page in Chrome on ChromeOS, open the "pet" select element, and then scroll the page (doesn't repro on some other platforms, because complexity!). The popup will not remain anchored. This is a bug, but it's a tiny example of the kind of bug I want to avoid having to fix in all its gross generality if it's not truly needed for the UX pattern. (And as you observe, Chrome is already set up to try to dismiss the select on scroll.) |
@chrishtr I see, thank you for the detailed explanation. Essentially what we need here is to be able to specify an element to position relative to, I just thought having that element act as containing block would be easier to implement. If the target element needs to be a top layer element, that is somehow positioned relative to another arbitrary element, I think that addresses most use cases as well.
An HTML attribute might be a simpler solution to implement, but it violates separation of concerns, as well as the Extensible Web Manifesto (by adding new magic that cannot be specified in CSS), so I'm really hoping there is a set of constraints that would enable a CSS solution to this. |
If the anchoring state is ephemeral, which is what I am proposing, then separation of concerns is not violated. I view it as similar to how the Regarding the Extensible Web Manifesto: right now the developer doesn't have programmatic access to top layer functionality. Perhaps there are use cases for doing that, but before doing it we should have strong use cases, and not do it just because extensibility. The reason is that the top layer is an important way the browser can coordinate certain UX in order to guarantee visibility/accessibility to the user. |
Hey everyone, joining the party a little late here, not sure how I missed this thread until very recently. (cc @BoCupp-Microsoft @dandclark @ipopescu93, who I refer to here as "we") For the Original concepts we fleshed out created a net-new positioning scheme ( You'll notice it's very similar to what @bfgeek and @tabatkins shared. This direction was very much influenced by their ideas, so full props to them and their prior thinking in this space. :) We think it's important that the anchor element is specified via markup or script instead of referenced in CSS, as these identifiers could be dynamic and unknown to the author as they are writing their styles. Two items we haven't be able to solve yet with this direction are:
Sounds like there's a lot of interest in this topic, a few different ideas floating around, and some valid concerns about perf and such. What would best help the group explore anchored positioning on the web? Is this worthy of a breakout discussion? |
Hi everyone, just wanted to let you know that we've put together some early ideas on anchored positioning for browser-managed, top-layer elements in this explainer: https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/CSSAnchoredPositioning/explainer.md Both the primary proposal (using at-rules) and "Alternate A" (using a We've just presented this to the Open UI CG for early feedback today; planning to discuss at the next telecon (June 17) which next steps we want to take w/r/t incubating a solution for anchored positioning. This proposal errs on the side of author control, but there might be some web devs who'd rather exchange some control in trade for a simpler syntax. I expect we'll chat about flexibility vs magic in upcoming discussions, and figure out whether to a) refine these ideas, b) go down a simpler path, or c) come up with a system where control and simplicity can more easily co-exist. |
In my frontend editing CMS i have these problems with the top layer concept. I would like to be able to place UI elements above the dialog element. The Wisiwig editor is not accessible as I can't position it above it. I would therefore welcome a solution like the one described here.
|
My opinionWe need something like On the child:
But the most importend thing for me is handling the "edge" cases, and this gives me the idea for new pseudos that could be used in the following ways:
|
Ah, this issue can be closed at this point; the Anchor Positioning spec is handling this now. |
The need to anchor a positioned element based on another element's position comes up very frequently in things like menus, popups, tooltips etc, and the current solutions are rather messy, involving copious amounts of flimsy JS code to monitor the element's position, scrolling, resizes etc and adjust the popup accordingly.
There is even a recent proposal for a
<popup>
element whose main feature is that its position can be "anchored" to another element.In my opinion this is something that needs to be addressed in CSS, not by introducing new "magic" baked in to specific HTML elements.
Would it be feasible to set a positioned element's containing block to an arbitrary (positioned) element in the tree?
If not feasible in the general case, what constraints would make it feasible?
An obvious constraint is that the containing block cannot be a descendant of the element. What others are there?
We'd also need some way to prevent cycles, e.g. where A uses B as its containing block and B uses A as its containing block. A good way to deal with cycles is to prevent them altogether, by only allowing elements that come before the positioned element in source order to be used as its containing block. This also has the advantage of limiting reflows during incremental rendering. If this proves insufficient for covering the use cases, I suppose there's always cycle detection.
Also, does this make sense for other positions beyond
absolute
?Syntax ideas
Assuming this is indeed doable, with certain constraints, a few rough thoughts on syntax.
There are three orthogonal questions:
1. How to specify the element?
First, I think we will ultimately need a new
<element>
type in CSS, as features that require element references have come up before, and will come up again. We should not repeat mistakes likeelement()
, which defined its own ad hoc querying logic. However, we can for now create ad hoc microsyntax for this feature and port it to an<element>
type later when the syntax needs to be shared with another property (as we've done with other types before, e.g.<position>
). However, keeping that future direction in mind should influence the syntax, i.e. we should not design it in a way that would be incompatible with being included in other properties. For example, we should use functional notation(s) instead of including a bare selector which would be impossible to disambiguate from other values.The only (?) precedent here is
element()
, which only accepted a bare<id-selector>
. We would definitely need some way to refer to a single element by id, not only because this is easiest to implement, but also because it offers an escape hatch into JS when the rest of the syntax is not sufficient to express author intent. Perhaps a function likefirst(<compound-selector>)
would work for this.We'd also need syntax to refer to the default containing block, though that could just be
auto
.Ideally, we need some kind of relative syntax which would resolve to an element based on the currently matched context. Something based on relative selectors (rel: #5745) is probably one's first thought. However, if we decide to go with the constraint that containing blocks need to come before the positioned element in source order, this means we cannot use (complex) selectors here, as we need to go up the tree and selectors only go down.
I think as long as we can match arbitrary ancestors and arbitrary previous siblings of the current element or any of its ancestors, this covers most use cases.
How about a set of functions, such as
Combined with
first(<compound-selector>)
, these provide a lot of expressivity, with a pretty small vocabulary.2. How to set the containing block to that element?
If we decide that this only makes sense for
position: absolute
, then specifying the containing block could be an extra parameter afterabsolute
(e.g.absolute from <element>
), or even a functional notation:absolute(<element>)
.Otherwise, I think it makes sense as a separate property:
3. How to handle errors?
Once we have arbitrary element references for
<element>
, but elements specified inposition-reference
need to follow certain constraints, we need to decide what happens when valid elements are specified that do not match these constraints.I think the easiest route to go is probably IACVT, which would make
position-reference
the same as ifauto
was specified.Related: #5699 though it doesn't address anchoring, but mainly allowing an element to spill out of an
overflow: hidden
container. However, changing the containing block was discussed there as a possible solution as well.Also related: #5304
The text was updated successfully, but these errors were encountered: