Hi w3c people! I have a proposal about calc()
, var()
and similar functions.
I'd like to be able to inherit the expression itself rather than the computed-once value they contain.
For example if I write calc(1em + 20px)
I expect to get the current font-size added of 20px, rather than the font-size
computed at the time of the very first evaluation of the calc()
expression.
A simple example could be the following:
div{
--foo: 10px;
--border: calc(var(--foo) - 5px); /* --border must always be --foo - 5px */
}
div > p{
--foo: 100px;
/* --border: inherit; /* no effect */
/* --border: var(--border); /* no effect */
border: var(--border) red solid; /* expecting 95px red solid - but got 5px red solid */
}
What do you think? Or maybe this is already possible and I don't know how?
Thank you in advance.
Also related https://bugs.chromium.org/p/chromium/issues/detail?id=850932
(thought it was just a bug, but can see why doesn't work)
I think it's a bit confusing that some things like var()
and inherit
are resolved in the element which has the declaration, but other things like em
units are resolved on the element that uses the variable.
Maybe add a token
or raw
functional notation that accepts a sequence of tokens and represents them literally, without allowing them to be substituted, and without adding variables into the dependency graph. Like --prop: token(inherit)
or --prop: token(var(--foo))
. Then var(--prop)
would remove the token()
and resolve what's inside it.
Would this make sense, syntactically?
letting var() refer to the inherited value of the variable, rather than the current value, is planned for L2.
https://github.com/w3c/csswg-drafts/issues/1594#issuecomment-382832667
This is not currently possible with unregistered custom properties (the default).
If you register the property with syntax: "<length>"
or similar, then --foo: 1em
will resolve based on the font-size
of the declaring element, not the element that finally uses the variable.
One of the plans for Variables 2 is to add a !-syntax for asking the browser to immediately evaluate a custom property as a given type, based on information from the declaring element, like --foo: 1em !type <length>;
, which would let you do this without having to register it.
@jonjohnjohnson That's a different issue entirely, related to letting you "build up" a variable value as you nest things.
I'm not sure I follow, but I really would like to do:
--foo: calc(parent-var(--foo) + 20px);
would work like oop programming languages that support inheritance:
this.foo ~ var(--foo) ie the last available definition
super.foo ~ parent-var(--foo) ie the inherited definition
Yes, that's the "different" thing I was talking about. Plan is to address that in Variables 2, with a syntax like var(--foo parent)
or something.
@tabatkins though I understand this "building up" a variable as you nest is a separate feature, I don't s'pose iteration like...
.el { --index: 1; }
.el ~ .el { --index: calc(var(--index currentValue) + 1); }
...would ever be viable? In line with https://github.com/w3c/csswg-drafts/issues/1026?
this is amazing! thank you tabatkins!
It seems the discussion got derailed to reading the value of a variable from the parent element (#1962) or to force the specified value to be resolved immediately. That's useful for sure, but it doesn't seem to provide a way to avoid resolving var()
and let it be inherited as specified. Unless I'm missing something this is what the first post asked, and I think it can also have its uses.
@jonjohnjohnson I would prefer .el { --index: sibling-index(.el); }
@Loirooriol I know this is off op topic, but my example --index
custom property wasn't solely asking about a literal index, but about building up of any custom property in any degree based upon cascade/selector/sequence.
Ah, I did indeed misread the issue.
The mechanics of "allow a var() to resolve to a CSS-wide keyword" should be the same as the current "var() becomes inherit/initial if it's the guaranteed-invalid value". In other words, seems like it would be fine implementation-wise, it would just need a special syntax to trigger it.
Late-resolving a var() (fetching the custom property value at time of var() resolution) has no mechanical problems either, but it does has some design issues. tokens() would be a "use once" thing - if you build up a variable from other variables, they'll be resolved at the middle stage, rather than at the ends. I guess you could wrap tokens() around itself multiple times, so each substitution stripped one layer off, but that requires a fragile prediction of how much you'll do, and might not even be possible if the build-up is based on depth.
Maybe it's ok that it'll only survive a single substitution? Or maybe it survives all substitutions, and you have to explicitly un-raw it with another function when you want the substitution to happen.
Most helpful comment
Yes, that's the "different" thing I was talking about. Plan is to address that in Variables 2, with a syntax like
var(--foo parent)
or something.