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-align-3][css-position-3] Better interaction of auto insets and self-alignment properties? #9124

Open
tabatkins opened this issue Jul 28, 2023 · 30 comments
Labels
css-align-3 Current Work css-position-3 Current Work

Comments

@tabatkins
Copy link
Member

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:

  • If both insets are auto, the box doesn't use abspos alignment at all in that axis; instead it uses its static position, and that is specified to pay attention to the alignment properties.
  • If only one inset is auto, it automatically computes so that the element's width + both insets fill the abspos containing block exactly.

Neither of these behaviors work nicely with the self-alignment properties!

  • Aligning the static position means you align in whatever box we happened to define for the static positioning rectangle, which is generally pretty arbitrary. (For example, in Grid it's just the content box of the grid container.) It feels unintuitive that this isn't just the inset-modified containing block that the abspos would otherwise be aligning in.
  • Auto-computing the auto inset, on the other hand, just leaves the abspos with no free space to align it at all! It's completely useless!

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 compute auto 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.)

@chrishtr
Copy link
Contributor

chrishtr commented Aug 2, 2023

Proposed resolution is in the second-to-last paragraph of Tab's comment.

@chrishtr
Copy link
Contributor

chrishtr commented Aug 2, 2023

@dholbert does this sound good to you?

@dholbert
Copy link
Member

dholbert commented Aug 2, 2023

Yeah, this makes sense to me. Thanks!

@dholbert
Copy link
Member

dholbert commented Aug 2, 2023

I suggest that, when a self-alignment property is set to a non-auto value, we compute auto insets in that axis to 0, rather than using the normal auto-resolution rules.

Nit: I see that d1fdfa7 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?

@tabatkins
Copy link
Member Author

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.

@bfgeek
Copy link

bfgeek commented Aug 2, 2023

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

@css-meeting-bot
Copy link
Member

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
The full IRC log of that discussion <dael_> TabAtkins: This is a generalization of one detail on anchor-center. When defining it, I realized if we didn't change anything about auto insets it wouldn't work very well. To make anchor-center work you need to set left and right to 0.
<dael_> TabAtkins: This is b/c current behavior is designed around certain assumptions and lacks of functionality CSS 2 had but we've since filled in. If you have a single auto it tightly wraps you element to align in the obvious way. This type of behavior made sense in css 2.
<dael_> TabAtkins: We now have better. The behaviors with rough approximations no longer make sense. anchor-center spec that double auto insets resolves one to 0 and the other to the same distance in other direction so you get largest possible non-overflow
<dael_> TabAtkins: In the other case, makes sense to do something similar and set auto insets to 0. If there's not 0 then alignment doesn't do something useful. Right now double auto makes you'd align static pos not abspos.
<dael_> TabAtkins: In the presense of an affermative signal you want alignment, meaning you set an alignment to non-intial, we probably want to change
<dael_> TabAtkins: Pro: for any non-initial value for self alignment properties we resolve auto to 0. We keep current for default, can't change that. but if you say something like start-align it makes sense to give them an inset CB you can position into
<dael_> TabAtkins: For abspos elements if self-alignment property is non-default we resolve auto to 0. I want to leave possibility to do more subtle things. For example anchor-center is a little smarter, but similar in spirit
<dael_> TabAtkins: I don't believe anyboy impl yet. We are about to in chrome. If anyone does, hopefully reasonable change.
<dael_> TabAtkins: thoughts?
<astearns> ack fantasai
<iank_> q+
<dael_> fantasai: I'm not 100% sure that's compat with flexbox and grid which are spec to honor alignment properties for staticpos
<fantasai> https://www.w3.org/TR/css-position-3/#abspos-insets
<dael_> fantasai: There is behavior in positioning spec for these values where alignment is with respoect to static pos. I disagree nothing useful to be done without setting to 0
<dael_> TabAtkins: Compat isn't an issue b/c chrome at least doesn't support there. It's a change in spec, but not a change in actual behavior. not sure on other browsers
<dael_> iank_: Small change for up, but I don't think it would be web compat issue
<dael_> TabAtkins: I also believe for single non-auto case it's pretty straight forwardly bad if you're indicating you want to align
<dael_> fantasai: In grid or flexbox you'r aligning to the container that's your parent
<dael_> TabAtkins: Single auto, not double
<dael_> TabAtkins: Single i think this is clearly right
<dael_> fantasai: Oooh, okay.
<iank_> +1 that its a better behaviour for the general case.
<dael_> TabAtkins: In the double case, the new behavior aligns you in abspos CB. I believe that's better. anchor-center definitely wants it and I suspect better in the general case. Often it's similar.
<dael_> TabAtkins: The staticpos rect doesn't seem the most useful
<dael_> fantasai: In flexbox align-self to does and effect when you're staticpos. Same for grid.
<dael_> fantasai: Also you're losing functionality by doing that b/c then you can't align in staticpos which in grid/flexbox is your parent and in block it's left/right which has some uses. You want to be in static but align to end for example
<dael_> fantasai: I don't mind changing for single auto where other end is anchor b/c you're not doing static at that point. So saying treat the other side as 0 doesn't seem unreasonable. Id on't think we should change spec
<fantasai> https://www.software.hixie.ch/utilities/js/live-dom-viewer/?%3C!DOCTYPE%20html%3E%0A%3Cdiv%20style%3D%22display%3A%20grid%3B%20border%3A%20solid%3B%20width%3A%20100px%3B%20height%3A%20100px%22%3E%0A%20%20%3Cdiv%20style%3D%22position%3A%20absolute%3B%20border%3A%20solid%20orange%3B%20align-self%3A%20end%22%3E%3C%2Fdiv%3E%0A%3C%2Fdiv%3E%0A
<dael_> TabAtkins: Main counter is all static pos behavior is a weak approx of what you can do with anchor pos. Anything you can do with static you can do better with anchor. So preserving isn't that relevant
<dael_> astearns: iank_ has been on the queue for a bit
<astearns> ack iank_
<dael_> iank_: Generally, I don't think people rely on static pos often.
<dael_> iank_: With self alignment I don't think we'll have compat issue
<chrishtr> q+
<dael_> iank_: When I played with it seemed like an upgrade for authors
<dholbert> q+
<dael_> iank_: Getting to compat, staticpos was already weird b/c align-self would impact both axes. I think this is an upgrade for double-auto. We're prepared to take web compat hit to see if this is compat
<astearns> ack chrishtr
<dael_> chrishtr: iank_ question on flexbox/grid. Are tehre use cases there?
<dael_> iank_: Not paticularly. You have the case where your CB is the parent. When the abspos they make the parent relpos. When mostly using abspos no behavior change. I agree with TabAtkins anything needing staticpos can be done better with anchor
<astearns> ack dholbert
<chrishtr> q+
<dael_> dholbert: Better to separate repaced and non-replaced elements. Replaced elements have intrinsic width. Actively giving it left:0 right:0 will stretch and I don't think that's the intent
<dael_> iank_: Intent would be we don't stretch when we resolve to 0. We keep shrink to fit. We're changing used not computed value. So you look at computed when determining to stretch
<dael_> TabAtkins: We're not changing initial value behavior. Only if you set the self alignment property to specific
<dael_> dholbert: Default doesn't have something, you set align-self:center and then it stretched
<dael_> TabAtkins: Center doesn't trigger stretch
<dael_> dholbert: Even if it's top/bottom set to 0
<dael_> TabAtkins: I think so. It would be a spec bug if it does, I think
<astearns> ack chrishtr
<dael_> chrishtr: My understanding is this substantialy simplifies engine. Correct?
<dael_> dholbert: I think so from when I tried to impl the grid change. Simplier not to have to compute the auto offset if you don't have to
<dael_> iank_: It's a straight up simplification and matches what devs expect
<dael_> astearns: I wonder if we could resolve to define this for anchor-center for now and keep this open and have fantasai look at flex/grid use cases and come up with here is what we'll lose with examples?
<dael_> TabAtkins: We've already significantly chopped down staticpos behavior for flex and grid. If fantasai wants to spend time, happy to keep open
<dael_> iank_: Presense of anchor being there is an important caveat
<dael_> fantasai: Yeah, I think that you don't lose as much with anchor positioning. I think the areas I'm more concerned is block layout
<dael_> TabAtkins: Haven't thought about block yet so happy to discuss there.
<dael_> iank_: I don't think you lose anything with block b/c if you need to you make the static parent an anchor and target the edges you're interested in
<dael_> fantasai: How would you do that?
<dael_> TabAtkins: I think we should go into the details in issue
<fantasai> s/that/that automatically/
<dael_> astearns: Prop: add this new bevaior for anchor-center and leave the issue open for if this is extended to the rest of the non-initial values
<una> so will there be a new issue open for that?
<dael_> fantasai: I think for anchor-center this does make sense
<dael_> astearns: [reads una] I think we leave this one open since it's not an anchor-position specific issue
<dael_> dholbert: And anchor-center is a bit more magical than this proposal
<dael_> astearns: Is that okay una?
<dael_> una: Sounds good
<dholbert> This is the testcase that I was looking at for "what are the effects of the used value being auto vs 0", fwiw: https://jsfiddle.net/dholbert/15y3dqu2/
<dael_> iank_: Can we try and get back within the month?
<dael_> astearns: Prop: Define this new auto behavior for anchor-center. Bring this issue back with examples for the rest
<dael_> astearns: Obj?
<dael_> RESOLVED: Define this new auto behavior for anchor-center. Bring this issue back with examples for the rest

