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-values] Idea to make "var() for regular properties" implementable #9454

Open
LeaVerou opened this issue Oct 10, 2023 · 5 comments
Open

Comments

@LeaVerou
Copy link
Member

LeaVerou commented Oct 10, 2023

The discussion in #7707 gave me an idea. We have this frequent author request for var() to work with regular properties, which we cannot do because cycles (though we have resolved for an ancestor version, see #2864 ).
However, the issues only occur for a small subset of properties, different for each property. For many properties any property would be fine except themselves (and any of their longhands).

So what if we could simply restrict the dependency graph by restricting which properties are allowed based on the property var() is used on?

For example, the use case in #7707 in the simple case (single radii, single box model metrics) can be done like:

border-radius: calc(
	inherit(border-radius) 
	- inherit(padding) - inherit(border-width) 
	- var(margin)
);

(The complex case would simply require four of these)

Some cycle detection would still be needed, to detect cycles with more than 1 step, like:

background: var(color);
color: var(background);

We'd still need to figure out serialization, but we need to do that for inherit() anyway.

Yes, this requires high effort, and is a very complex feature, however there is also very high value. This together with inherit() would unlock a ton of use cases (I can list them if non-obvious, but wanted to check feasibility first).

@Loirooriol
Copy link
Contributor

Note that border-radius can be something like 1px 2px 3px 4px / 5px 6px 7px 8px. What would calc() do with that, invalid at computed-value time? What authors would probably want would be closer to

border-radius:
    calc(inherit(border-top-left-radius-x) - inherit(padding-left) - inherit(border-left-width) - var(margin-left)
    calc(inherit(border-top-right-radius-x) - inherit(padding-right) - inherit(border-right-width) - var(margin-right)
    calc(inherit(border-bottom-right-radius-x) - inherit(padding-right) - inherit(border-right-width) - var(margin-right)
    calc(inherit(border-bottom-left-radius-x) - inherit(padding-left) - inherit(border-left-width) - var(margin-left)
    /
    calc(inherit(border-top-left-radius-y) - inherit(padding-top) - inherit(border-top-width) - var(margin-top)
    calc(inherit(border-top-right-radius-y) - inherit(padding-top) - inherit(border-top-width) - var(margin-top)
    calc(inherit(border-bottom-right-radius-y) - inherit(padding-bottom) - inherit(border-bottom-width) - var(margin-bottom)
    calc(inherit(border-bottom-left-radius-y) - inherit(padding-bottom) - inherit(border-bottom-width) - var(margin-bottom)

(in an hypothetical world with axial longhands for each corner) . So even if this could be helpful in very simple cases, it seems it would make it easy for the authors to shoot themselves in their foot as things get a bit more complex.

And even if this is limited to a few properties, it still has the risk of preventing desirable property dependencies in the future to avoid breaking existing pages.

I think it makes more sense to first work on the serialization problem to generalize inherit(), and defer exploration of this for later.

@LeaVerou
Copy link
Member Author

LeaVerou commented Oct 15, 2023

Note that border-radius can be something like 1px 2px 3px 4px / 5px 6px 7px 8px.

Hence why I wrote "in the simple case (single radii, single box model metrics)"

What would calc() do with that, invalid at computed-value time?

Yup. Same as custom property var(), why would there be a difference?

What authors would probably want would be closer to
[snip]
So even if this could be helpful in very simple cases, it seems it would make it easy for the authors to shoot themselves in their foot as things get a bit more complex.

Sure, but it makes it possible for tools to be built that do the right thing. Or mixin libraries, once we have a mechanism for that.

And even if this is limited to a few properties, it still has the risk of preventing desirable property dependencies in the future to avoid breaking existing pages.

But wouldn't it also eliminate the need for these dependencies?

@Loirooriol
Copy link
Contributor

But wouldn't it also eliminate the need for these dependencies?

No. We add new dependencies all the time. For example, #8407 made content-visibility: auto add the auto keyword to contain-intrinsic-size, and this was a desirable dependency to avoid an unstable layout. Probably not the best example since content-visibility: var(contain-intrinsic-size) doesn't make much sense, and in this case it wouldn't have been a big deal to add the dependency at used-value time. But yeah, I'm concerned about forwards compatibility.

@kizu
Copy link
Member

kizu commented Oct 16, 2023

I remember when I was thinking about this problem, my idea of a compromised solution was to go the other way: to treat the usage of the properties as unregistered custom properties.

My usual test for something like that: is it easy to have a working polyfill that would allow doing this? If we'd think of the values as custom properties, then it is very easy to do so, and it just works: https://codepen.io/kizu/pen/QWzRJjB

Just by replacing every value with a variable, we make it so things “just work”. Of course, the compromise is that we could have, say, two different font-size on .panel and .sub-panel, but even then, if we'd have an ability to get also strip a value out of units, then we could just make the calculation ourselves: https://codepen.io/kizu/pen/MWZdzjx?editors=1100

We will probably lose a number of other things that proper serialization would give us, but I'd say that using non-serialized and just treating them as if they were custom properties (with the same cyclic dependency tracking that already works) would cover most of the use cases.

Especially, if this would only be necessary for the regular var() — with inherited values we won't have any cyclic dependency issues, and the difference in using inherit(padding) vs var(padding) would be easy enough to understand (inherit would give us serialized value, while var() would give us non-serialized).

@Loirooriol
Copy link
Contributor

@kizu Tracking the specified value for these substitutions wouldn't be great if you specify inherit or such. Like padding-left: inherit; something: calc(var(padding-left) + ...) would want the actual computed value for the calculation. Also not great if var(border-left-width) is e.g. 3px but the actual computed value is 0px because of border-left-style: none.

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

3 participants