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-5] Allow inherit in calc() (and friends), color-mix(), RCS #9757

Open
LeaVerou opened this issue Dec 30, 2023 · 8 comments
Open

Comments

@LeaVerou
Copy link
Member

LeaVerou commented Dec 30, 2023

In #2864 we resolved to add inherit(property) to CSS, starting from custom properties.

inherit() covers use cases across two main axes:

  1. Using the inherited value of property A in property B (verbatim or with a calculation)
  2. Doing math on the inherited value

However, for 2 we don’t actually need inherit(), simply allowing inherit in calc() would work fine, is more elegant and familiar than inherit(), and being more limited it could likely ship earlier, possibly even for regular properties.

One downside is that unlike inherit(), it does not allow for providing a fallback value.

Use cases

(These are a subset of the use cases for inherit())

Custom property value that depends on parent value

Generic --depth (1, 2)

With inherit():

* {
	--depth: calc(inherit(--depth, 0) + 1);
}

With inherit (note that the code had to be restructured due to the lack of fallback value):

:root {
	--depth: 1;

	* {
		--depth: calc(inherit + 1);
	}
}

See also: #1962

Font metrics relative to parent

With inherit():

strong {
	font-weight: clamp(600, 1.2 * inherit(font-weight), 999);
}

With inherit:

strong {
	font-weight: clamp(600, 1.2 * inherit, 999);
}

And any other numerical typographic metric that can be specified in CSS, e.g. font-stretch, font-style (for variable fonts) etc

A few of the currentBackgroundColor use cases

#5292

Way fewer use cases than inherit() but it could still be useful for creating variations of the parent background color like so:

background-color: color-mix(in oklch, inherit, white 90%);

Matching nested radii for simple cases

#7707

.child {
	padding: 1em;
	border-radius: calc(inherit - 1em);
}

Shipping

Clearly, this doesn't make sense for every property, but then again, calc() also doesn't. Also, we should define that inherit in calc() computes to a value that would be useful, which would be different per property (though we could define a hierarchy, e.g. <number> over <length> if both are options), and IACVT if the computation doesn't make sense. We could ship for custom properties and an allowlist of regular properties to make this more tractable.

@Loirooriol
Copy link
Contributor

Consider

#el { --foo: 1 + 2 }
#el > * { --foo: calc(inherit * 2); z-index: var(--foo) }

should z-index be (1 + 2) * 2 = 6 or 1 + 2 * 2 = 5?

Also, it's not clear if you are proposing this just for custom properties (with the idea to extend it to all properties in the future), or if you want it for all properties now. I'm concerned about the same serialization inconsistencies from #2864.

@LeaVerou
Copy link
Member Author

LeaVerou commented Dec 30, 2023

@Loirooriol

Consider

#el { --foo: 1 + 2 }
#el > * { --foo: calc(inherit * 2); z-index: var(--foo) }

should z-index be (1 + 2) * 2 = 6 or 1 + 2 * 2 = 5?

Not sure where you see the ambiguity. Please note that:

  • var(--foo) refers to the value of --foo on the current element, not the parent
  • There is already language in the custom properties spec that CSS-wide keywords are resolved and not passed around as tokens

In your example, if --foo is unregistered, its inherited value would be the token stream 1 + 2, making --foo on #el > * be calc(1 + 2 * 2), which would be 5. If --foo is registered as a <number> or <integer>, the value is invalid, and thus it would be IACVT, making --foo on the child also IACVT. If custom properties could be registered as <calc-sum>, then it would be inherited as 3, making --foo on the child 6, and thus also z-index would be 6.

Also, it's not clear if you are proposing this just for custom properties (with the idea to extend it to all properties in the future), or if you want it for all properties now. I'm concerned about the same serialization inconsistencies from #2864.

Per my edit, custom properties and a restricted set of regular properties TBD. Worst case we could start from custom properties, but I'm hoping since this is a much more restricted feature, we could broaden scope a bit.

@Loirooriol
Copy link
Contributor

Loirooriol commented Dec 30, 2023

I'm not saying it's ambiguous. I see that 5 results from a simple substitution in the unregistered case, but 6 seems maybe better? The inherited value could be automatically wrapped in parentheses. Less consistent with var(), but kinda makes sense as a calculation, not sure.

@brandonmcconnell
Copy link

brandonmcconnell commented Dec 31, 2023

@LeaVerou I know there's also this proposal (#5055) from @tabatkins for first-valid(), which may fill in the gap for first-valid(inherit, FALLBACK_VALUE).

We would need to ensure that inherit is seen as an invalid value when used inline in this context so that first-valid() falls back to the next value(s), or account for that in its spec.

@LeaVerou
Copy link
Member Author

LeaVerou commented Dec 31, 2023

Not sure how that's related. This is about browser support, not whether a value is set. The fallback parameter in var() (and thus, inherit()) kicks in when a property is not set, not when something is not supported.

@brandonmcconnell
Copy link

brandonmcconnell commented Dec 31, 2023

@LeaVerou using the example from the PR brief:

:root {
  --depth: 1;
  * {
    --depth: calc(first-valid(inherit, 0) + 1);
  }
}

…would be equivalent to this:

:root {
  --depth: 1;
  * {
    --depth: calc(inherit(--depth, 0) + 1);
  }
}

provided this stipulation I mentioned is possible/accurate:

We would need to make sure that inherit is seen as an invalid value when used inline in this context so that first-valid() falls back to the next value(s).

That way, when --depth is not set, first-valid() would have nothing to inherit from the inherit value and thereby fall back to the next value provided, in this case— 0.


That's the relevance explainer. We're still bike shedding the name

@LeaVerou
Copy link
Member Author

LeaVerou commented Jan 1, 2024

@brandonmcconnell Again, that's not how first-valid() works, nor is it available for parts of a property value. CC @tabatkins

@brandonmcconnell
Copy link

brandonmcconnell commented Jan 4, 2024

@LeaVerou Ah I see. Thanks for clarifying that.

some tangential thoughts about if/or to provide a solution for this

I know there's been discussion around an inline if function (i.e. if(condition, ifTrue, ifFalse)).

I wonder if we could use that or something similar to optimize this (down the road).

Something like a logical OR would be ideal here, like or(inherit, FALLBACK). Alternatively, we could overload if() to support this with a 2-arg usage instead of its usual 3-arg usage, but I think that would cause some confusion.

This is a bit unrelated to this issue specifically, so we can save that discussion for a separate ticket once both are implemented.

@fantasai fantasai changed the title [css-values-4] Allow inherit in calc() (and friends), color-mix(), RCS [css-values-5] Allow inherit in calc() (and friends), color-mix(), RCS Jan 9, 2024
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

4 participants