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-anchor-position-1][css-position] Add the “centering” behavior which is now defined as an example in the specs as something built-in #8979

Closed
kizu opened this issue Jun 16, 2023 · 14 comments

Comments

@kizu
Copy link
Member

kizu commented Jun 16, 2023

The example in the specs that describes using math functions for the anchors involves a rather complicated calculation:

https://drafts.csswg.org/css-anchor-position-1/#example-a7ac0832

  --center: anchor(--x 50%);
  --half-distance: min(
    abs(0% - var(--center)),
    abs(100% - var(--center))
  );
  left: calc(var(--center) - var(--half-distance));
  right: calc(var(--center) - var(--half-distance));

All of this to attempt to align the element and the anchor alongside their centers, and the example in the specs is not even enough: for a good result we need to add an extra element inside and use flex alignment:

https://codepen.io/kizu/pen/wvYYLGr?editors=1100

To quote @o-t-w from the OpenUI discord,

This will be a common use case so its a shame that the solution given in the spec using abs() is so hyper-verbose.

I previously attempted to do this on a single element with margin: auto, but this did not give a good result as it lead to negative margins in some cases. Similar to if we would use translateX(-50%), a single element solution won't work well with the fallbacks as well.

This is one of the very very common things developers do, and usually end up having to add wrappers to use flex/grid alignment, use margins (which can become negative), or transform (which creates subpixel rendering issues).

I think it is time to add something that would enable these use cases to CSS, either as a part of anchor positioning, or a part of the css-position spec in general.

Some ideas:

  1. When talking about anchor positioning — if we could improve on the method described in the specs regarding the calculation — what if we could provide this behavior in some way as a built-in one, with some keywords or something? I don't like this option, as I couldn't produce a good result on a single element.
  2. Allow using the dimensions of the element itself somehow when anchoring. From what I understand, the main issue with allowing this is due to this: https://codepen.io/kizu/pen/abRRdNW — the dimensions of an elements can depend on its position when the width is auto (https://www.w3.org/TR/css-position-3/#abspos-auto-size). So, what if allow using something like anchor-size(self width) only when the width is not auto? this way, setting width: max-content; would unlock the self-anchor-size, allowing doing calculations for centering much much easier. I think I would prefer this solution, unless there are other limitations not related to the auto-sizing.
  3. Similar how we have inset properties that align the element relative to one or more of its sides, what if we could have something like a inset-center or something, to align the element based on its center? In this case we'd need to update how the auto-sizing would work (probably disable it?), and how it would interplay with the start and end if they're set. Probably use them as “limits”, so if we'd say something like inset-inline-center: 100px alongside with inset-inline-start: 0px, then we would try to position the element so its center would be at the 100px coordinate, but if its start would go beyond the 0px mark, we stop it at it. Overall, I like this option, but it seems to be much more complicated and would require more spec work than option 2.
  4. Something else?

Overall, it is nice that we can kinda align things with math functions, but, given this is a very common case, it would be really nice to have something build-in that would help the developers.

@xiaochengh
Copy link
Contributor

  1. ... if we could improve on the method described in the specs regarding the calculation ...

This actually seems to be the most feasible idea to me. We just need to add a new anchor value, e.g.:

anchor(center-align)

which automatically unfolds into

min(abs(0% - anchor(center)), abs(100% - anchor(center)))

then we can achieve centering with two lines of CSS

inset-inline: anchor(center-align);
justify-self: center; /* The proper way to do centering in positioned layout, but lacking browser support */
margin-inline: auto; /* Fallback if the browser doesn't support it */
  1. Allow using the dimensions of the element itself somehow when anchoring.

anchor-size(self-width) doesn't look compatible with the current design, where anchor-size() needs to be treated as a regular <length>, can participate in calc() and must be evaluated into a pixel value before laying out the element.

To make it compatible, it has to be a new keyword value, something like width: self-anchor-width, but then it can't participate in any calc() any more.

  1. ... what if we could have something like a inset-center or something ...

That's an interesting idea.

It's also more general than anchor positioning that it should go to the CSS Positioned Layout module instead.

@kizu
Copy link
Member Author

kizu commented Jul 4, 2023

This actually seems to be the most feasible idea to me.

Agree about the feasibility — and it could be a good start in absence of other ways to achieve this, even with the margin-inline fallback (or using multiple elements).


Started reading the working draft from scratch, and the first example really falls apart when it comes to centering. First all is good: we have a few declarations with very simple values, but then, suddenly, we get a block of declarations with calculations and a hard-coded values:

  left: clamp(0px, anchor(center) - 150px, 100% - 300px);
  right: clamp(0px, anchor(center) - 150px, 100% - 300px);
  max-width: 300px;

With this being the first example (and the tooltips/popovers centered around their anchors being one of the most prominent use cases for anchor positioning overall), it would be really nice to somehow address this in the specs. cc @tabatkins

@tabatkins
Copy link
Member

The issue with centering is indeed that the inset properties don't actually align a point of the element, they create an area that you size/position into. When aligning one of the box corners you can generally conflate the two, but centering exposes the difference.

The way to actually center thus is to size and align the positioning area such that it's centered on the anchor; then you can center-align the positioned element with justify-self:center. This is easy to do naively; the more complicated examples are baking in the additional constraints that it not overflow the CB, thus the min()/etc functions. (The one with 300/150 is additionally doing some work to ensure the box shifts off-center if that's needed to keep it 300px wide.)