@dholbert
Copy link
Member

dholbert commented Aug 3, 2023

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:
image

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.)

@dholbert
Copy link
Member

dholbert commented Aug 3, 2023

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).

@dholbert
Copy link
Member

dholbert commented Aug 3, 2023

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>

@bfgeek
Copy link

bfgeek commented Aug 3, 2023

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:

Screenshot 2023-08-02 at 7 43 34 PM

(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

@tabatkins
Copy link
Member Author

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.

@dholbert
Copy link
Member

dholbert commented Aug 4, 2023

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?)

@tabatkins
Copy link
Member Author

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.

@dholbert
Copy link
Member

dholbert commented Aug 4, 2023

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.)

@tabatkins
Copy link
Member Author

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.)

@fantasai
Copy link
Collaborator

Upon further reflection, I'd like to revert the resolution in #9124 (comment) : I don't think anchor-center should have any special behavior different from any other alignment value here. However

  • I agree that making auto compute to zero in a single-auto axis with non-normal alignment makes sense; there's nothing particularly useful going on otherwise.
  • In a double-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:

  • PROPOSED: If only one inset in an axis is auto, and self-alignment in that axis is not normal, the auto inset computes to zero.
  • PROPOSED: Revert previous resolution, do not define special inset behavior for anchor-center alone.
  • 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'll tackle the generic (non-anchored) double-auto case in a separate comment.)

@fantasai
Copy link
Collaborator

fantasai commented Aug 15, 2023

Wrt the double-auto case, there are two possible models:

  • Option A: Currently-specced model, where we use an approximation of the “static position”, i.e. the position the abspos would have had if it were not absolutely positioned. In this model, the self-alignment properties influence the calculation of this “static position”.
  • Option B: Proposed model, where any non-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:

  • 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)

Wrt Web-compatibility:

  • Regardless of which option, the self-alignment properties will have an effect on non-auto-positioned abspos, where they currently have no effect. Hopefully authors aren't assigning these properties on absolutely positioned elements where implementing these would cause an undesired change in position.
  • However, Option B (proposed behavior) also changes cases where self-alignment properties currently have an effect, to have a dramatically different effect: for flex and grid children, these properties currently affect the calculation of the static position, aligning the abspos within its parent. Under the proposal, however, they cause the abspos to position relative to its absolute-positioning containing block (which defaults to the ICB, not the parent).

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

