-
Notifications
You must be signed in to change notification settings - Fork 667
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-4] inherit() function: like var() for parent value, for any property #2864
Comments
We probably don't want it to be a function, or at least not a function accepting arbitrary property. Different properties may have different underlying storage / handling, so it could be hard if it uses arbitrary property in arbitrary property. It might be fine to restrict it to properties with only a single numeric value, but I believe in the past we occasionally extend single-value properties into multi-values, so a general mechanism like this may not be future-proofing. |
Also it sounds like a dupe of #2764. |
@FremyCompany mentioned the |
@upsuper Not a dupe of #2764, which is about a special case of this. Could you elaborate a bit on the underlying storage/handling issue? How could it get in the way? Is it theoretical or are there existing examples of properties that would have issues? Also, by future-proofing, are you referring to implementation issues or author code? |
I wrote that mostly concerning the implementation difficulties in mind, since we (in Stylo) use lots of different types to handle subtle differences between properties. But I realized that it is actually not a problem of types, but the differences themselves. For example, Such mechanism has its own issue, that supported type at a value position may depend on the context, and can change as CSS evolves. For example, there is plan to allow division between two dimensions, which may enable
For spec about how to handle it. Actually the above has something related to future-proofing already. |
It doesn't need to retain anything about the type, just serialize the value and use it the same as when |
OK, so I misunderstood the approach you are proposing. This approach is probably better to some extent, although I'm a bit concerned that serialization of computed value of properties may not be as well-defined and interoperable as token stream used by custom properties. |
Discussion with dbaron suggested that the effect would be that you basically just sub in the serialization of the computed value (probably per the well-specified serialization in Typed OM). If the property you're subbing in doesn't work for the property you're subbing into, it would follow the same rules as var(), and make it invalid at computed-value time. |
Shouldn't it be called |
I just came across this issue again looking for something else and was reminded of how useful this would be. @tabatkins wrote
It sounds like there is a clear implementation path, but the discussion just stalled 2 years ago. I promised in the first post that if this is feasible, I could try and collect a list of use cases. Here are a few:
Edit: Added these to OP, for convenience. In terms of how it should behave, a few more thoughts:
|
In general I don't think walking ancestors makes much sense for non-inherited properties. Even for inherited ones it may be undesirable. So I would just return the parent value.
But using the fallback for initial values would be inconsistent with
If this feature is based on serializations, then it doesn't seem problematic to me. Just try to serialize the shorthand, and return that in case of success, or empty string otherwise. When reparsed, empty string can be treated as a guaranteed-invalid value, and use the fallback value if provided, or become IACVT otherwise. |
I suppose that's more predictable (and probably more performant and easier to implement). After all, if one wants the value from a specific ancestor, there's an easy workaround: /* Swap background & foreground in descendants use case: */
.container > * {
--container-background: inherit(background-color);
}
.container * {
color: var(--container-background);
background: inherit(color);
}
The fallback for
I really like this. 💯 |
Ah, yes, that's because the initial value for unregistered custom properties is the guaranteed-invalid value, which is what triggers the fallback. But if you register the property with some other initial value, then |
Sounds like it's already settled, but I'll emphasize anyway that this is something we should definitely not do.
We recently (-ish) invented the equivalent token sequence, so we can just re-use that for this. I've been trying to think about whether or not the new "cross property inheritance" proposed here creates any significant new problems (performance, complexity, etc) for style recalcs. I think it should be fine. It's similar to situations that must anyway be dealt with via This looks like a relatively "easy win", so I'm supportive, FWIW. 👍 |
Since there is implementor interest, I'm adding this to the agenda for the next call so we can resolve about work on it. |
Here's a rad use case with p {
color: color-contrast(
inherit(background-color)
vs
var(--brand-1), var(--brand-2), var(--brand-3)
to AA
);
} On brand, dynamic and high contrast text color on 🤷🏻 |
Thinking about this a bit more, I think it should be called A few more use cases (in addition to those in #2864 (comment) ):
|
Here's my use-case: I regularly will set a CSS variable in a parent element, then use that value for some dimension (like width). Then, in a child element, I'll use that same CSS variable and perform a It would be much nicer if, in those cases, I could skip the CSS variable altogether, and just have the child element reference a parent CSS property value, like for example: |
Working with custom properties, I've wanted this a few times. Here are a few examples (some may duplicate Leas examples above): .examples {
/* change a value locally, without losing context */
--columns: parent(--span);
--span: 3;
width: calc(var(--span) / var(--columns) * 100%);
/* normal properties */
color: color-contrast(parent(background-color) vs black, white);
margin: calc(parent(padding) * -1);
/* more generic patterns */
--alternate: calc(parent(--alternate) * -1);
--count: calc(parent(--count) + 1);
--switch-1: parent(--switch-2);
--switch-2: parent(--switch-1);
} |
I really want this for |
For width and height, I think container query units will probably work much better for most use cases, and they're already supported eveyrwhere! |
Unless I'm misunderstanding, that first requires me to make a container query though, right? I can't just say |
That's my understanding as well. Moreover, in some of my container query testing, I've noticed that setting up a container produces some wonky and unexpected results to the element that is declared as a container, causing its height to essentially zero out, if not explicitly set. Being able to inherit those computed values would be a huge win in this case. Most of the times I've set up a container, I've done so specifically to use its width and height, so this seems like it would be easier to reach for in simpler circumstances like this. Using container queries /* Parent Element */
.parent {
width: 300px;
height: 200px;
container-type: size;
}
/* Child Element */
.child {
width: 50cqw;
height: 50cqh;
} Using inherit() /* Parent Element */
.parent {
width: 300px;
height: 200px;
}
/* Child Element */
.child {
width: calc(inherit(width) / 2);
height: calc(inherit(height) / 2);
} Also, I understand it would be easier on the first pass of this feature to only support CSS custom properties with the |
You just need to set appropriate
This sounds like a potential browser bug?
See above, you wouldn't be inheriting used values with
a) Most use cases for |
I guess there are actually two issues here:
|
That's not a browser bug, it's the result of size containment. In order to create a container for height queries, the height has to be 'contained' and therefor extrinsic. Querying an intrinsic height would result self-referential behavior: the height is based on the contents, which are styled based on the height, etc. So applying a Which gets at a distinction between what these two features would do, which Lea also covered above:
If we want the former, we have to live with the size containment limitation. There's no way to get the actual used dimensions without that limitation. If we want the latter, we can avoid containment… But the examples so far rely on explicit heights anyway. That's roughly what Lea said. But then:
Looking back through the thread, I can't find this stated anywhere. In fact, most of the discussion is about the fact that you would inherit the computed value – which makes sense, because that's how inheritance works. |
I really think those are just container query length units use cases. Exposing them without the containment could lead to circularity. Some other cases could also be handled by Can you provide your use cases to look at, so it would be easier to gauge if they can be done with what we have, or if |
No. Percentages refer to the inline size of the containing block (which is typically established by the parent, but not necessarily). See https://drafts.csswg.org/css-box-4/#propdef-padding
I think you first need to figure out what you are actually trying to do, and then, if if your usecase would benefit from new functionality, open a new issue. |
You're absolutely right on this -- don't know why I mixed these up!
Not sure if you are asking for a specific reduction here or what, but in my experience this is generally difficult to do for cases like this (especially since there's always some workaround). I think just referring to the large number of questions around how to get padding to be a percentage of height should demonstrate a need for wanting to have the same existing functionality as we currently get with percentage, but for height as well as width. In a world where |
I certainly haven't thought through all the ramifications, but I would be surprised if this addition is any more circular than the existing percentages we already have access to. One way to look at it is that all geometry-based |
The key to thinking about this is to think about them not as “width” and “height”, but as “inline” and “block”. These are very different: predominantly, the inline dimension stays the same, while the block one grows. Adding a padding in the inline direction, in a normal flow, reduces the space available for content and does not change the inline dimension of the parent. Adding a padding in the block direction is not constrained, and makes the element and its container grow in the block direction (thus, can lead to circularity). And, because block and inline dimensions are so different, the use-cases for the percentage in them are also different! Percentage in the inline dimension usually reflects the available space: we can make the padding bigger when we have a wider screen (or wider container). What will the padding in a block dimension reflect? |
@tolmasky Then you should file a new issue, since you want to resolve percentages with the actual size of the containing block, while
Yeah it should be fine, in the block axis it's easier for percentages to be cyclic but that can also happen in the inline axis, and the behavior is defined in https://drafts.csswg.org/css-sizing-3/#cyclic-percentage-contribution. In fact, for a while the flexbox spec allowed resolving padding percentages like you want (see #2085), so it's not intrinsically problematic. |
@LeaVerou @mirisuzanne Thanks for clarifying the behavior of inheritance here. It sounds like we might still need to discuss whether those values are computed or not. How would the below example work? .bordered {
border: 1px solid #000;
> .bordered {
border: inherit;
border-width: calc(inherit(border-width) + 1px);
}
} At first glance, I would expect each |
@brandonmcconnell The computed value of border-*-width is an absolute length, so assuming this is based on the serialization of the specified property:
If you want an example that varies depending on whether this uses the computed vs the used value: #foo { height: 100px }
#foo > #bar { height: 50% }
#foo > #bar > #baz { height: inherit(height) } Then I would say that this inherits the computed value:
|
Typo, I meant used values. Have edited now! |
This week I posted two articles: “Alternating Style Queries” and ”Self-Modifying Variables: the |
I've been using the same technique that @kizu describes to work around the lack of |
Another usecase would be essentially enabling #5292 ( i.e. Leveraging |
This is now edited in for custom properties as resolved. Leaving the issue open for the more general discussion. |
[Edit: We've now defined 'inherit()', but only for custom properties. The issue is left open to track the fuller feature.]
We've declined numerous author requests to extend
var()
to arbitrary properties, due to the potential for cycles and how expensive cycle detection would be in the general case. However, providing a way to get the parent or inherited value of any property does not cause cycles and still helps with a ton of use cases.Use cases
Custom property value that depends on parent value
Generic
--depth
(1, 2)See also: #1962
Font metrics relative to parent
And any other numerical typographic metric that can be specified in CSS, e.g.
font-stretch
,font-style
(for variable fonts) etcMany of the
currentBackgroundColor
use cases#5292
While this does not address all use cases (since it would only provide an ancestor background color), it does address quite a few, and offers a workaround for others.
Matching nested radii
#7707
inherit
inherit()
+ calcSwapping foreground and background colors
Decorations visually extending the background
https://twitter.com/HugoGiraudel/status/1350496044681990147
Override parent margins (bleed)
Inherit grandparent value
https://twitter.com/cjw0/status/1350499207648583683
@property
does provide a workaround for this particular case (just register a<length>
custom property and set it on.foo
’s parent), but not in the general case where no suitable registration synstax exists.More use cases
The text was updated successfully, but these errors were encountered: