Skip to content

[css-anchor-position] anchor-size() should fallback to auto, not zero #10005

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

Closed
fantasai opened this issue Feb 26, 2024 · 14 comments
Closed

[css-anchor-position] anchor-size() should fallback to auto, not zero #10005

fantasai opened this issue Feb 26, 2024 · 14 comments

Comments

@fantasai
Copy link
Collaborator

I think by default the anchor-size() function in the sizing properties should

  • compute to auto on elements to which it doesn't apply (i.e. non-absolutely-positioned boxes)
  • fall back to auto otherwise

Falling back to zero is almost never what you want here.

@xiaochengh
Copy link
Contributor

What happens when it's used in a calc, like width: calc(anchor-size(--bad-ref width) + 20px)?

@tabatkins
Copy link
Member

More specifically, it should trigger the "behaves as auto" behavior that things like cyclic percentages do.

@xiaochengh
Copy link
Contributor

What happens when it's used in a calc, like width: calc(anchor-size(--bad-ref width) + 20px)?

I found the answer myself. calc-size(auto) works here.

(I was away for a few months and missed so much progress...)

More specifically, it should trigger the "behaves as auto" behavior that things like cyclic percentages do.

It's a bit different from my mental model that a calc() should always keep going (like division-by-zero becomes infinity), and invalid values are resolved only at the top level.

Or are there existing examples where a calc() can be invalidated as long as part of it is invalid, in case I missed something?

@tabatkins
Copy link
Member

Yes, the behavior I'm describing (turning the whole value into "behaves as auto") is already what happens with cyclic percentages. width: calc(20% + 20px) will turn into auto if the parent's size is indeterminate.

@andruud
Copy link
Member

andruud commented Apr 9, 2024

What is the computed value of width:calc(anchor-size(--bad-ref width) + 20px)? Keeping in mind that anchor-size() now resolves at computed-value time.

@chrishtr
Copy link
Contributor

chrishtr commented Apr 9, 2024

What is the computed value of width:calc(anchor-size(--bad-ref width) + 20px)? Keeping in mind that anchor-size() now resolves at computed-value time.

Tab explained it to me offline. The computed value will be a "simplified form" of the specified value, and the used value will be auto. I.e., the effect on layout will be the same as if the author had used width:auto instead.

@tabatkins
Copy link
Member

Yeah, the anchor() reference would just remain unresolved (similar to how layout-dependent %s are left unresolved in the computed value of a calc()). And then the whole value would "behave as auto" during layout, identical to cyclic percentages.

@andruud
Copy link
Member

andruud commented Apr 9, 2024

That is very annoying. We removed the capability of representing anchor*() past computed-value time, this would then be the only reason why that is needed again. But I don't have a better proposal given the circumstances.

@tabatkins Can auto be specified explicitly by the author, or is that triggered only when no fallback is present?

@tabatkins
Copy link
Member

No fallback is the way to specify "this should behave as auto if it fails", yeah.

@andruud
Copy link
Member

andruud commented Apr 9, 2024

Is "behaves as auto" only for width/height? What about min-width/height? And what about max-width/height (does not support auto).

Yeah, the anchor() reference would just remain unresolved [on the computed value]

Do we gain anything from this vs. coercing the computed value into auto?

@andruud
Copy link
Member

andruud commented Apr 9, 2024

Actually, can we just make the bad situations invalid at computed-value time?

@tabatkins
Copy link
Member

We're suggesting the "behaves as auto" just to have consistency with cyclic percentages, which didn't coerce the computed value to auto for Reasons. I don't have any particular reason to prefer one way or another, tho.

Actually, can we just make the bad situations invalid at computed-value time?

Hm, there's a thought! I... think that would be okay? It seems within the remit of that concept.

@andruud
Copy link
Member

andruud commented Apr 10, 2024

Actually, can we just make the bad situations invalid at computed-value time?

Hm, there's a thought! I... think that would be okay? It seems within the remit of that concept.

Then I think we should do that instead of "behaves as auto". Because:

  • We should generally resolve things at the value stage where we have enough information to resolve that thing.
  • It's consistent with the regular behavior of anchor*() resolving computed-value time (interleaving). For percentages-in-sizing, surviving computed-value time is business as usual. Not so for anchor-size(). It would only happen in this error case.
  • We automatically get reasonable behavior for the property in question, also for future properties. ("Behaves as auto" does not work for max-*, which doesn't accept auto).
  • The rendering pipeline after style does not need to know or care about the behavior. It makes it possible to deal with this problem more locally.

Regardless of the chosen approach, it would be good to clarify if the behavior should also apply to anchor(), or if that should stick with a 0px fallback.

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-anchor-position] `anchor-size()` should fallback to `auto`, not zero, and agreed to the following:

  • RESOLVED: if an anchor-size can't resolve and doesn't have a fallback, then it is ICVT
  • RESOLVED: if an anchor() can't resolve and doesn't have a fallback, then it is ICVT
The full IRC log of that discussion <chrishtr> TabAtkins: currently the anchor pos spec says that an anchor-size refers to an invalid anchor then it resolves to 0px. We do the same with the anchor function.
<chrishtr> TabAtkins: fantasai pointed out that for anchor-size in particular this doesn't seem ideal, and would result in a broken page. Suggestion originally proposed was to resolve to auto if there is no valid fallback.
<chrishtr> TabAtkins: later in the thread there was a proposal to describe it as invalid instead of auto, to simplify things. This seems fine to me.
<chrishtr> TabAtkins: there would have had to be special cases though if we went with auto, so invalid is more complete and consistent IMO
<fantasai> sgtm
<Rossen__> q?
<chrishtr> TabAtkins: propose that we go with the "invalid" definition.
<chrishtr> +1 to proposed resolution
<kizu> ICVT sounds good
<TabAtkins> "invalid at computed-value time", specifically
<fantasai> s/invalid instead/"invalid at computed value time" instead/
<chrishtr> emilio: this would be a first case of that happening, which is a bit odd.
<chrishtr> TabAtkins: otherwise calc that contains anchor-size would remove and go to auto; the cyclic cases in other places also were defined to do that
<chrishtr> emilio: if the developer passes auto to calc-size explicitly then it should work once that is defined right?
<emilio> Instead of calc-size(anchor-size(<invalid>) + 20px), calc-size(auto + 20px)
<chrishtr> TabAtkins: auto is fine in calc-size, but that doesn't allow you to put it in arbitrary locations when it can't resolve to a length
<chrishtr> emilio: ok then I'm fine with the ICVT definition
<chrishtr> proposed resolution: if an anchor-size can't resolve and doesn't have a fallback, then it is ICVT
<chrishtr> RESOLVED: if an anchor-size can't resolve and doesn't have a fallback, then it is ICVT
<chrishtr> TabAtkins: anchor makes a bit more sense than anchor-size to resolve to 0px, but for consistency perhaps we should align with the anchor-size resolution
<chrishtr> TabAtkins: propose that we apply the same resolution to the anchor() function as well
<chrishtr> RESOLVED: if an anchor() can't resolve and doesn't have a fallback, then it is ICVT

chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Apr 11, 2024
A recent CSSWG resolution changed the "missing fallback behavior"
from always returning 0px, to instead behaving as invalid at computed-
value time [1]. This is the first time a calc() result can become IACVT,
so we need to make some adjustments for this to be possible.

The approach in this CL does not have the best performance, but it gets
the job done without a massive calc()/ApplyValue code refactor.
In StyleCascade, at the place we currently resolve var() substitutions,
we also check if we need to "resolve" any CSSMathFunctionValues into
IACVT (unset). This is required when the CSSMathFunctionValue contains
anchor() or anchor-size() functions without fallbacks, and when
evaluating any of those produces no result. In order to figure this out,
we have to actually evaluate the queries. This unfortunately means that
we evaluate the queries twice for the time being: once for the validity
check, and again for the real calculation immediately after.

We had an existing function InvolvesAnchorQueries which traversed the
expression tree in full every time, but I changed this to instead
return a boolean stored on the CSSMathExpressionNode itself. Since the
approach is identical to HasComparisons, I renamed to HasAnchorFunctions
for consistency. The reason for this change is to avoid an expensive
InvolvesAnchorQueries check for every CSSMathFunctionValue seen
in the cascade.