@tabatkins
Copy link
Member Author

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:

  • we're implementing B anyway (by which I mean, anchor positioning; whether we auto-compute the insets to 0 doesn't impact this). So A does not represent an alternative, but rather an addition, and it needs to justify itself.
  • for the most part, applying alignment properties to the static position doesn't work today. In the cases where it does (flex and maybe grid?) it's simplified to the point where the staticpos rectangle is just the flex/grid container, and aligning the abspos in that space instead is identical.
  • The original 2.1 notion of static position was more or less reasonable - "where it would have laid out if it weren't abspos". It was just a point. This newer notion of "static position rectangle" is something we defined a few years ago, and is quite different. It's a relatively arbitrary rectangle, constrained heavily by back compat in how it can be defined, relative to the nearest container element. It's no longer "where it would have laid out", and it's not clear that it maps to anything that authors are aware or or need. At the time we defined this we didn't have a lot of better options, but now we do, with anchor positioning.
  • A staticpos rectangle is only more options in the strict sense that the rectangle so defined is distinct from the containing block. Whether it's a useful rectangle is a completely separate question, and the "utility" of it depends heavily on that.

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.

@bfgeek
Copy link

bfgeek commented Aug 16, 2023

Replying to: #9124 (comment)

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.

@fantasai
Copy link
Collaborator

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.)

@tabatkins
Copy link
Member Author

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.)

@bfgeek
Copy link

bfgeek commented Aug 29, 2023

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.

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-align-3][css-position-3] Better interaction of auto insets and self-alignment properties?.

The full IRC log of that discussion <ntim> TabAtkins: before the spec existed there is no interaction between the 2, we would use the CSS 2.1 behavior
<ntim> TabAtkins: The remaining tension is what to do in the double auto case
<fantasai> My positions:
<fantasai> https://github.com//issues/9124
<fantasai> https://github.com//issues/9124#issuecomment-1679487628
<ntim> TabAtkins: my preference is to have double autos resolve the same way so they resolve to zeros when you have non-default alignments
<fantasai> https://github.com//issues/9124#issuecomment-1678174879
<ntim> TabAtkins: main reason for this, there is one alignment keyword where this is necessary, the anchor-center keyword needs the space to be able to align.
<ntim> TabAtkins: the larger thing behind my reasoning, static pos as defined in CSS 2.1 is just a poor version of anchor positioning
<ntim> TabAtkins: it wasn't great, but it did do the job
<ntim> TabAtkins: but we don't need this anymore, since we have anchor positniing
<ntim> TabAtkins: we don't need to do this anymore, unless we are constrained by compat
<astearns> q+ masonf
<astearns> ack fantasai
<iank_> q+
<ntim> fantasai: It would be useful to focus on the case where anchor positioning is happening, because we probably have agreement on that one
<fantasai> PROPOSED: If only one inset in an axis is auto, and self-alignment in that axis is not normal, then auto computes to zero
<ntim> astearns: lets try to resolve on it
<TabAtkins> ack masonf
<fantasai> TENTATIVELY RESOLVED: If only one inset in an axis is auto, and self-alignment in that axis is not normal, then auto computes to zero
<ntim> fantasai: my proposal is in the double auto case if there is a default anchor, set the alignment rect to be the anchor's element's bounds
<ntim> iank: there are a few cases where this breaks down:
<ntim> iank: let's say my left is based on one anchor, and my right on another based on another
<ntim> TabAtkins: having the behavior being significantly different between the single and double auto cases is confusing
<fantasai> fantasai: We already have two different modes in abspos: static positoining or not
<fantasai> ... all this is doing is saying, instead of anchoring to your hypothetical box in the staticpos case, you anchor to the explicit anchor from anchor-default
<fantasai> ... I think it's more consistent with how CSS already works
<ntim> TabAtkins: i don't want it to be based on whether there's a default anchor or not, I want it to be based on the alignment value
<ntim> fantasai: we already have these 2 modes
<ntim> fantasai: alignment doesn't change what you layout relative to
<ntim> TabAtkins: I think it would be nice to have a consistent alignment model,
<ntim> we no longer need the static positioning, it would be nice to have alignment use a single consistent model
<ntim> fantasai: you're proposing to change existing behavior on web pages
<ntim> TabAtkins: no I'm not
<ntim> TabAtkins: my argument is not that we should change based on some arbitrary prop, for the single auto case, we turn the auto into 0
<fantasai> i/fantasai:/fantasai: Just becaue you dislike staticpos, because inferior to anchorpos, doesn't mean we should use the opportunity of any non-default property value to switch us out of it/
<ntim> TabAtkins: we should be consistent in the double auto case and resolve both to auto
<fantasai> s/auto/0
<TabAtkins> and since the staticpos behavior is subsumed by anchor pos, we don't actually lose anything by dropping it in this case, so we can ahve a better, more ocnsistent behavior by having autos always become 0 when alignment is happening
<ntim> TabAtkins: re: changing existing behavior, no one implements alignment properties on abspos
<ntim> fantasai: they do impl alignment properties for the static pos of the abspos
<ntim> iank: not really
<ntim> iank: it's implemented in grid / flexbox
<ntim> iank: but flexbox only implements in one axis
<astearns> q?
<ntim> iank: we're willing to take that compat risk and take that back to the group
<astearns> ack iank_
<ntim> iank: the proposed behavior is super useful
<ntim> iank: today if you need to center somnething in the containing block
<ntim> iank: there's about 2-3 ways people use, they are very clunky
<ntim> iank: you need to set multiple props
<ntim> iank: one way is setting insets to 0
<ntim> iank: the other way is using calc
<fantasai> iank: They're all super clunky
<ntim> iank: being able to set place-content: center and get centered alignment is very neat
<fantasai> scribe+
<fantasai> fantasai: I'm not arguing that being able to use alignment props in abspos is bad. I specced it out, obviously I think we should have it
<fantasai> fantasai: The hacks iank mentions are indeed all terrible and should be replaced with alignment
<fantasai> fantasai: that doesn't mean that alignment should change the behavior in auto-auto case
<fantasai> fantasai: just means you set 'inset: 0' to switch to useing the abspos containing block
<miriam> q+
<fantasai> fantasai: I don't think this his hard or confusing
<emeyer> scribe+
<iank_> q+
<xiaochengh> q+
<miriam> ack fantasai
<Zakim> fantasai, you wanted to say that adding 'inset: 0' is not that much to add
<emeyer> miriam: inset 0 isn’t a lot to add, but I as an author have never found abspos particularly useful
<emeyer> …I’m wondering what we gain by maintaining it
<dbaron> s/abspos/static positioning of abspos/
<emeyer> …What is the use case where I want to maintain it while adding alignment?
<emeyer> fantasai: Two things about adding alignment to static pos
<emeyer> …Static pos is like if you have an element in the document and then flip on abspos
<emeyer> …If you apply alignment properties in block mode (for example), then it will jump to somewhere else
<emeyer> …What I’m arguing for is, when you define an anchor, then static positioning becomes relative to the anchor bow
<emeyer> …That gets you the ability to be centered inside another box very easily
<emeyer> …I think for the non-anchor case, it’s about consistency with the way static positioning works
<emeyer> miriam: I understood you were proposing that when you switch to abspos, that would change your alignment, but in a different way
<emeyer> fantasai: If you want to be anchored to an element, I think it makes sense your automatic position moves to that element
<emeyer> …I think alignment changing your containing block to change is confusing
<emeyer> …Right now, alighment shifts you within the container to which you’re sizing
<emeyer> …anchor-default does move you; that’s its intention
<emeyer> …Why not have it do something as soon as it’s set?
<emeyer> TabAtkins: I think your argument is that current behavior is useful, and I want to argue with that
<emeyer> …We know that flexbox rules aren’t very useful; we watered them down on purpose
<emeyer> …In the one case that isn’t well implemented yet, the inline-block case, aligning goes into degenerate rectangles
<emeyer> …It would do something, but usually the opposite of what you expect it to do
<emeyer> …align: start would put you more end-ward and align: end would put you more start-word, in certain cases
<emeyer> …I think we were okay with that confusion when we defined the behvavior because we didn’t have anything better
<emeyer> …Now that we have better, we don’t have to offer authors this confusing behavior
<emeyer> fantasai: I think it’s great to offer a better way to do this; I’m not convinced this is the way to do it
<emeyer> iank_: I don’t think there’s been strong use cases presented for the double-auto case
<emeyer> …The behavior being proposed does solve developer pain quite well
<emeyer> …It does something very useful for center, stretch, a bunch of other things
<emeyer> …This is outside of anchor positioning
<vmpstr> q?
<emeyer> TabAtkins: I’m not sure how to resolve this, Elika
<iank_> ack iank_
<miriam> q+
<emeyer> …My argument is that the default behavior wasn’t useful, perhaps when it was defined, but not now
<emeyer> xiaochengh: For center, resolving double-auto to zero seems more useful
<emeyer> …THe inset modified containing block isn’t just used for alignm,ent, but also for position fallback
<ntim> q+
<emeyer> …The only containing block to test is the original containing block
<emeyer> …For alignment, we could use a different containing block than for fallback, but I’d like to avoid that
<astearns> ack xiaochengh
<astearns> ack miriam
<emeyer> miriam: Already we have a behavior where apply insets changes your reference, you’re no longer using the static position box
<emeyer> …To me, changing the alignment is exactly the same concept, so in my mind it aligns better with current behavior
<emeyer> …Alignment is an auto-inset sort of thing, so if one changes, why not the other?
<astearns> ack ntim
<vmpstr> q+
<astearns> ack vmpstr
<emeyer> vmpstr: Is there a web compatibility risk with this resolution?
<emeyer> TabAtkins: For some layout modes, we do implement the double-auto case
<emeyer> …We’re willing to take the compat risk
<emeyer> iank_: We’re also willing to take the risk
<emeyer> …We don’t think there will be much
<emeyer> astearns: Is there anyone who wants to show solidarity for Elika’s position?
<emeyer> emilio: Elika’s position is safest
<emeyer> iank_: If we show it’s web compatible, would that change your mind?
<emeyer> emilio: I don’t feel too strongly about preserving the current behavior
<emeyer> fantasai: Part of my point is you can get the behavior Tab and Ian want by setting inset to 0
<emeyer> astearns: Having a property that does nothing until you set another property isn’t good design
<emeyer> emilio: That’s already positioning, right?
<emeyer> iank_: I think our argument is that what we want is more useful and more what devs expect
<fantasai> astearns, that's a good argument for anchor-default having an effect without setting 'inset: anchor(..)'!
<emeyer> emilio: How is it more useful if the behavior Ian and Tab want can be achieved
<emeyer> …The current behavior cannot
<astearns> fantasai: I am OK with needing two properties when two things need to be connected
<fantasai> s/fantasai:/fantasai,/
<emeyer> TabAtkins: Current behavior can be achieved via anchor position
<fantasai> astearns, they are connected already, why not have an effect
<emeyer> iank_: I think also, there hasn’t been strong use cases for the existing behavior
<noamr_> q+
<emeyer> TabAtkins: Curernt staticpos behavior is in many cases unintuitive and inconsistent between layout modes
<emeyer> …Given it was mostly defined to give you certain powers that anchor positioning can give in better ways, we think authors are better served by having the old behavior be gone
<emeyer> emilio: I still th ink Elika’s position is safer but I wouldn’t object to removing it if you can get away with it
<astearns> ack noamr_
<fantasai> I'm not convinced you can do the same as staticpos for block and inline layout, at least not easily or without injecting additional things into the DOM
<emeyer> noamr_: If you want to achieve this non-useful behavior, you’d have to name everything
<emeyer> TabAtkins: You’d have to write a name, but it could be a locally s coped name (according to another proposal)
<emeyer> astearns: Does that meet your concern, Elika?
<emeyer> fantasai: Replicating for grid and flexbox is not too hard
<emeyer> …You have to assign a name to the contain and then tell the item to anchor itself to that
<emeyer> …For block and inline, you can’t just slap a name on a containing block
<emeyer> …You have to reference where the item would have been
<emeyer> …You’d have to add an empty element to the DOM to act as a placeholder
<TabAtkins> (not correct; you're always positioning relative to the prev/following element, and those can receive a name)
<emeyer> astearns: We’re out of time; not hearing consensus, but
<emeyer> …can we take a resolution?
<emeyer> fantasai: I think I’d object unless everyone else in the WG disagrees
<emeyer> TabAtkins: Thank you all for coming!
<emeyer> topic-
<fantasai> Tab, that doesn't work for "here's some text <span class=abspos></span> more text"

@astearns astearns moved this from Thursday Afternoon to Thursday Morning in TPAC 2023 agenda Sep 13, 2023
@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-align-3][css-position-3] Better interaction of auto insets and self-alignment properties?.

The full IRC log of that discussion <emilio> miriam: this was discussed yesterday in the anchor-positioning breakout session
<emilio> ... when both insets on an axis are auto we currently use the static positioning box
<emilio> ... roughly where the element would've been
<emilio> ... question is: when you change alignment via justify-/align-/place-self, should we zero those out?
<emilio> ... or is it weird to change it based on alignment
<emilio> ... the question is (1) is it web compatible to make this change, and (2) if it is, is there anyone who agrees with fantasai that we still shouldn't do it
<emilio> florian: the implications take a while to process
<emilio> miriam: when you set alignment should that refer to the static position box or the otherwise positioning context
<TabAtkins> And to be clear, for compat: currently, impls only respect the current spec (aligning in the staticpos rect) for a flex/grid parent. Other parent display types, it's not implemented yet, so doing *either* behavior is *potentially* a compat problem.
<emilio> emilio: not sure if we can get to resolve this in 9 minutes
<astearns> ack fantasai
<Zakim> fantasai, you wanted to explain this
<emilio> fantasai: [whiteboard time]
<emilio> fantasai: you got the abspos cb, the regular cb, and then the child you position
<emilio> ... right now with insets auto you stay within the regular cb
<emilio> ... if you set insets to 0 then we don't care where this box lives, we lay it out inside the abspos cb and ignore everything else
<emilio> ... question is when we go back to being auto
<emilio> ... flex/grid pretend that you are aligned as the single kid
<emilio> ... what TabAtkins and iank_ are saying is that if you set the alignment props to a value other than normal then we align to the abspos cb
<emilio> ... you could think it as setting insets to 0
<emilio> ... so question is, is that more intuitive that remaining in the regular cb (and thus you need to set insets to 0 to get positioned in your abspos cb)
<emilio> ... that's the fundamental issue we're disagreeing on
<TabAtkins> q+
<emilio> ... the existing behavior on grid / flex is a compat constraint, though iank_ is willing to implement / ship this and see if it's not
<emilio> ... the other thing is that alignment on block are not easy to emulate with anchor positioning
<fremy> based on the explainer, slightly favoring auto-zeroing, I think
<emilio> TabAtkins: the behavior you described for block is not what's in the spec
<emilio> ... the spec is saying a 0-width behavior
<emilio> fantasai: it's not, it's full-width
<emilio> TabAtkins: so in this case if you did align-self: end would be higher than align-self: start
<emilio> ... in an inline context you have 0-width, 1lh-height so you get that weird behavior for justify
<emilio> ... my argument is that nobody implements this behavior for inline / block yet
<emilio> ... so there's compat impact either way
<emilio> ... we don't believe there's compat impact on changing the grid / flex behavior
<miriam> q?
<miriam> ack TabAtkins
<emilio> ... ignoring compat the argument is still that the behavior defined here is very limited and weird
<florian> q+
<emilio> ... we want to do less of this static positiony stuff
<emilio> ... given its power is largely replaced by anchor positioning
<florian> q-
<emilio> miriam: we're at time
<emilio> astearns: we'll get compat data and come back

@FremyCompany
Copy link
Contributor

Based on the discussion, I think I weakly favor the zero-ing behavior.

@kizu
Copy link
Member

kizu commented Sep 14, 2023

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.

@astearns astearns removed the Agenda+ label Sep 14, 2023
@hober
Copy link
Member

hober commented Oct 3, 2023

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.

@tabatkins
Copy link
Member Author

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 autos 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.

@nicoburns
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
css-align-3 Current Work css-position-3 Current Work
Projects
TPAC 2023 agenda
Thursday Morning
Development

No branches or pull requests