Open schenney-chromium opened 9 months ago
What about a combination where you look at the originating element and then the highlight pseudo of the parent until you end up at the root? That would be least confusing to authors.
For example, this markup:
<html>
<body>
<div>
<p>…</p>
</div>
</body>
<html>
When selecting text in the paragrph, this would be the lookup chain:
Inheriting through both originating ancestors and highlight ancestors is possible to implement, I think.
On the other hand, I'm reconsidering this proposal anyway because of the memory costs of inheriting so much stuff, plus the potential confusion of where properties are coming from (DevTools support would need to change in Chrome, for instance).
And making the changes either of us suggest does not fix all website breakage. There's no way of getting around sites that have a foo::selection that they only expect to apply to foo, and not its decendents.
I'll ponder this some more and maybe try to figure out how much stuff is broken due to which aspect of the change: custom properties and/or unexpected inheritance.
A model with less load could be to inherit:
The root highlights would still inherit custom properties from :root
in this model.
For example, this markup:
<html>
<body>
<div>
<p>…</p>
</div>
</body>
<html>
When selecting text in the paragrph, this would be the lookup chain:
This would still allow authors to set a custom property on an intermediary element. For example, if they set --color
on <div>
, the <p>
would end up inheriting that, and thus the p::selection
would be able to use --color
.
A model with less load could be to inherit:
- custom properties from the originating element
- properties from the highlight tree
The root highlights would still inherit custom properties from
:root
in this model.
Yeah, that's what I was considering implementing. The nasty part is that one would need to merge the custom properties from the originating element and the highlight parent, with the originating winning any conflicts. It's doable for sure but could do a lot of damage to style resolution performance in the presence of highlight pseudos, and I'm loath to move forward unless there is measurable benefit in the wild.
I'm loath to move forward unless there is measurable benefit in the wild
Measurable benefit as in benefit to developers? Or benefits such as performance-related ones?
Thinking a bit further on all this, I think maybe a general rule for all pseudo-elements could be that they inherit custom properties (only custom ones) from their originating element at the minimum. Specific pseudo elements can then further specify what they do or don’t do. That would keep it practical and predictable to authors.
Coming to this late, I was wondering if the problem statement could be clarified a bit. The first post makes it sound like highlight pseudos don't currently have access to custom property values defined on their originating element or its ancestors, but I'm sure that can't be it, because that would render them almost unusable?
That is currently the specified behavior: highlight pseudos inherit from the highlight chain, with the root highlight inheriting from root.
This is, as you say, unusable and is also what made us postpone shipping this currently specced (and implemented behind a flag) behavior in Chrome.
I made a demo to understand the current situation: https://codepen.io/leaverou/pen/poBzRVP
It looks like custom properties specified on :root
are applied, but nothing below that, even if it contains the entire element. That’s …very unfortunate and severely restricts use cases for this.
As an example, I was planning to base Prism v2 (one of the most popular syntax highlighters on the Web) on the CSS custom highlight API, and now I’m not even sure it's possible at all, since different languages need different colors so these cannot just be defined on the root.
That’s why I proposed this:
I think maybe a general rule for all pseudo-elements could be that they inherit custom properties (only custom ones) from their originating element at the minimum. Specific pseudo elements can then further specify what they do or don’t do.
For highlights, in addition to inheriting custom props from the originating element, they could also walk up the highlight tree in addition to that.
My understanding from @schenney-chromium's initial comment in this issue was that this is strictly a backwards-compatibility problem with the current ::selection
behavior, since the new highlight inheritance is a breaking change when applied to best practices around the current behavior.
That shouldn't be a blocker with CSS custom highlights since they've only ever used the new inheritance behavior. So I'm a little unclear on this point:
It looks like custom properties specified on :root are applied, but nothing below that, even if it contains the entire element. That’s …very unfortunate and severely restricts use cases for this.
As an example, I was planning to base Prism v2 (one of the most popular syntax highlighters on the Web) on the CSS custom highlight API, and now I’m not even sure it's possible at all, since different languages need different colors so these cannot just be defined on the root.
This use case can still be realized by defining the colors on the ::highlight()
pseudos for elements below :root
. This adoption of @LeaVerou's demo shows property definitions being scoped under particular elements: https://codepen.io/daniec/pen/MWRgENy. It's a little clunky but this kind of scoping is definitely possible.
A model with less load could be to inherit:
- custom properties from the originating element
- [standard] properties from the highlight tree
The root highlights would still inherit custom properties from :root in this model.
I agree this idea makes sense. But the inheritance path in the example that follows doesn't. E.g. color
and background-color
need to inherit purely through the highlight pseudo-tree, without looking to the originating element at all, for things like section::selection { color: blue; background: yellow; }
to work reasonably. You can't have a random em { color: red; }
in the middle of the section break selection colors within the em
.
The CSS Working Group just discussed [CSS Pseudo] Revisit CSS Custom Properties in highlight pseudos
, and agreed to the following:
RESOLVED: custom properties don't apply to the highlight pseudos. On highlight pseudos, the var() function takes from the originating element.
FWIW I ran into this today and damn, that’s some bizarre behavior if I've ever seen one.
This doesn’t work:
::highlight(mark-green) {
--color: var(--color-green);
background-color: color-mix(in oklab, canvas, var(--color));
}
This also doesn't work:
::highlight(mark-green) {
--color: var(--color-green);
}
::highlight(mark-green) {
background-color: color-mix(in oklab, canvas, var(--color));
}
This does work (--color-green
is defined on :root
):
::highlight(mark-green) {
background-color: color-mix(in oklab, canvas, var(--color-green));
}
Can we please fix this? This is really bad lol.
Yeah, simple things seem like they should work ...
Start with the premise that to ever launch this we need to support the existing method, as best we can ascertain, of styling selection. That made use of custom properties to achieve a sensible highlight inheritance model, which is indeed the model we are trying to launch now. Authors would define custom properties on the elements, which would be inherited through the elements, and then use the properties in the selection. I have a write up at https://blogs.igalia.com/schenney/css-custom-properties-in-highlight-pseudos/
This existing method relies on highlights inheriting custom properties from their originating element.
To understand why we decided to ignore custom properties defined on the highlight itself, consider this.
<style>
p { --color: blue; }
p::highlight { --color: green; color: var(--color); }
em { --color: red; }
em::highlight { color: var(--color); }
</style>
<p>Some text <em>which color?</em></p>
Which --color should the em highlight use? One expects properties to inherit through the highlight chain, arguing for green. But we have made a very explicit web-compat decision to inherit custom properties from originating elements, arguing for red.
Managing the potential confusion was a goal, and also making implementations not-too-inefficient. The resolution is that the least confusing, most web-compat design is to inherit from originating and not allow custom properties in highlights.
This fits with a broader general principle for property values in highlight pseudos: When a value depends on information not available in the highlight (font size, container size, custom properties) the information is taken from the originating element.
If we’re talking purely ergonomics, the answer seems simple: the em highlight uses red, unless some of it spans outside the <em>
in which case that part only uses green, essentially treating ::highlight
the same as a child encompassing the element content in terms of inheritance.
If that’s not possible to implement we can talk about how to reduce requirements from there to make it implementable, but I don't think disallowing custom properties from ::highlight
altogether is a good compromise. I suspect folks had not realized the full implications of the Mar 7 resolution when they resolved that way. Ironically, I had sent regrets for that call saying:
I would really like to be there for 9909, but I also don't want to delay it further and end up with web compat forcing our hand. :/
Is it too late to revisit? If we can implement what I’m proposing in the first paragraph there should be no webcompat issue since custom properties are currently dropped entirely (and also since I don't think the CSS Highlight API is used much still).
I'll need to look into how an implementation of "use custom properties from the highlight itself but inherit others from the originating element" would work. If I recall correctly, the problem was at the point we need to copy properties from the originating element at which point we don't know which custom properties on the highlight was inherited or from the highlight itself.
The use case seems to be overriding the color on just one highlight easily when a site's general design is to use custom properties. But I don't know why you would do that in production when you could just replace the var on the one highlight with a specific color.
Anyways, I will look into achieving the proposed change.
Problem indeed was with all the properties needing to get copied over.
The updated resolution was made so that we would prevent from ending up with authors declaring all their custom properties on *::selection
as well in order to make this work – a practice they now already do for generated content, i.e. *, *::after, *::before
.
In Issue #6641 the issue of CSS Custom Properties used in highlight pseudos, particularly selection, was discussed. The motivating use case was the heavy uses of custom properties to define the colors for selection highlights, and these custom properties were typically defined on :root.
The resolution was to copy custom properties from :root to :root::selection (and other highlights).
In trying to enable Highlight Inheritance for ::selection in chromium, it has come to my attention that custom properties are the recommended way to achieve the behavior of highlight inheritance in the absence of an implementation of highlight inheritance. And this pre-existing workaround breaks when highlight inheritance is actually implemented.
This is a simple example of how custom properties are used:
With the non-spec-compliant originating inheritance implemented by all browsers today, all of the transparent
<p>
is transparent when selected, including the descendant<strong>
element. The corresponding example with highlight inheritance requires this:The original example with highlight inheritance renders both paragraphs as grey when selected.
The use of custom properties for selection is out there in the world right now. In particular, developers looking to style selection may well end up on Stack Overflow where they find things like this:
Setting a custom property on inline style to control selection from javascript Controlling selection colors through JS "CSS custom properties / CSS Variables will be a better approach"
As I understand things, it highlights a use case that will continue to exist once highlight inheritance is shipped: You can't set pseudo elements in inline style, so the only way to control the highlights with inline style is through custom properties.
I hate to say this, but I propose option (1) we resolve on inheriting custom properties from the originating element for highlight pseudos.
The alternative, I think , is option (2) to special case ::selection and continue to use originating inheritance as implemented by browsers today, and only use highlight inheritance for the other highlight pseudos. In my opinion that's worse that option 1 given there is some utility in inheriting custom properties for these pseudo elements too.