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][css-position] Fixing the animation problem #9598

Closed
tabatkins opened this issue Nov 13, 2023 · 25 comments
Closed

[css-anchor-position][css-position] Fixing the animation problem #9598

tabatkins opened this issue Nov 13, 2023 · 25 comments

Comments

@tabatkins
Copy link
Member

tabatkins commented Nov 13, 2023

Edit: This comment's proposal is out of data, our current proposal is over here.

Issue: Because the inset properties are four separate properties, you can't animate them "together" - instead you can animate each of the four independently, and hope that what you're expressing is reasonable for that to work. That's not always true!

For example, going from bottom: anchor(top); to top: anchor(bottom); (with the opposite inset being auto) will not animate properly - it'll be trying to interpolate top from auto to anchor(bottom) (which won't work), and similar for bottom. You can explicitly write a top/bottom: 0; in each case, which will technically animate, but not in a useful way; now, to get things to line up correctly, you'll need to specify align-self too:

.first {
  top: 0;
  bottom: anchor(top);
  align-self: end;
}
.second {
  top: anchor(bottom);
  bottom: 0;
  align-self: start;
}

...and align-self doesn't have animation rules to smoothly animate from start to end (yet, #9556). (And even if we did, it wouldn't work quite correctly - an align-self: start 50%; will center in an IMCB with a top of (0% + anchor(bottom))/2 and a bottom of (0% + anchor(top))/2, and that is very likely not halfway between its starting and ending position. In general, this sort of "interpolate a position within an interpolating range" doesn't produce a linear interpolation of the position in an absolute context; it produces a quadratic spline path instead.)


So that's the issue. How do we solve it? Here's my suggestion:

  1. Repurpose inset-area. In addition to the existing grid-based syntax, add a [ <side-keyword> <length-percentage> ]{1,4} production, which similarly defines a rectangle and causes auto insets (and normal self-alignment) to resolve accordingly. So inset-area: top anchor(bottom);, for example.
  2. Change the inset-area rules so that, rather than resolving auto insets (and normal self-alignments) at computed-value time, they resolve at used-value time. This removes the ability to animate the inset properties directly from the inset-area value.
  3. Instead, make inset-area directly animateable. Add inset-area-mix(...), which represents the interpolated result between two inset-area values. This represents whatever insets and alignments would be required to make the position of the element interpolate smoothly between the two endpoints (rather than being a naive interpolation of edges and alignment). So long as all your actual inset properties and self-alignment properties are set to auto/normal, it'll Just Work.

I still need to give a little more thought to how the new side-based production handles alignment. In the grid syntax I can pretend that the default anchor is inside the containing block, so all the grid lines are correctly ordered and I can infer some positioning, but that's more difficult when the edges can indeed be misordered due to positioning. I'm pretty sure there's a good solution here, I just haven't fully worked thru it yet.

@tabatkins
Copy link
Member Author

tabatkins commented Nov 17, 2023

Talking this over with @fantasai, she pointed out that this will create very weird and confusing ergonomics for developers using positioning. So we suggest to rejigger it a bit.

Issues with above proposal:

  • doesn't let you animate from a position specfied with inset-area to one specified with inset properties
  • doesn't let you cascade the various positioning bits separately like you can do today (you'd be forced to indirect thru custom props)
  • creates a confusing duplication with the existing inset and alignment properties

So instead of messing with inset-area, the new proposal is to use a new, fairly magical, property

  • provisionally called position-animation
  • captures all the relevant data to resolve the element's margin box position (as described above)
  • authors can only specify split or combo, initial value split (for back-compat)
  • split gives today's behavior, where insets and alignment properties all animate independently. When animating between split and any other value, the interpolated value is split.
  • combo magically resolves at computed-value time to a package of all the relevant data (a "position package"). The used value of a "position package" is the size and position of the element's margin box, after resolving insets/alignment.
  • getComputedStyle serializes split as split, and any other computed value as combo.
    (This way we don't need to expose syntax or serialization of position packages, they're internal to the UA.)
  • There exists a computed-value representation for interpolating two "position package", so that we can represent intermediary states. The used value of the interpolation of two "position packages" is the interpolated position of each margin edge of the box. (This effectively requires the used-value layout information of both packages.) Thus, if both sides are a "position package", the element's actual position animates smoothly between the endpoints.

So, authors can write:

.foo { 
    position: absolute; 
    position-animation: combo; 
    transition: position-animation 1s; 
}
.foo.start { 
    top: 0;
    bottom: anchor(top);
    align-self: bottom;
}
.foo.end {
    top: anchor(bottom);
    bottom: 0;
    align-self: end;
}

...and when they swap the classes from .start to .end, it will smoothy animate the element's position, rather than doing a weird unintuitive animation, or not animating at all.

Using this, we can animate all of the following cases:

  • top: auto to top: <length>
  • inset-area: <anything> to inset-area: <anything>
  • top/left/bottom/right to inset-area
  • anchor(--foo top) when the element with anchor-name: --foo; changes
  • (maybe?) top: 0 when the element establishing the abspos containing block changes (if we want to include abspos containing block resolution info in the package)

~fantasai and Tab

P.S. If this property is combo, and you animate any of the package component properties in a given keyframe, that keyframe will interpolate the missing properties and re-calculate the entire positioning package for that keyframe.

@andruud
Copy link
Member

andruud commented Nov 20, 2023

None of the issues listed with the first approach strike me as huge problems, whereas the "position package" proposal introduces a likely problematic layout dependency (the "magic"), and intermediate values that can't be represented. I'm not sure that's a net win as far as "issues" go, and I'm also not sure if this can be reasonably implemented, but it's an interesting direction to explore regardless. Especially if it could solve the anchor change case.

I'm confused by the model as stated, however. We seem to want to do layout of the box ignoring the "position package" (to determine what goes into the package), but then also it's really the position package that has the final say for the size/position?

provisionally called position-animation

I see the "provisionally", but I'd avoid anything related to "animation" in the property name. The animated aspect of this should come from the act of animating/transitioning the thing, not as an intrinsic part of the property.

split or combo

I don't think we actually need to have two values. Any interpolated state (that's not at the endpoints) can automatically behave as "combo".

getComputedStyle serializes split as split, and any other computed value as combo.
(This way we don't need to expose syntax or serialization of position packages, they're internal to the UA.)

It feels so wrong. :-) Would this be the first thing we can't even represent with Typed OM?

@tabatkins
Copy link
Member Author

None of the issues listed with the first approach strike me as huge problems

I think they are pretty reasonable usability issues. Nothing technically wrong with what's in the first post, but it's not a great layering.

a likely problematic layout dependency

The second proposal is exactly identical in terms of power and layout dependency as the first. It allows literally no new abilities, save that it can interact directly with top/left/etc rather than requiring them to be packed into an inset-area value.

Literally, this was intended to be absolutely nothing but a movement of syntax; rather than blessing inset-area as "the way to get good position animation" and relying on inset:auto to not override that (which then requires us to recreate top/left/etc in inset-area, so we can actually do the things those properties are good at), we just move the "good position animation" thing to a third property, so we can continue to use top/left/etc as normal and still have it interact with inset-area as necessary. If you're reading anything else into this, that's just a failure on our part to communicate it well.

I don't think we actually need to have two values. Any interpolated state (that's not at the endpoints) can automatically behave as "combo".

No, we have to have split because people can animate the inset properties today and get the existing not-particularly-great behavior. And if they got this by specifying transition-property: all, then that would apply to the new prop as well, and change their behavior.

@andruud
Copy link
Member

andruud commented Nov 21, 2023

Literally, this was intended to be absolutely nothing but a movement of syntax;

Ah, OK. That's not how I read it. But I think I get it now, thanks. Instead of the author explicitly creating the "position package" via inset-area (v2), it's sort of automatically created from the existing inset properties and other stuff known at computed-value time.

However, it does go slightly beyond moving the syntax around if it solves the named anchor change issue. :-)

No, we have to have split [...]

Right, transition:all. I always forget about that.

@lilles
Copy link
Member

lilles commented Nov 21, 2023

Maybe I should have been able to infer from the discussion above, but what happens if you animate an inset property at the same time as animating the position package?: transition-property: top, position-animation

Just does not interpolate top separately if position-animation is combo?

@tabatkins
Copy link
Member Author

However, it does go slightly beyond moving the syntax around if it solves the named anchor change issue.

Well, sure, but as we discussed internally, the named anchor change issue should end up being fixed anyway, with proper definition and reference to the machinery driving cq transitions.

what happens if you animate an inset property at the same time as animating the position package

I'm open to whatever behavior in that case. We could snapshot, or live-update, whatever's more reasonable for impls. There's no user argument for one behavior or another.

@kizu
Copy link
Member

kizu commented Nov 21, 2023

As a curious sidenote: when I was playing with “polyfilling” the position-area using the existing anchor positioning, I did stumble upon several transition issues, mostly related to aligning the popover close to the target based on the side it is shown.

The way I solved it in my case was by having two elements, and having transitions for a) inset properties (which were never auto), and the grid columns and rows (as a way to handle the “auto” aligning).

Here is this experiment: https://codepen.io/kizu/pen/zYMmVJd

(this comment is mostly as “as an author I did encounter this issue, would be happy with any solution, and for some cases we might have some workarounds already, though not ideal ones”).

chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Nov 27, 2023
As part of the proposal in [1], inset-area affects inset properties at
used value time. Testing with typed-om since inset properties use
resolved values for getComputedStyle.

[1] w3c/csswg-drafts#9598 (comment)

Bug: 1477314
Change-Id: I2db25b3546c6553b36f8b9cfd88aa4d906e7ea28
aarongable pushed a commit to chromium/chromium that referenced this issue Nov 29, 2023
As part of the proposal in [1], inset-area affects inset properties at
used value time. Testing with typed-om since inset properties use
resolved values for getComputedStyle.

[1] w3c/csswg-drafts#9598 (comment)

Bug: 1477314
Change-Id: I2db25b3546c6553b36f8b9cfd88aa4d906e7ea28
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5063151
Reviewed-by: Anders Hartvoll Ruud <andruud@chromium.org>
Commit-Queue: Rune Lillesveen <futhark@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1230573}
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Nov 29, 2023
As part of the proposal in [1], inset-area affects inset properties at
used value time. Testing with typed-om since inset properties use
resolved values for getComputedStyle.

[1] w3c/csswg-drafts#9598 (comment)

Bug: 1477314
Change-Id: I2db25b3546c6553b36f8b9cfd88aa4d906e7ea28
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5063151
Reviewed-by: Anders Hartvoll Ruud <andruud@chromium.org>
Commit-Queue: Rune Lillesveen <futhark@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1230573}
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Nov 29, 2023
As part of the proposal in [1], inset-area affects inset properties at
used value time. Testing with typed-om since inset properties use
resolved values for getComputedStyle.

[1] w3c/csswg-drafts#9598 (comment)

Bug: 1477314
Change-Id: I2db25b3546c6553b36f8b9cfd88aa4d906e7ea28
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5063151
Reviewed-by: Anders Hartvoll Ruud <andruud@chromium.org>
Commit-Queue: Rune Lillesveen <futhark@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1230573}
@tabatkins
Copy link
Member Author

tabatkins commented Dec 1, 2023

Okay, further discussion with Anders and Rune led to some more evolution in this idea. They pointed out that the “inset-area affects inset:auto values” thing can't actually be done at computed-value time, like I hoped, because it relies on the writing mode of the containing block, and the containing block tree isn't built until later. So inset-area can't turn into animatable computed values anyway.

But, now that we're entertaining the “just make a magic way to animate good” solution, we don't actually need inset-area to be itself animatable in the first place. We can return inset-area to modifying the containing block itself, like in Apple's earlier draft, and still retain the animatability we wanted. This means that the inset properties would now compose with inset-area, rather than being an alternative to it.

So @fantasai and I have an expanded Round 3 of the proposal:

  1. inset-area just changes the containing block, and inset properties then inset from that modified block (as proposed by Apple in July)
    • This is a better separation of functionality.
    • Allows for more powerful offsetting than margins do (because margin %s are always relative to inline avaiable space, while inset %s are relative to their own axis).
    • Lets us fix overconstrained cases when the anchor starts outside of the CB in a more deliberate way (rather than just relying on the 'top'/'bottom' /etc biasing rules). The CB shrinks to zero width/height and stays attached to the anchor, regardless of which side the anchor is off.
    • Also addresses some of the “absolute positioning, but in reference to the element I specify, not the nearest positioned ancestor” use case.
    • Composes with Grid's ability to set the CB to a grid area: Grid goes first, then inset-area modifies the CB further.
  2. auto insets always compute to 0 when inset-area is non-none.
  3. normal alignment adjusts based on the inset-area (per axis):
    • If the inset-area is center, or all three tracks: normal alignment resolves to anchor-center
    • Otherwise: normal aligns towards the “missing” areas.
    • Examples: inset-area: top / all; would align as align-self: end; justify-self: anchor-center;, while inset-area: top / left center; would instead use justify-self: end;/
  4. “Good” interpolation is done by a separate property (provisionally called 'position-animation') that represents the used position, see positioning package method, above.

moz-v2v-gh pushed a commit to mozilla/gecko-dev that referenced this issue Dec 2, 2023
…ues, a=testonly

Automatic update from web-platform-tests
[inset-area] Test for computed inset values

As part of the proposal in [1], inset-area affects inset properties at
used value time. Testing with typed-om since inset properties use
resolved values for getComputedStyle.

[1] w3c/csswg-drafts#9598 (comment)

Bug: 1477314
Change-Id: I2db25b3546c6553b36f8b9cfd88aa4d906e7ea28
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5063151
Reviewed-by: Anders Hartvoll Ruud <andruud@chromium.org>
Commit-Queue: Rune Lillesveen <futhark@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1230573}

--

wpt-commits: 8fe24833480b88775d0f9b4f908c63c185162fb2
wpt-pr: 43375
vinnydiehl pushed a commit to vinnydiehl/mozilla-unified that referenced this issue Dec 3, 2023
…ues, a=testonly

Automatic update from web-platform-tests
[inset-area] Test for computed inset values

As part of the proposal in [1], inset-area affects inset properties at
used value time. Testing with typed-om since inset properties use
resolved values for getComputedStyle.

[1] w3c/csswg-drafts#9598 (comment)

Bug: 1477314
Change-Id: I2db25b3546c6553b36f8b9cfd88aa4d906e7ea28
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5063151
Reviewed-by: Anders Hartvoll Ruud <andruud@chromium.org>
Commit-Queue: Rune Lillesveen <futhark@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1230573}

--

wpt-commits: 8fe24833480b88775d0f9b4f908c63c185162fb2
wpt-pr: 43375
@lilles
Copy link
Member

lilles commented Dec 4, 2023

  1. auto insets always compute to 0 when inset-area is non-none.

Is that really better than authors having to set insets to 0 explicitly?

Isn't it more common to have a specified size, or intrinisic sizing for the anchored element where you would want the insets to be auto and let the anchored element be positioned according to the alignment given by the inset-area spans? In that case you would have to force some insets back to auto.

Example:

#anchored {
  inset-area: top / left;
  bottom: 10px;
  right: 10px;
  /* resetting left/right necessary if insets are set to 0 by inset-area */
  left: auto;
  right: auto;
}

Actually, "auto insets always compute to 0" wouldn't make my example above work at all.

@tabatkins
Copy link
Member Author

Is that really better than authors having to set insets to 0 explicitly?

Well, without that, you have double-auto inset behavior, which currently implies invoking static positioning (instead of actually positioning relative to the anchor, which you're clearly indicating you want).

Are you imagining some additional behavior to make double-auto insets work differently in the presence of inset-area?

and let the anchored element be positioned according to the alignment given by the inset-area spans?

Note that that's already done in the proposal in item (3), by having inset-area change the behavior of normal self-alignment. But it does depend on having a reasonable inset-modified CB set up, which is why the current definition resolves auto insets to 0.

@lilles
Copy link
Member

lilles commented Dec 5, 2023

Is that really better than authors having to set insets to 0 explicitly?

Well, without that, you have double-auto inset behavior, which currently implies invoking static positioning (instead of actually positioning relative to the anchor, which you're clearly indicating you want).

I thought the double-auto inset behavior is what I wanted if I didn't apply any inset properties.

I didn't understand how you could possibly get the intrinsic sizing behavior if you forced auto insets to 0, but I see now that you can achieve that with width: fit-content as that would override the constraints from left:0; right:0;.

Are you imagining some additional behavior to make double-auto insets work differently in the presence of inset-area?

No.

and let the anchored element be positioned according to the alignment given by the inset-area spans?

Note that that's already done in the proposal in item (3), by having inset-area change the behavior of normal self-alignment. But it does depend on having a reasonable inset-modified CB set up, which is why the current definition resolves auto insets to 0.

@tabatkins
Copy link
Member Author

I thought the double-auto inset behavior is what I wanted if I didn't apply any inset properties.

Unless we define something else, the double-auto inset beahvior is the "position yourself to the static pos" behavior, which doesn't pay attention to inset-area (or anything else directly position-related). Could you elaborate on what you are expecting the behavior to be?

I didn't understand how you could possibly get the intrinsic sizing behavior if you forced auto insets to 0

The legacy behavior where absposes stretch to fill their inset-modified containing block is trigger by align/justify-self: stretch; (or, currently, align-self: normal;, which acts like stretch by default). If you use any other alignment, you get the intrinsic sizing behavior, and then it aligns within the IMCB instead. See https://drafts.csswg.org/css-align/#justify-abspos for details.

The proposal here changes align-self: normal, when inset-area is used, to behave in that latter fashion. No need to explicitly size the element.

@lilles
Copy link
Member

lilles commented Dec 6, 2023

I thought the double-auto inset behavior is what I wanted if I didn't apply any inset properties.

Unless we define something else, the double-auto inset beahvior is the "position yourself to the static pos" behavior, which doesn't pay attention to inset-area (or anything else directly position-related). Could you elaborate on what you are expecting the behavior to be?

Nothing more to elaborate on. It was me believing that it wouldn't be possible to have a fixed offset against the anchor corner while still have intrinsic sizing of the anchored element possible if the auto inset values where forced to 0.

gecko-dev-updater pushed a commit to marco-c/gecko-dev-comments-removed that referenced this issue Dec 7, 2023
…ues, a=testonly

Automatic update from web-platform-tests
[inset-area] Test for computed inset values

As part of the proposal in [1], inset-area affects inset properties at
used value time. Testing with typed-om since inset properties use
resolved values for getComputedStyle.

[1] w3c/csswg-drafts#9598 (comment)

Bug: 1477314
Change-Id: I2db25b3546c6553b36f8b9cfd88aa4d906e7ea28
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5063151
Reviewed-by: Anders Hartvoll Ruud <andruudchromium.org>
Commit-Queue: Rune Lillesveen <futharkchromium.org>
Cr-Commit-Position: refs/heads/main{#1230573}

--

wpt-commits: 8fe24833480b88775d0f9b4f908c63c185162fb2
wpt-pr: 43375

UltraBlame original commit: 6a88796424e055d7b7064bc9b11edcfc0ce321f2
gecko-dev-updater pushed a commit to marco-c/gecko-dev-wordified that referenced this issue Dec 7, 2023
…ues, a=testonly

Automatic update from web-platform-tests
[inset-area] Test for computed inset values

As part of the proposal in [1], inset-area affects inset properties at
used value time. Testing with typed-om since inset properties use
resolved values for getComputedStyle.

[1] w3c/csswg-drafts#9598 (comment)

Bug: 1477314
Change-Id: I2db25b3546c6553b36f8b9cfd88aa4d906e7ea28
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5063151
Reviewed-by: Anders Hartvoll Ruud <andruudchromium.org>
Commit-Queue: Rune Lillesveen <futharkchromium.org>
Cr-Commit-Position: refs/heads/main{#1230573}

--

wpt-commits: 8fe24833480b88775d0f9b4f908c63c185162fb2
wpt-pr: 43375

UltraBlame original commit: 6a88796424e055d7b7064bc9b11edcfc0ce321f2
gecko-dev-updater pushed a commit to marco-c/gecko-dev-wordified-and-comments-removed that referenced this issue Dec 7, 2023
…ues, a=testonly

Automatic update from web-platform-tests
[inset-area] Test for computed inset values

As part of the proposal in [1], inset-area affects inset properties at
used value time. Testing with typed-om since inset properties use
resolved values for getComputedStyle.

[1] w3c/csswg-drafts#9598 (comment)

Bug: 1477314
Change-Id: I2db25b3546c6553b36f8b9cfd88aa4d906e7ea28
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5063151
Reviewed-by: Anders Hartvoll Ruud <andruudchromium.org>
Commit-Queue: Rune Lillesveen <futharkchromium.org>
Cr-Commit-Position: refs/heads/main{#1230573}

--

wpt-commits: 8fe24833480b88775d0f9b4f908c63c185162fb2
wpt-pr: 43375

UltraBlame original commit: 6a88796424e055d7b7064bc9b11edcfc0ce321f2
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Jan 11, 2024
The new proposal is here:

w3c/csswg-drafts#9598 (comment)

inset-area now affects the containing block size and 'auto' insets
become 0 for inset-area.

Bug: 1477314

Change-Id: Ia2f0d5a1f866cd659f9e3f87ec2f639141df6a94
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Jan 11, 2024
The new proposal is here:

w3c/csswg-drafts#9598 (comment)

inset-area now affects the containing block size and 'auto' insets
become 0 for inset-area.

Bug: 1477314

Change-Id: Ia2f0d5a1f866cd659f9e3f87ec2f639141df6a94
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Jan 12, 2024
The new proposal is here:

w3c/csswg-drafts#9598 (comment)

inset-area now affects the containing block size and 'auto' insets
become 0 for inset-area.

Bug: 1477314

Change-Id: Ia2f0d5a1f866cd659f9e3f87ec2f639141df6a94
tabatkins added a commit that referenced this issue Feb 6, 2024
@astearns astearns moved this from Unsorted assorted to Tuesday morning in Feb 2024 Agenda Feb 8, 2024
@FremyCompany
Copy link
Contributor

Also see #9945

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-anchor-position][css-position] Fixing the animation problem.

The full IRC log of that discussion <fantasai> Tab is going to intro the issue for people to think about
<fantasai> -> https://github.com//issues/9598#issuecomment-1836854109
<fantasai> -> https://github.com//issues/9598#issuecomment-1817244328
<fantasai> TabAtkins: Suppose a very simple example: animating from `inset-area: top` to `inset-area: bottom`.
<fantasai> TabAtkins: These containing blocks aren't numbers, they're names. So can't transition them.
<fantasai> TabAtkins: It also defaults the alignment to different values
<fantasai> TabAtkins: can't transition from align-self end (first case) to align-self: start (second case)
<fantasai> TabAtkins: Can't animate between those
<fantasai> TabAtkins: If your positions can be described by top/left/bottom/right (and the same combination of such properties) in both first and last frames, you can animate smoothly
<fantasai> TabAtkins: but otherwise can't
<fantasai> TabAtkins: We're proposing to solve this by introducing a new property
<fantasai> TabAtkins: position-animation: normal | magic
<dbaron> TabAtkins: "magic" is not the final name
<fantasai> TabAtkins: magic computes to a rectangle. This rectangle is never exposed to authors, always serializes as "magic"
<fantasai> TabAtkins: but it has x/y offsets and width/height
<fantasai> TabAtkins: when position-animation is magic on both sides of a transition, this overrides all the rest of the abspos positioning properties (insets, alignment, etc)
<fantasai> TabAtkins: we can smoothly interpolate the rect, so you get proper animation
<fantasai> TabAtkins: We don't expose the rect to authors because this would bypass the rest of the cascade -- it needs to win over all the other properties to work
<florian> q?
<florian> q+
<fantasai> TabAtkins: modulo style interleaving issue, which need to discuss, we think this would solve all the animation issues for positioning
<khush> q+
<dbaron> position-animation: properties | coordinates
<fantasai> TabAtkins: it also fixes, e.g. if you change which element resolves against your --foo anchor name
<emilio> q+
<fantasai> fantasai: So you'd write `transition: position-animation 2s`
<fantasai> TabAtkins: assuming you set `magic` for both starting and ending points
<fantasai> TabAtkins: our implementers think something similar to this is going to work

@astearns
Copy link
Member

Reminder to @frivoal @khushalsagar and @emilio to post the responses we did not have time for in the meeting here

@khushalsagar
Copy link
Member

Its unclear how we deal with the fact that the anchor's position can change in the middle of the animation. Its possible you're interpolating from top to left, there is an active scroll, and while we're in the middle of the top->left interpolation the position fallback algorithm changes the end state to bottom.

@dbaron
Copy link
Member

dbaron commented Feb 15, 2024

I think the general handling that's in CSS transitions for style changes that happen in the middle of a transition and change the end state ought to handle that sort of case reasonably. Transitions has a bunch of special cases for the timing of such a change if the new endpoint is the old start point, that is, when the original change is being reversed -- but otherwise you should just end up with a regular new transition from the current point in the old transition to the new destination. As long as the magic animation of positions picks up the values from the current point in the animation, I think this ought to animate smoothly from the current position. (At least, I'm assuming a smooth animation is what's desirable.)

I admit it's not quite smooth in that the old transition stops suddenly while the new one starts smoothly as though the old position were static, but I haven't heard complaints about that behavior for other types of animations.

@khushalsagar
Copy link
Member

I think the general handling that's in CSS transitions for style changes that happen in the middle of a transition and change the end state ought to handle that sort of case reasonably.

I agree that as long as the end state of the property being interpolated by the CSS transition is updated when the anchor position changes, it should work. But the model @fantasai was describing had some sort of "reflect"/"override" modes, where in reflect mode the browser is computing the anchor position (using all the fallbacks) as usual. And when the CSS transition is triggered, we switch to an "override" mode where the interpolated position applies until the end of the transition. We don't recompute the anchor position. With this model the anchor position won't retarget until the end of the transition which would be incorrect.

@andruud
Copy link
Member

andruud commented Feb 16, 2024

TabAtkins: our implementers think something similar to this is going to work

Yes, but as we've discussed before internally Tab, I don't like the very animation-specific feel of this, nor the magic behavior of magic, which serializes as one thing, but "is" another thing. We may have precedent for this, but it feels like a last resort. So I'll propose a tweak to what has been discussed so far. "Boring", because it's less magical.

Boring Position Package

The position/size of the element can be affected by many things:

  • left, right, top, bottom
  • align-self, justify-self
  • inset-area
  • sizing properties

But ultimately, it must result in a rect [X,Y,W,H] in some reference frame.

Perhaps we can give the author the power to optionally override this [X,Y,W,H] with a new property, tentatively called extent (is that available?). The extent property is the thing which actually determines the used position/size of the element, it just happens to resolve to the aforementioned [X,Y,W,H] by default.

extent: normal | auto | <length>{4}

  • normal
    • Initial value. Computes to normal.
    • The extent is whatever came out of insets/alignment/sizing, i.e. [X,Y,W,H].
  • auto
    • Computes to Xpx Ypx Wpx Hpx.
    • This means auto is resolved into pixels by "style interleaving", and that transitions can happen whenever auto changes to something else.
  • <length>{4}
    • Computes to those lengths, in pixels. Using this means that inset/alignment/sizing properties don’t actually affect the result.
    • The main purpose of this is not for direct author use, but as interpolated values generated by animation effects.

Unanswered question: What is [X,Y] relative to? (We have to answer this question also for the non-boring position package).

The above would not affect what we need to implement, nor the problems we need to solve vs. the non-boring position package, but I think this results in a less magical API.

Usage:

extent: auto;
transtion: extent 1s;

@fantasai
Copy link
Collaborator

fantasai commented Feb 22, 2024

@andruud The reason I pushed to have the rects serialize out as a magic keyword and not be parseable in CSS syntax is that this property effectively overrides every declaration that affects positioning and layout in CSS regardless of its cascade level. I don't think we should be giving authors access to such a property, even if we need it internally to make the animations work. It should never be used in CSS.

@emilio
Copy link
Collaborator

emilio commented Feb 22, 2024

I commented on this in the F2F, but I think this is a lot of complexity and magic, to the point where it might be worth punting to L2 along with ::tether. It's also not so clear to me if this would deal correctly with various edge cases around moving the anchor around or changing the popup contents...

@FremyCompany
Copy link
Contributor

FremyCompany commented Feb 22, 2024

A compromise is to support position interpolation but not size interpolation. This can be implemented very efficiently by having the translate property be the way the popup's top-left corner (in LTR-TB writing mode) is positioned, and allowing transitions on that property. If however the popup changes design or layout as a result of the positioning, it changes design immediately (pending a better mechanism to deal with it).

I'm not a fan of the "intermediary sizes are just interpolations of two rectangles" thing, it's not efficient and I don't think it makes sense in the vast majority of the cases where it will happen.

marcoscaceres pushed a commit to web-platform-tests/wpt that referenced this issue Feb 23, 2024
The new proposal is here:

w3c/csswg-drafts#9598 (comment)

inset-area now affects the containing block size and 'auto' insets
become 0 for inset-area.

Bug: 1477314

Change-Id: Ia2f0d5a1f866cd659f9e3f87ec2f639141df6a94
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5163733
Commit-Queue: Rune Lillesveen <futhark@chromium.org>
Reviewed-by: Ian Kilpatrick <ikilpatrick@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1247073}
@andruud
Copy link
Member

andruud commented Feb 27, 2024

@fantasai Right, I was a bit surprised by this, as it contradicted what Tab and I discussed a few days before. But I do see the problem.

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-anchor-position][css-position] Fixing the animation problem, and agreed to the following:

  • RESOLVED: Specify how styles that are conditionally dependant on layout get recomputed
  • RESOLVED: We will use the same sort of recomputed values for animations on anchor & anchor-size functions
  • RESOLVED: Accept that inset area changes the containing block
  • RESOLVED: Defer magic position animations until level 2
The full IRC log of that discussion <keithamus> TabAtkins: this is two distinct issues. I had an issue in the works to post, but didn't get it finished before the end of the day.
<keithamus> TabAtkins: Presently, when we define container queries we implied how anything effected by the query worked but it's not specified.
<keithamus> TabAtkins: At the least chrome and webkit act similarly - so there's a throughline.
<keithamus> TabAtkins: The existence of container queries is that computed values depend on layout
<keithamus> TabAtkins: They must be computed. If a container query matches, changes the value, and the child has a transition on the property it needs to fire the transition
<keithamus> TabAtkins: This is a computed value change. We can't know this until layout is already happening and has reached the container.
<keithamus> TabAtkins: So we need to somehow figure out what the values were and patch them in.
<keithamus> TabAtkins: This needs to be specified.
<keithamus> TabAtkins: The rough description is what i just described; part way through layout when we interleave style and layout, and some point (which we'll specify) you realise the computed values need to change as the result of layout, we compute them, teat them as computed values - figure out trasnitions, inheritance, etc - then continue along as if they
<keithamus> were alwyas those values
<keithamus> TabAtkins: this is also how we make anchoring work well
<keithamus> TabAtkins: With the interleaving - does that sound reasonable to eveyrone? Chrome already does this. WebKit is similar but its not as scoped, they do more work but the end result is the same
<keithamus> TabAtkins: I do not know what Firefox does. How it handles these.
<keithamus> emilio: we do something very similar to WebKit here.
<keithamus> TabAtkins: impl details determine exactly what happens, but does this description work?
<emilio> q
<emilio> q+
<astearns> ack emilio
<keithamus> emilio: for webkit and gecko (may have some issues with transitions). Is sort of what you describe but it's not... you update the page layout, then recompute the styles for things that change. I'm not sure specifying it that way... like the interleaving blink has...
<keithamus> emilio: if you do the extra work of if something changes then recompute it... I mean it generally sounds the right direction but I'd like to have a read of it before.
<keithamus> TabAtkins: yes I'm interested in if this is the right direction
<keithamus> emilio: the difference between blink and webkit/gecko, AFAIK, style the page as if no container queries match, then do layout, then for things where container queries change, recompute style, then loop until you get to a stable state. Blink does something more subtle.
<iank_> q+
<keithamus> futhark: in optimal cases we can pause layout and style the subtree, then continue. In several cases we do also need to do multiple passes. Layout depends on auto widths for eg we may not correctly detect this then have something more similar to gecko/webkit
<keithamus> iank_: not exactly the same...
<astearns> ack iank_
<keithamus> iank_: webkit/gecko are failing tests for behavioural difference. Some subtle differences, because they're revisiting prior states.
<keithamus> astearns: are tests written down based on spec or impl?
<keithamus> iank_: written in spec.
<TabAtkins> (there is no spec to be followed right now)
<keithamus> astearns: observability... is this a design decision for how we visit this? Recomputation being observable?
<emilio> q+
<keithamus> chrishtr: right. Not observable.
<keithamus> chrishtr: difference in blink and other two engines, it's a bit more optimised to avoid recomputing.
<keithamus> iank_: it's quite a bit faster for degenerate cases, and bugs in other engines. Theoretically could be fixed.
<keithamus> chrishtr: just a matter of quality of impl difference?
<astearns> ack emilio
<keithamus> emilio: main difference is how to prevent cycles, things depending on siblings, ancestors, scrollbars, and so on
<keithamus> iank_: not cyclical but previous states.
<keithamus> astearns: Do we want a resolution? Computed values getting recomputed?
<keithamus> PROPOSED RESOLUTION: Specify how styles that are conditionally dependant on layout get recomputed
<keithamus> RESOLVED: Specify how styles that are conditionally dependant on layout get recomputed
<keithamus> Zakim, next item
<Zakim> I see nothing on the agenda
<keithamus> TabAtkins: next up. Anchor and anchor size functions should be dependent on this concept.
<keithamus> TabAtkins: right now... the anchor function needs to turn into a length, we're not sure exactly when, but at some point it needs to turn into a length to position your element. You need to layout your anchor first then figure out what the length is. It should be transitionable.
<keithamus> TabAtkins: if your anchor moves, you should be able to smoothly transition things. If the element you're referring to, e.g. changing anchor name values, you should smoothly transition to them.
<keithamus> TabAtkins: So this is dependent on that too.
<keithamus> TabAtkins: Larger bit on animation too, but the interleaving is important
<keithamus> emilio: other than animation why do we need it to turn into a length?
<keithamus> TabAtkins: it is for animation
<keithamus> TabAtkins: for all other purposes it's fine
<keithamus> emilio: it feels unfortunate that for all other cases it needs to do the extra work to compute
<keithamus> emilio: making it a computed value requires some amount of extra work. Unfortunate to require this. If we need it to be computed, then it makes sense, but ordering & dependencies get very tricky.
<keithamus> TabAtkins: It's a more complex relationship than container queries. At the moment it means we don't do as-smart trimming for interleaving because the relationship is more complex.
<keithamus> astearns: if we want these things to animate then we have to do something? These things animate descretely... we don't want that. Stuffing these changes into computed values, beause that's how animations work, but I suppose we could say computed values that depend on layout use used values instead? That seems worse
<keithamus> TabAtkins: I don't know if that seems worse. We're doing all this for the purpose of animations. If there's a simpler solution I'm happy to explore it.
<chrishtr> q+
<keithamus> iank_: can't use used values... they're at the wrong phase for animations. To do that would be a big and complicated lift.
<keithamus> astearns: we're kind of doing that, not used but recomputed which are closer to computed.
<keithamus> iank_: I don't think it's comparable.
<astearns> ack chrishtr
<keithamus> chrishtr: second what iank_ says. emilio is right in difficulty of interleaving but the coherence and overall complexity is less than creating a new way of doing animations. This is just scoped to doing the interelaving, already had to be done for container queries, animations just fall out of that. Confirmed in prototype in blink. Clean solution.
<keithamus> q?
<astearns> ack dbaron
<keithamus> dbaron: the other scary thing is used values - we don't fully formally defined them vs earlier stages
<keithamus> PROPOSED RESOLUTION: We will use the same sort of recomputed values for animations on anchor & anchor-size functions
<keithamus> emilio: do we need to define what they resolve to when the anchor is not there?
<keithamus> TabAtkins: already in the spec. It's the fallback value, which is zero if not specified.
<keithamus> emilio: if your anchor has been laid out... I guess I'm wary of the outcomes that happen when you incrementally do this. An anchor function...
<keithamus> emilio: I think container queries have the same issues but this can be sorted in the spec.
<keithamus> emilio: when you have a style change and the anchor has been laid out before. That can be dealt with the same way as container queries. Probably fine.
<keithamus> TabAtkins: Curious about exactly the case. We can discuss in the issue if you'd like
<keithamus> astearns: is there a need to specify how animation from anchor size from initial to actual recomputed value? Or does that fall out
<keithamus> TabAtkins: initial value?
<keithamus> astearns: if anchor isnt there it falls back to initial value. Something changes such that there is an anchor - is it possible to animate from that to a place in layout?
<keithamus> TabAtkins: that behavior should fall out
<keithamus> RESOLVED: We will use the same sort of recomputed values for animations on anchor & anchor-size functions
<chrishtr> +1 to deferring to level 2
<keithamus> TabAtkins: one final bit. Do we want to defer fancy animation stuff? emilio proposed that there are enough holes in the idea of position animation animating the rectangle idea I discussed in the f2f. We want to spend more time thinking about it? I'm okay with this based on above resolutions. Most cases will animate reasonably.
<keithamus> TabAtkins: Edge cases should be safe to upgrade to via some kind of opt in.
<keithamus> TabAtkins: I propose we kick animation idea to level 2 and work on it later.
<astearns> ack fantasai
<fantasai> https://github.com//issues/9598#issuecomment-1836854109
<keithamus> fantasai: I dont mind if we want to push animatability questions to level 2, but there was a number of follow up changes we were planning to make to inset area, alignment, and other things listed, I don't want to defer. As long as making those changes depend on animation, I'd like us to figure out animation
<keithamus> TabAtkins: what might we change on inset area?
<keithamus> fantasai: containing block vs only causing auto values to compute
<keithamus> TabAtkins: already in the spec
<keithamus> fantasai: when? That was in this issue?
<keithamus> TabAtkins: it's been in for a while
<keithamus> TabAtkins: previously we had inset area work by adjusting auto values of inset properties, causing inset properties to match up. This would allow you to animate between inset areas, to some extent. The Apple proposal instead proposed the inset area changed the containing block, which I pushed back on as it was less animatable.
<keithamus> TabAtkins: it's a better behaviour to do the container block change and solve animations some other way. If we're deferring the animations stuff to level 2 it means we're potentially regressing inset area animations. I'm okay with that... if we set inset area back to how... resolves, we don't have a good path to a better future world.
<keithamus> TabAtkins: I'm okay with it being less animatable now since we have an idea of how we're going to fix in the future.
<keithamus> PROPOSED RESOLUTION: Accept that inset area changes the containing block
<keithamus> RESOLVED: Accept that inset area changes the containing block
<flackr> q+
<keithamus> fantasai: that was my main concern, if we need more time and nothing is blocking then that's fine
<astearns> ack flackr
<keithamus> flackr: we won't be able to automatically enable these animations right?
<keithamus> TabAtkins: current proposal is a position-animation property to trigger the good behaviour.
<keithamus> TabAtkins: cannot do it by default as it'd be incompatible with todays behaviour
<keithamus> flackr: can change it so certain position types are animatable and others aren't?
<keithamus> TabAtkins: yes, this is an argument to defer it so it isn't just focused on anchor
<keithamus> flackr: I just want to make sure the upgrade path isn't too difficult
<keithamus> astearns: what will be animatable in level 1 vs 2?
<keithamus> TabAtkins: level 1- properties animating as normal to the extent of positioning insets, e.g. top and left. but if you switch from top to using bottom that won't smoothly interpolate.
<keithamus> TabAtkins: or if you change inset area, or self alignment from align self to justify self. Properties will animate it won;t be descrete, but it wont be smooth
<keithamus> flackr: usually authors will rely on transitions so there'd be no animation, change from auto to non auto would be descrete
<keithamus> astearns: any concerns with level 1?
<keithamus> PROPOSED RESOLUTION: Defer magic position animations until level 2
<keithamus> RESOLVED: Defer magic position animations until level 2
<keithamus> astearns: Next Clarifying inset area syntax.

aarongable pushed a commit to chromium/chromium that referenced this issue Mar 8, 2024
This CL removes the option of evaluating anchor() and anchor-size()
functions at used-value time, per recent CSSWG resolution [1].

This eliminates the need for an AnchorEvaluator to resolve a Length,
which means don't need to send that around to the extent we did before.
It's still needed in some cases, because AnchorEvaluatorImpl is used
for some secondary purposes in absolute_utils.cc.

We still use CalculationExpressionAnchorQueryNode to represent
the anchor query during Length::AnchorEvaluator::Evaluate for now.
A follow-up CL will address this.

Except for the removal of the feature flag, there should be no
behavior change.

Bug: 41483417

[1] w3c/csswg-drafts#9598

Change-Id: I068a57fcb6b5c90e133a36e095e8bbf25861714d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5355673
Reviewed-by: Ian Kilpatrick <ikilpatrick@chromium.org>
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1270318}
ns-rsilva pushed a commit to ns-rsilva/chromium that referenced this issue Apr 25, 2024
As part of the proposal in [1], inset-area affects inset properties at
used value time. Testing with typed-om since inset properties use
resolved values for getComputedStyle.

[1] w3c/csswg-drafts#9598 (comment)

Bug: 1477314
Change-Id: I2db25b3546c6553b36f8b9cfd88aa4d906e7ea28
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5063151
Reviewed-by: Anders Hartvoll Ruud <andruud@chromium.org>
Commit-Queue: Rune Lillesveen <futhark@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1230573}

Former-commit-id: 1421d72c1ab32ca7572dd1e34b22f07b999a3b34
ns-rsilva pushed a commit to ns-rsilva/chromium that referenced this issue Apr 25, 2024
The new proposal is here:

w3c/csswg-drafts#9598 (comment)

inset-area now affects the containing block size and 'auto' insets
become 0 for inset-area.

Bug: 1477314

Change-Id: Ia2f0d5a1f866cd659f9e3f87ec2f639141df6a94
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5163733
Commit-Queue: Rune Lillesveen <futhark@chromium.org>
Reviewed-by: Ian Kilpatrick <ikilpatrick@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1247073}

Former-commit-id: 6a27683dd8ecf495240c13630d9ceafd7c78b34e
ns-rsilva pushed a commit to ns-rsilva/chromium that referenced this issue Apr 25, 2024
This CL removes the option of evaluating anchor() and anchor-size()
functions at used-value time, per recent CSSWG resolution [1].

This eliminates the need for an AnchorEvaluator to resolve a Length,
which means don't need to send that around to the extent we did before.
It's still needed in some cases, because AnchorEvaluatorImpl is used
for some secondary purposes in absolute_utils.cc.

We still use CalculationExpressionAnchorQueryNode to represent
the anchor query during Length::AnchorEvaluator::Evaluate for now.
A follow-up CL will address this.

Except for the removal of the feature flag, there should be no
behavior change.

Bug: 41483417

[1] w3c/csswg-drafts#9598

Change-Id: I068a57fcb6b5c90e133a36e095e8bbf25861714d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5355673
Reviewed-by: Ian Kilpatrick <ikilpatrick@chromium.org>
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1270318}

Former-commit-id: b6b27b64ed49dbb40c15bcde8796a6c93cc9bd42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Feb 2024 Agenda
Tuesday morning
Development

No branches or pull requests