Also, you can generally do these things more simply if you're not relying on fallback and are okay with it overflowing the "inset-modified containing block". (The left: anchor(center); transform: translateX(-50%); trick works then.) Getting the IMCB set up exactly so that overflow can be responded to requires a bit more care.

@xiaochengh's idea works, tho we might want to give it slightly more options. Happy to start from the simple version that just gives you the "largest possible centered area", tho.

@kizu
Copy link
Member Author

kizu commented Jul 20, 2023

Happy to start from the simple version that just gives you the "largest possible centered area", tho.

Yep, I think even that would drastically improve things!

@tabatkins
Copy link
Member

tabatkins commented Jul 20, 2023

Brought up in the CSSWG meeting right now is the idea of an align-self: anchor-center; value, which will automatically center over the anchor (assuming one is specified via anchor-default). (Presumably falling back to 'center' if the element isn't positioned and has an anchor-default.)

Then the code would just be left: 0; right: 0; align-self: anchor-center;, DONE. That's pretty darn good! And it would allow us to specify the "shift off-center if needed to avoid overflow" behavior automatically, without having to do more complicated things in the left/right properties.

@tabatkins
Copy link
Member

Dropped a PR for this proposal in #9136.

@Crissov
Copy link
Contributor

Crissov commented Aug 2, 2023

This is not the same as “circular center alignment” in #1849 but somewhat related, so I’m leaving a reference to it anyway. ;)

@una
Copy link
Contributor

una commented Aug 2, 2023

So to clarify, is this correct?

Current spec:

.centered-anchor {
  position: absolute;
  anchor-default: --myAnchor;
  left: anchor(center);
  translate: -50% 0;
}

New proposal with align-self: anchor-center;:

.centered-anchor {
  position: absolute;
  anchor-default: --myAnchor;
  left: 0;
  right: 0;
  align-self: anchor-center;
}

@tabatkins
Copy link
Member

You can actually omit the left/right as well - anchor-center makes auto insets resolve to 0.

So it's just

.centered-anchor {
  position: absolute;
  anchor-default: --myAnchor;
  justify-self: anchor-center;
}

@una
Copy link
Contributor

una commented Aug 2, 2023

Ok nice, that makes sense to me and looks a lot cleaner 🙂 Big +1

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-anchor-1][css-position] Add the “centering” behavior which is now defined as an example in the specs as something built-in, and agreed to the following:

  • RESOLVED: add the anchor-center value
The full IRC log of that discussion <dael_> TabAtkins: The very first example in the spec shows a common use case. Center positioned element over anchor with a bit of safety
<dael_> TabAtkins: If centering would overflow CB we shift you a little off center.
<dael_> TabAtkins: Cool you can express this, but it's a bit complicated. We had an issue to make it easier
<dael_> TabAtkins: Luckily in grid-baed proposal there was an alternative. A new self-alignment value 'anchor-center'
<dael_> TabAtkins: If you're using anchor positioning and are abspos it will attempt to center you over the element in the axis you spec. If strict center causes overflow we'll shift a bit
<dael_> TabAtkins: I have a PR with spec text.
<dael_> TabAtkins: To decide on- live in anchor positioning or alignment? I put in anchor positioning
<dael_> TabAtkins: few more detailed questions on exact mechanics if no general concerns
<dael_> TabAtkins: Opening the floor for questions
<florian> q?
<dael_> astearns: Seeing +1 in issue. Other opinions
<fantasai> +1 ot adding it, but I haven't had time to review the spec text
<miriam> +1
<una> +1
<dael_> dholbert: Took a quick look and it makes sense to me. Nice to have easy syntax to do the thing people will want
<chrishtr> +1
<florian> +1 to the idea, didn't review the PR yet.
<changseok> +1
<dael_> astearns: I do think makes sense to leave in anchor positioning for now since defined based on anchor properties
<dael_> astearns: Lots of +1s
<dael_> astearns: Objections to adding the anchor-center value
<dael_> RESOLVED: add the anchor-center value
<dael_> astearns: You said there were details. Would it make sense to get PR in and raise separate issues?
<dael_> TabAtkins: That would be good

@xiaochengh
Copy link
Contributor

xiaochengh commented Aug 22, 2023

(Edit: This comment is not about whether to have anchor-center keyword, but refining its behavior. I was suggested to split it off as #9223)

Discussed with @tabatkins offline.

Unfortunately, the way how anchor-center is currently specified doesn't work well if the anchor is initially out of the containing block (but can be scrolled back in), in which case the IMCB is collapsed. The closest idea I can get is to ignore insets when aligning the element, but check IMCB overflow only until position fallback. And this idea doesn't achieve the "shift the element a little to keep it within containing block" behavior that the current spec tries to achieve.

More work is needed.

@kizu kizu changed the title [css-anchor-1][css-position] Add the “centering” behavior which is now defined as an example in the specs as something built-in [css-anchor-position-1][css-position] Add the “centering” behavior which is now defined as an example in the specs as something built-in Nov 25, 2023
@yisibl
Copy link
Contributor

yisibl commented Jan 4, 2024

.centered-anchor {
  position: absolute;
  anchor-default: --myAnchor;
  left: anchor(center);
  translate: -50% 0;
}

One potential benefit of no longer needing translate: -50% 0 is better logical support, since translate does not currently support logical.

@mfreed7
Copy link
Contributor

mfreed7 commented Feb 26, 2024

Should be addressed by the current draft spec.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants