Open tabatkins opened 1 year ago
Proposed resolution is in the second-to-last paragraph of Tab's comment.
@dholbert does this sound good to you?
Yeah, this makes sense to me. Thanks!
I suggest that, when a self-alignment property is set to a non-
auto
value, we computeauto
insets in that axis to 0, rather than using the normal auto-resolution rules.
Nit: I see that https://github.com/w3c/csswg-drafts/pull/9136/commits/d1fdfa7c6e68962def777d6a6e5ce28882cefc1a proposes something a bit more subtle than computing auto
to 0 for that case - - over there, we only compute to 0 on one side, because we're trying to center over an arbitrary point.
As long as this doesn't end up contradictory, this all seems fine.
Also, RE "compute" - probably this would all be simpler to do in the used value rather than in the computed value?
Yeah, I think it's better for anchor-center to have slightly more subtle behavior (and I'd define the effect here to allow for that) but if we wanted to be completely consistent and always send both to 0 that would probably be okay.
For computed vs used, I don't believe I have an opinion either way. Using computed has the interesting effect that setting a transition on the insets, then changing from normal
to, say, center
alignment would cause it to gradually move out to its new position, but I don't think that's a very important use-case to worry about, so if used values would be simpler that'd probably be fine.
For the anchor-center
you need the more sutble behaviour to ensure that the available-size (for fit-content()
etc) works as expected. Otherwise you'll end up with an element that's too large potentially.
For the other align cases resolving to zero works well as you will consume all the free space when you apply alignment.
Ian
The CSS Working Group just discussed [css-align-3][css-position-3] Better interaction of auto insets and self-alignment properties?
, and agreed to the following:
RESOLVED: Define this new auto behavior for anchor-center. Bring this issue back with examples for the rest
FWIW, here's a testcase showing what non-default align-self/justify-self
currently does, for several children of a grid with auto
insets (top half) vs. 0
insets (bottom half):
https://jsfiddle.net/dholbert/7fze6aLt/
[edit from future-dholbert: yes, this testcase uses an element with class "flex" which is in fact a grid container. :) sorry for any confusion.]
I think Gecko, WebKit, Blink, and Blink-with-new-grid-staticpos-behavior all agree on the rendering of this testcase -- it looks like this:
The top half seems clearly more-usefully-centered to me. And, taken literally, the proposed change here seems like it'd make the top half behave like the bottom half (since it'd make the used values of auto
be 0
here), I think. I don't think that's the intent, so we need to be extra-clear about the expected outcome here. (E.g. there are various behaviors that are conditioned on whether or not the inset properties are auto
, and we need to be explicit about which of those behaviors would still happen when we have an auto
-that-we-are-treating-as-zero-due-to-this-proposal.)
FWIW, here's a testcase showing what non-default
align-self/justify-self
currently does, for several children of a grid
Here's a flex version, which notably needs to use justify-content: center
rather than justify-self
to align the abspos thing, since justify-content
is the way to center stuff in a flex container's main axis (even an abspos child). (Gecko/WebKit/Blink all respect justify-content: center
for centering the static position of an abspos flex child, and they all ignore justify-self: center
.)
So we might need to generalize the proposal so that non-default justify-content
on a flex container will also activate the new special behavior (whatever that behavior is) for its abspos children, I think? (so that that situation gets the same treatment as the analogous situation with non-default justify-self
on an abspos grid child).
Oops, forgot to paste the link -- here's the link to the flex version (using justify-content
as noted above):
https://jsfiddle.net/dholbert/03n9Lpwu/
<style>
.flex {
display: flex;
width: 200px;
height: 200px;
position: relative;
border: 1px solid black;
justify-content: center;
}
.flex > * {
align-self: center;
/* justify-self has no effect on flex items or on abspos flex children: */
/* justify-self: center; */
position: absolute;
}
.zero-insets > * {
left: 0;
right: 0;
top: 0;
bottom: 0;
}
.small-block {
height: 30px;
width: 30px;
border: 2px solid lime;
}
.paragraph {
border: 2px solid red;
}
img {
border: 2px solid cyan;
}
</style>
<div class="flex">
<div class="paragraph">
long string of text which will probably have to wrap
</div>
<img src="//placekitten.com/40/40">
<div class="small-block">
a
</div>
</div>
<div class="flex zero-insets">
<div class="paragraph">
long string of text which will probably have to wrap
</div>
<img src="//placekitten.com/40/40">
<div class="small-block">
a
</div>
</div>
The top half seems clearly more-usefully-centered to me. And, taken literally, the proposed change here seems like it'd make the top half behave like the bottom half (since it'd make the used values of auto be 0 here), I think. I don't think that's the intent, so we need to be extra-clear about the expected outcome here. (E.g. there are various behaviors that are conditioned on whether or not the inset properties are auto, and we need to be explicit about which of those behaviors would still happen when we have an auto-that-we-are-treating-as-zero-due-to-this-proposal.)
Right it'd actually render the bottom the same as the top for what we are proposing - the insets would be zero - but we'd still trigger shrink to fit behaviour as alignment != normal
.
Here is how it renders with my patch:
(there is an argument to be made that if alignment != normal
and the insets are specified to be zero it'd trigger the default behaviour if we run into compat issue).
Ian
Yeah, I'm confused by your assertion, @dholbert. Unless I'm blind (easily possible when reading my own text), there's absolutely nothing that would suggest "ignore the alignment keyword and just put yourself in the start/start corner" is the correct behavior for position: absolute; inset: 0; place-self: center;
. Blink doesn't honor the alignment properties in abspos right now, so you'll indeed get that behavior (since, for replaced and fixed-width non-replaced, we'll treat the right/bottom insets as auto), but that's incorrect per spec. You should be getting the "everything is centered" behavior, since they all share the same inset-modified containing block and all specify center self-alignment.
To clarify, all I was saying above was: if we were to literally apply the suggestion here to today's web engines as-they-currently-exist, i.e. just change them such that top/right/bottom/left auto
would be treated as 0
when self-alignment is happening in that axis, then we would end up rendering the top half of my jsfiddles like the bottom half. (Since the bottom half of each jsfiddle is just the top half with inset:0
added.) And that seems not-great.
It sounds like you & Ian are saying that that's because in fact nobody is rendering the bottom half of my jsfiddles correctly right now, though. And if we rendered the bottom-half correctly, then changing to make the top half match the bottom half would be a no-op in some cases (per Ian's screenshot above) and a net improvement in following-the-alignment other cases (which is what's motivating this proposal).
(Also: I'm curious about your thoughts regarding whether we need to expand this to cover justify-content
as well, since that's what gets used in-practice to center-align the static position (i.e. auto insets) for abspos flex children?)
It sounds like you & Ian are saying that that's because in fact nobody is rendering the bottom half of my jsfiddles correctly right now, though. [...]
Yes.
(Also: I'm curious about your thoughts regarding whether we need to expand this to cover justify-content as well, since that's what gets used in-practice to center-align the static position (i.e. auto insets) for abspos flex children?)
This case isn't relevant here, right? It applies when you're using the static position, which now would only be when you're not using a self-alignment property. So that would continue working as it currently does.
This case isn't relevant here, right? It applies when you're using the static position, which now would only be when you're not using a self-alignment property. So that would continue working as it currently does.
[Discussed this on IRC a bit, dumping notes here for clarity]
Yes, I agree it'd continue working; I just was wondering if we might want to opt this scenario into a version of the new special case, since this proposal is premised on detecting a signal from the author that they want to align this abspos thing, and for flex containers as-implemented in browsers right now, justify-content
is the only way to align their abspos children and hence is the way that authors might send such a signal.
But per IRC, it sounds like "no". The intent is that justify-self:center
would ultimately Just Work for position:absolute things in a flex container just like it does in a grid container; and it would be subject to the proposal here, and would work by impacting how the inset properties resolve. (Whereas justify-content
would continue to impact the static position.)
Ultimately, if content-alignment on a container had an effect on the absolute position of a box, I'd expect it to be the abspos's containing block element, not its parent element; that's roughly the self/content relationship we have in all other cases today. (And that would be weird, since that content-alignment value would also apply to the container's in-flow children; this is why we defined that content alignment has no effect on the absolute position of an element, and the *-items properties don't inherit into absposes either.) Today content-alignment only affects the static position of the abspos, and having that flow from the abspos's parent makes sense, since static position is pretending to still be laid out by the parent as normal.
(In a perfect world where anchor positioning existing earlier, we almost certainly wouldn't have static position for absposes at all; the static position is just a poor approximation of anchor positioning relative to a nearby element in the source.)
Upon further reflection, I'd like to revert the resolution in https://github.com/w3c/csswg-drafts/issues/9124#issuecomment-1663098232 : I don't think anchor-center
should have any special behavior different from any other alignment value here. However
auto
compute to zero in a single-auto
axis with non-normal
alignment makes sense; there's nothing particularly useful going on otherwise.auto
axis, if the abspos has an associated anchor, assigning an explicit anchor should obliterate the static position association, regardless of alignment value, and instead position the abspos relative to the anchor. (In the July 2023 Anchor Positioning draft model, this would compute its auto
insets to some kind of anchor()
value; in the July 2023 Anchor Exploration presentation model, this would compute its auto
insets to zero.)Proposed resolutions for the above would be:
inset
in an axis is auto
, and self-alignment in that axis is not normal
, the auto
inset computes to zero.inset
behavior for anchor-center
alone.inset
values in an axis are auto
, and the abspos is associated with an anchor box, its automatic position is relative to its anchor box, not its static position.(I'll tackle the generic (non-anchored) double-auto
case in a separate comment.)
Wrt the double-auto
case, there are two possible models:
normal
value for a self-alignment property obliterates the static position and lays out the box in its absolute-positioning containing block.I don't think the CSSWG should resolve on this question without thoroughly understanding what each of them mean, and I don't think most people understood these options very well last time around. Relevant questions to explore are:
Wrt Web-compatibility:
To the extent that there are authors applying the self-alignment properties to children of flex or grid containers, where the flex or grid container is not the abspos containing block, Proposal B would be less Web-compatible. (It's unclear how common this case is, but iirc this behavior has been implemented for nearly a decade.)
The question of utility is largely this:
Option A's main benefit is that you can align with regards to the static position, rather than only ever being start aligned. This same alignment can often be achieved using anchor positioning, but it requires setting up the relevant associations--and in the case of flow layout might be quite tricky.
Option B's main benefit (afaict) is that if you want to center an absolutely positioned box in the absolute-positioning containing block, you no longer need to specify inset: 0
in addition to place-self: center
.
The question of which is more understandable is more complicated.
Having non-normal
self-alignment break out of static positioning into the absolute-position containing block is a simple mental model.
But if we go back to what position: absolute
by itself does: in the basic case of block or inline layout, it simply takes the element out of flow while leaving it in place. Currently that means its start-aligned, because everything is start-aligned in flow layout.
However if justify-self
were implemented on blocks (which it should be), then authors might use justify-self: center
, for example, to center a block while also providing margins. And then one might expect position: absolute
to simply take it out of flow and leave it where it is. But if justify-self: center
also disables the auto-positioning of abspos boxes, then adding position: absolute
on such an element would both take it out of flow and center it in the abspos CB.
What's more expected? I'm not sure.
I think the CSSWG needs to think about the implications of each option, their utility, understandability, compatibility, and implementability, as well as considering their impact on CSS as a complete and integrated system
PROPOSED: If both inset values in an axis are auto, and the abspos is associated with an anchor box, its automatic position is relative to its anchor box, not its static position.
I don't understand what you could be meaning by this. The only way I can read it is that you want the "alignment container" to be the anchor element's box, but that's almost certainly not useful. What did you actually mean?
- Which has more utility for authors? (strictly speaking, A provides more functionality)
- Which is more understandable for authors?
- Which is more Web-compatible?
- Which easier to implement? (strictly speaking, B is simpler to implement)
In reverse order:
Option A's main benefit is that you can align with regards to the static position, rather than only ever being start aligned. This same alignment can often be achieved using anchor positioning, but it requires setting up the relevant associations--and in the case of flow layout might be quite tricky.
It only achieves this in a very strange way that I don't think is at all useful now that we have better options. The details are described in https://drafts.csswg.org/css-position/#staticpos-rect.
Most notably: for blocks, the "rectangle" is zero-height, and spans the width of the staticpos containing block. So align-self: start
puts you below the rectangle, while align-self: end
puts you above it. Similarly, for an inline the "rectangle" is zero-width, and the height of the line box, so justify-self: start
puts you after the rectangle (on the end side) while justify-self: end
puts you before it (on the start side).
Being capable of this sort of positioning is useful, sure. But achieving them via alignment into degenerate rectangles is counter-intuitive and confusing! Instead, relying on anchor positioning makes a lot more sense: declare which nearby element you're aligning to, and then just say which of your edges goes where. This is straightforward and meaningful, and substantially more powerful, since you can do more complex alignments than just what the alignment keywords offer.
(For flexbox and grid, the "staticpos rectangle" does not, in any meaningful way, correspond to "where the element would be if it weren't abspos". So we don't even have that justification there. It's just a weird box we defined in the hope that we could get some sort of useful behavior out of alignment, but it's not based on satisfying any particular use-case.)
Option B's main benefit (afaict) is that if you want to center an absolutely positioned box in the absolute-positioning containing block, you no longer need to specify inset: 0 in addition to place-self: center.
No, the main benefit is that the alignment properties do something reasonable and understandable (and still reasonably useful), instead of aligning into either a degenerate rectangle in counter-intuitive ways, or an arbitrary rectangle that doesn't appear anywhere else in CSS and isn't particularly useful.
Ultimately, the issue is that abspos, as defined by CSS2, is at least two (possibly three) different layout modes rolled into one. The "double auto" case, that we call static positioning, is precisely and exactly a weak, fragile form of anchor positioning. At the time of writing CSS2 we didn't have many of the concepts necessary for anchor positioning to work well, and it was a useful and frankly necessary ability, so it's fine that it was defined that way. But it was always a weak, hacky way of laying things out, that only barely worked; if you strayed even a little bit from what it offered you were SOL.
You and I tried to retrofit the alignment properties onto this model to get some more power out of it, and it technically worked, but it was still fragile and weak. Anchor positioning solves the entire use-case much, much more robustly, powerfully, and intuitively, so we just don't need to use the alignment properties for this anymore. Instead we can repurpose them to the much more straightforward and understandable purpose of "aligning inside the containing block", exactly like how they work in all the other layout modes. And for an abspos, the containing block is the abspos containing block. If you're not specifying anything more specific, the most useful default is to use the entire containing block, which is achieved by computing auto insets to 0.
We can't get rid of the "static position" concept; that's got two or three decades of compat supporting it. But we don't need to make it more complicated when we can spend those complexity resources in more effective ways. That's what this resolution is about.
Replying to: https://github.com/w3c/csswg-drafts/issues/9124#issuecomment-1679487628
Utility:
Using static positioning is very difficult to achieve for web-developers, as within flow-layout you need to place your abspos element in a very precise location within the DOM (and ensure it has the right display
as well). Practically speaking not many web-developers use it and we haven't heard much desire to add more capabilities here.
This same alignment can often be achieved using anchor positioning, but it requires setting up the relevant associations--and in the case of flow layout might be quite tricky.
I'd argue the opposite - using static-positioning in flow-layout is much more difficult as you need precise location within the DOM + correct display
, vs. anchor positioning where can just target the element you are interested in. The current defined behaviour in the spec (from investigations) doesn't have much real-world applicability wrt. use-cases, and capabilities it does provide can easily be covered by anchor positioning.
Compatibility:
It's really only implemented in 1.5 layout modes, and its very buggy in all engines. (Implemented in grid, and half implemented for flexbox as only supports align-self
).
Practically speaking not many web-developers use static-positioning, and on top of that don't place align-self
, justify-self
on the abspos. This wouldn't change the static-position being picked up from align-content
/ justify-content
.
We are happy to take the compatibility risk for the proposed model, and can report back to the group if we find any major issues.
Understandable:
After playing around with it quite a bit the proposed model is very natural, it does what I expect. The current model has hidden confusing behaviours like the potential for align-self
to work in both axes (column flexbox + top/bottom insets set) for example.
The only way I can read it is that you want the "alignment container" to be the anchor element's box, but that's almost certainly not useful. What did you actually mean?
That is exactly what I meant, and I think it is useful: you're immediately able to align yourself relative to the anchor box. (If you think about the current use cases of abspos, rather than focusing on pop-ups specifically, it'll be more obvious why this is useful.)
That is exactly what I meant, and I think it is useful: you're immediately able to align yourself relative to the anchor box. (If you think about the current use cases of abspos, rather than focusing on pop-ups specifically, it'll be more obvious why this is useful.)
Hm, I don't think I agree. For one, if that is the box that you want, you can get it with a one-liner inset: anchor(same);
already. So, we're not meaningfully saving authors any complexity.
In addition, having this behavior means authors need to track two totally different behaviors for what auto
does when using alignment props, depending on whether they have a single auto inset or two. Similarly, the alignment box could differ based on axis, which generally is also a little confusing. (That is, if you specified just top: 0;
it would be equivalent to top: 0; bottom: 0; left: anchor(left); right: anchor(right);
, which feels confusing/weird.)
I'd like to avoid this sort of mental complexity if it's not significantly helping authors, and given how trivial it is to invoke this behavior when it's desired, I don't think that's the case.
(I'll reiterate my general point - the double-auto "static position" behavior was nothing more than a simple, early, very weak/limited form of anchor positioning. It did something useful at the time when we didn't have the tools to do better, but it's no longer necessary, and we should instead opt for a simple, consistent model for the future, as much as compat allows.)
FWIW I see lots of absolute code with effectively the following:
.abspos {
--width: 40px;
width: var(--width);
left: calc(50% - var(--width) / 2);
}
To achieve centering.
The CSS Working Group just discussed [css-align-3][css-position-3] Better interaction of auto insets and self-alignment properties?
.
The CSS Working Group just discussed [css-align-3][css-position-3] Better interaction of auto insets and self-alignment properties?
.
Based on the discussion, I think I weakly favor the zero-ing behavior.
Small question about the
alignment on block are not easy to emulate with anchor positioning
Do I understand correct that this is the case when neither top&bottom or left&right (or their logical alternatives) are set, and when the absolutely positioned element gets its position in relation to its place in the content on that axis with missing inset property based on where it would've been if not absolutely positioned or something?
(not correct; you're always positioning relative to the prev/following element, and those can receive a name)
I think that won't work for the plain text cases, see this case: https://codepen.io/kizu/pen/ExOvBEo, as we don't have anything to add the anchor to (we could wrap things with spans, but not sure this is always viable)
My proposal: what if we'd have some kind of a keyword that could be used to anchor things to that hypothetical box position? I imagine the information about where it is should be always available for an absolutely positioned element, so maybe it could be used explicitly?
This would make it so we would unlock any resolution that removes an ability to reuse this behavior, as we could then use it explicitly.
I don't like the freaky action-at-a-distance aspect of @tabatkins' proposal, e.g. if this is in one stylesheet:
#foo {
top: auto;
}
and this is in some other stylesheet, or very far away in the same stylesheet:
#foo {
align-self: center;
}
it's very odd that the latter causes the former to mean something very different.
That's the case right now, tho. Depending on whether the first stylesheet says top: auto; bottom: auto;
or top: 0; bottom: 0;
, the current spec causes the second stylesheet's align-self
to resolve against totally different elements/rectangles, so the former is causing the latter to mean something very different. Heck, it's true even more directly - if the first stylesheet is expecting the auto
s to mean it's positioned "where it would normally be", then the alignment in the second stylesheet will change that assumption.
My suggestion, among other things, basically just swaps which property is "in control" here, so the non-default alignments always act the same (resolving against the abspos containing block, possibly with insets/outsets adjusting the rectangle a bit).
No matter what we do, tho, there's a significant amount of "action at a distance" between the insets and the alignment properties. They have to be treated as a unit for positioning purposes, in both current spec and my suggestion.
Could this change also be made for absolutely positioned elements with auto
margins? I believe they currently have a similar problem where you need to set the inset properties to 0 to make them work properly.
Currently, the self-alignment properties (justify-self, align-self) are defined to position the abspos within its inset-modified containing block.
Separately,
auto
insets have varied behavior:Neither of these behaviors work nicely with the self-alignment properties!
In either case, you can recover useful behavior by explicitly setting the inset to 0 (or another value, if something else is more appropriate). But that's not the initial value, so it requires extra work on the author's side (and more importantly, requires them to know that there is work to do in the first place!).
I suggest that, when a self-alignment property is set to a non-
auto
value, we computeauto
insets in that axis to 0, rather than using the normal auto-resolution rules. This will allow authors to use the self-alignment properties immediately, in what I believe is a more intuitive manner.(We can't change the default behavior of abspos for long-standing compat reasons, so when the self-alignment properties are their initial value,
auto
, we have to leave the existing auto-insets behavior alone.)