Evaluating calc() values with anchor*() inside, even to just check for
validity, needs to happen on scoped values (CSSValue::IsScopedValue).
Previously, we'd scope-ify the value immediately before ApplyPhysical-
Property (which ultimately evaluates any anchor*() functions), but now
we can risk evaluating anchor* () sooner than that. Therefore I had to
split out the tree-scope-ensuring code, and use that during the Resolve
step for the CSSMathFunctionValue.

Finally, the static StyleCascade::Resolve function, which is an API for
resolving arbitrary CSSValues against the computed values of some
element, used kAuthor (by arbitrary choice) as the value's origin.
This conflicts with EnsureScopedValue's reasonable assumption that any
cascade entry with kAuthor is backed by an actual MatchResult entry,
hence I changed this to kNone. This part of the CL should be a "no
behavior change" vs the existing code.

[1] w3c/csswg-drafts#10005

Bug: 333720826
Change-Id: I7e02beb44981fd47a30981ef808af3ec18c1950a
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Apr 11, 2024
A recent CSSWG resolution changed the "missing fallback behavior"
from always returning 0px, to instead behaving as invalid at computed-
value time [1]. This is the first time a calc() result can become IACVT,
so we need to make some adjustments for this to be possible.

The approach in this CL does not have the best performance, but it gets
the job done without a massive calc()/ApplyValue code refactor.
In StyleCascade, at the place we currently resolve var() substitutions,
we also check if we need to "resolve" any CSSMathFunctionValues into
IACVT (unset). This is required when the CSSMathFunctionValue contains
anchor() or anchor-size() functions without fallbacks, and when
evaluating any of those produces no result. In order to figure this out,
we have to actually evaluate the queries. This unfortunately means that
we evaluate the queries twice for the time being: once for the validity
check, and again for the real calculation immediately after.

We had an existing function InvolvesAnchorQueries which traversed the
expression tree in full every time, but I changed this to instead
return a boolean stored on the CSSMathExpressionNode itself. Since the
approach is identical to HasComparisons, I renamed to HasAnchorFunctions
for consistency. The reason for this change is to avoid an expensive
InvolvesAnchorQueries check for every CSSMathFunctionValue seen
in the cascade.

Evaluating calc() values with anchor*() inside, even to just check for
validity, needs to happen on scoped values (CSSValue::IsScopedValue).
Previously, we'd scope-ify the value immediately before ApplyPhysical-
Property (which ultimately evaluates any anchor*() functions), but now
we can risk evaluating anchor* () sooner than that. Therefore I had to
split out the tree-scope-ensuring code, and use that during the Resolve
step for the CSSMathFunctionValue.

Finally, the static StyleCascade::Resolve function, which is an API for
resolving arbitrary CSSValues against the computed values of some
element, used kAuthor (by arbitrary choice) as the value's origin.
This conflicts with EnsureScopedValue's reasonable assumption that any
cascade entry with kAuthor is backed by an actual MatchResult entry,
hence I changed this to kNone. This part of the CL should be a "no
behavior change" vs the existing code.

[1] w3c/csswg-drafts#10005

Bug: 333720826
Change-Id: I7e02beb44981fd47a30981ef808af3ec18c1950a
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Apr 11, 2024
A recent CSSWG resolution changed the "missing fallback behavior"
from always returning 0px, to instead behaving as invalid at computed-
value time [1]. This is the first time a calc() result can become IACVT,
so we need to make some adjustments for this to be possible.

The approach in this CL does not have the best performance, but it gets
the job done without a massive calc()/ApplyValue code refactor.
In StyleCascade, at the place we currently resolve var() substitutions,
we also check if we need to "resolve" any CSSMathFunctionValues into
IACVT (unset). This is required when the CSSMathFunctionValue contains
anchor() or anchor-size() functions without fallbacks, and when
evaluating any of those produces no result. In order to figure this out,
we have to actually evaluate the queries. This unfortunately means that
we evaluate the queries twice for the time being: once for the validity
check, and again for the real calculation immediately after.

We had an existing function InvolvesAnchorQueries which traversed the
expression tree in full every time, but I changed this to instead
return a boolean stored on the CSSMathExpressionNode itself. Since the
approach is identical to HasComparisons, I renamed to HasAnchorFunctions
for consistency. The reason for this change is to avoid an expensive
InvolvesAnchorQueries check for every CSSMathFunctionValue seen
in the cascade.

Evaluating calc() values with anchor*() inside, even to just check for
validity, needs to happen on scoped values (CSSValue::IsScopedValue).
Previously, we'd scope-ify the value immediately before ApplyPhysical-
Property (which ultimately evaluates any anchor*() functions), but now
we can risk evaluating anchor* () sooner than that. Therefore I had to
split out the tree-scope-ensuring code, and use that during the Resolve
step for the CSSMathFunctionValue.

Finally, the static StyleCascade::Resolve function, which is an API for
resolving arbitrary CSSValues against the computed values of some
element, used kAuthor (by arbitrary choice) as the value's origin.
This conflicts with EnsureScopedValue's reasonable assumption that any
cascade entry with kAuthor is backed by an actual MatchResult entry,
hence I changed this to kNone. This part of the CL should be a "no
behavior change" vs the existing code.

[1] w3c/csswg-drafts#10005

Bug: 333720826
Change-Id: I7e02beb44981fd47a30981ef808af3ec18c1950a
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Apr 11, 2024
A recent CSSWG resolution changed the "missing fallback behavior"
from always returning 0px, to instead behaving as invalid at computed-
value time [1]. This is the first time a calc() result can become IACVT,
so we need to make some adjustments for this to be possible.

The approach in this CL does not have the best performance, but it gets
the job done without a massive calc()/ApplyValue code refactor.
In StyleCascade, at the place we currently resolve var() substitutions,
we also check if we need to "resolve" any CSSMathFunctionValues into
IACVT (unset). This is required when the CSSMathFunctionValue contains
anchor() or anchor-size() functions without fallbacks, and when
evaluating any of those produces no result. In order to figure this out,
we have to actually evaluate the queries. This unfortunately means that
we evaluate the queries twice for the time being: once for the validity
check, and again for the real calculation immediately after.

We had an existing function InvolvesAnchorQueries which traversed the
expression tree in full every time, but I changed this to instead
return a boolean stored on the CSSMathExpressionNode itself. Since the
approach is identical to HasComparisons, I renamed to HasAnchorFunctions
for consistency. The reason for this change is to avoid an expensive
InvolvesAnchorQueries check for every CSSMathFunctionValue seen
in the cascade.

Evaluating calc() values with anchor*() inside, even to just check for
validity, needs to happen on scoped values (CSSValue::IsScopedValue).
Previously, we'd scope-ify the value immediately before ApplyPhysical-
Property (which ultimately evaluates any anchor*() functions), but now
we can risk evaluating anchor* () sooner than that. Therefore I had to
split out the tree-scope-ensuring code, and use that during the Resolve
step for the CSSMathFunctionValue.

Finally, the static StyleCascade::Resolve function, which is an API for
resolving arbitrary CSSValues against the computed values of some
element, used kAuthor (by arbitrary choice) as the value's origin.
This conflicts with EnsureScopedValue's reasonable assumption that any
cascade entry with kAuthor is backed by an actual MatchResult entry,
hence I changed this to kNone. This part of the CL should be a "no
behavior change" vs the existing code.

[1] w3c/csswg-drafts#10005

Bug: 333720826
Change-Id: I7e02beb44981fd47a30981ef808af3ec18c1950a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5447517
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
Reviewed-by: Daniil Sakhapov <sakhapov@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1286010}
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Apr 11, 2024
A recent CSSWG resolution changed the "missing fallback behavior"
from always returning 0px, to instead behaving as invalid at computed-
value time [1]. This is the first time a calc() result can become IACVT,
so we need to make some adjustments for this to be possible.

The approach in this CL does not have the best performance, but it gets
the job done without a massive calc()/ApplyValue code refactor.
In StyleCascade, at the place we currently resolve var() substitutions,
we also check if we need to "resolve" any CSSMathFunctionValues into
IACVT (unset). This is required when the CSSMathFunctionValue contains
anchor() or anchor-size() functions without fallbacks, and when
evaluating any of those produces no result. In order to figure this out,
we have to actually evaluate the queries. This unfortunately means that
we evaluate the queries twice for the time being: once for the validity
check, and again for the real calculation immediately after.

We had an existing function InvolvesAnchorQueries which traversed the
expression tree in full every time, but I changed this to instead
return a boolean stored on the CSSMathExpressionNode itself. Since the
approach is identical to HasComparisons, I renamed to HasAnchorFunctions
for consistency. The reason for this change is to avoid an expensive
InvolvesAnchorQueries check for every CSSMathFunctionValue seen
in the cascade.

Evaluating calc() values with anchor*() inside, even to just check for
validity, needs to happen on scoped values (CSSValue::IsScopedValue).
Previously, we'd scope-ify the value immediately before ApplyPhysical-
Property (which ultimately evaluates any anchor*() functions), but now
we can risk evaluating anchor* () sooner than that. Therefore I had to
split out the tree-scope-ensuring code, and use that during the Resolve
step for the CSSMathFunctionValue.

Finally, the static StyleCascade::Resolve function, which is an API for
resolving arbitrary CSSValues against the computed values of some
element, used kAuthor (by arbitrary choice) as the value's origin.
This conflicts with EnsureScopedValue's reasonable assumption that any
cascade entry with kAuthor is backed by an actual MatchResult entry,
hence I changed this to kNone. This part of the CL should be a "no
behavior change" vs the existing code.

[1] w3c/csswg-drafts#10005

Bug: 333720826
Change-Id: I7e02beb44981fd47a30981ef808af3ec18c1950a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5447517
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
Reviewed-by: Daniil Sakhapov <sakhapov@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1286010}
moz-v2v-gh pushed a commit to mozilla/gecko-dev that referenced this issue Apr 19, 2024
…without fallback becomes IACVT, a=testonly

Automatic update from web-platform-tests
[anchor] Invalid anchor()/anchor-size() without fallback becomes IACVT

A recent CSSWG resolution changed the "missing fallback behavior"
from always returning 0px, to instead behaving as invalid at computed-
value time [1]. This is the first time a calc() result can become IACVT,
so we need to make some adjustments for this to be possible.

The approach in this CL does not have the best performance, but it gets
the job done without a massive calc()/ApplyValue code refactor.
In StyleCascade, at the place we currently resolve var() substitutions,
we also check if we need to "resolve" any CSSMathFunctionValues into
IACVT (unset). This is required when the CSSMathFunctionValue contains
anchor() or anchor-size() functions without fallbacks, and when
evaluating any of those produces no result. In order to figure this out,
we have to actually evaluate the queries. This unfortunately means that
we evaluate the queries twice for the time being: once for the validity
check, and again for the real calculation immediately after.

We had an existing function InvolvesAnchorQueries which traversed the
expression tree in full every time, but I changed this to instead
return a boolean stored on the CSSMathExpressionNode itself. Since the
approach is identical to HasComparisons, I renamed to HasAnchorFunctions
for consistency. The reason for this change is to avoid an expensive
InvolvesAnchorQueries check for every CSSMathFunctionValue seen
in the cascade.

Evaluating calc() values with anchor*() inside, even to just check for
validity, needs to happen on scoped values (CSSValue::IsScopedValue).
Previously, we'd scope-ify the value immediately before ApplyPhysical-
Property (which ultimately evaluates any anchor*() functions), but now
we can risk evaluating anchor* () sooner than that. Therefore I had to
split out the tree-scope-ensuring code, and use that during the Resolve
step for the CSSMathFunctionValue.

Finally, the static StyleCascade::Resolve function, which is an API for
resolving arbitrary CSSValues against the computed values of some
element, used kAuthor (by arbitrary choice) as the value's origin.
This conflicts with EnsureScopedValue's reasonable assumption that any
cascade entry with kAuthor is backed by an actual MatchResult entry,
hence I changed this to kNone. This part of the CL should be a "no
behavior change" vs the existing code.

[1] w3c/csswg-drafts#10005

Bug: 333720826
Change-Id: I7e02beb44981fd47a30981ef808af3ec18c1950a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5447517
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
Reviewed-by: Daniil Sakhapov <sakhapov@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1286010}

--

wpt-commits: 039e0b75a1457090a6d0aed1cb10b8997ee8d284
wpt-pr: 45666
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

6 participants