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

Support Custom Pseudo-elements #300

Closed
hayatoito opened this Issue Sep 1, 2015 · 74 comments

Comments

Projects
None yet
@hayatoito
Member

hayatoito commented Sep 1, 2015

See the proposal from @philipwalton.
https://github.com/w3c/webcomponents/blob/gh-pages/proposals/Custom-Pseudo-Elements.md

Let me file an issue here to discuss and keep track of the proposal.

@hayatoito hayatoito added the shadow-dom label Sep 1, 2015

@hayatoito

This comment has been minimized.

Show comment
Hide comment
@hayatoito

hayatoito Sep 1, 2015

Member

At the first glance, the proposal sounds good to me.
I think we would have more questions and answers for detail, however, before proceeding, I'd like to hear more opinions from other people.

@tabatkins, WDYT if you have a chance to take a look?

Member

hayatoito commented Sep 1, 2015

At the first glance, the proposal sounds good to me.
I think we would have more questions and answers for detail, however, before proceeding, I'd like to hear more opinions from other people.

@tabatkins, WDYT if you have a chance to take a look?

@rniwa

This comment has been minimized.

Show comment
Hide comment
@rniwa

rniwa Sep 1, 2015

Contributor

We would prefer using part syntax as follows so that future pseudo element names we introduce in CSS wouldn't interfere with author defined parts components expose:

<date-range-selector>
  <!-- #shadow-root -->
    <div id="container">
      <input part="start-date" id="start-date" type="date">
      <input part="end-date" id="end-date" type="date">
    </div>
  <!-- /shadow-root -->
</date-range-selector>
date-range-selector:part(start-date),
date-range-selector:part(end-date) {
  /* normal styles */
}

@hober @othermaciej

Contributor

rniwa commented Sep 1, 2015

We would prefer using part syntax as follows so that future pseudo element names we introduce in CSS wouldn't interfere with author defined parts components expose:

<date-range-selector>
  <!-- #shadow-root -->
    <div id="container">
      <input part="start-date" id="start-date" type="date">
      <input part="end-date" id="end-date" type="date">
    </div>
  <!-- /shadow-root -->
</date-range-selector>
date-range-selector:part(start-date),
date-range-selector:part(end-date) {
  /* normal styles */
}

@hober @othermaciej

@tabatkins

This comment has been minimized.

Show comment
Hide comment
@tabatkins

tabatkins Sep 2, 2015

Member

Yeah, if we do add custom pseudo-elements, we need to do it namespaced, like ::part.

That said, the CSSWG provisionally accepted my @apply rule draft, which makes pseudo-elements unnecessary for WC.

That is, CSS variables handle most of the styling needs of WC already. They only fall down when you want to offer the ability to arbitrarily style an element; there are too many properties to make it reasonable to offer enough variables for styling. Pseudo-elements expose an element for arbitrary styling, so that's useful. The @apply rule also does that, tho, and via the existing mechanism of CSS custom properties, so there's probably no need to add custom pseudo-elements.

Member

tabatkins commented Sep 2, 2015

Yeah, if we do add custom pseudo-elements, we need to do it namespaced, like ::part.

That said, the CSSWG provisionally accepted my @apply rule draft, which makes pseudo-elements unnecessary for WC.

That is, CSS variables handle most of the styling needs of WC already. They only fall down when you want to offer the ability to arbitrarily style an element; there are too many properties to make it reasonable to offer enough variables for styling. Pseudo-elements expose an element for arbitrary styling, so that's useful. The @apply rule also does that, tho, and via the existing mechanism of CSS custom properties, so there's probably no need to add custom pseudo-elements.

@philipwalton

This comment has been minimized.

Show comment
Hide comment
@philipwalton

philipwalton Sep 2, 2015

Member

@tabatkins I'm aware of the @apply rule (it was discussed in the issue that prompted this proposal), and I think in general using @apply with custom properties is the best way to style third-party elements.

However, I brought up several use-cases in the proposal that I think are legitimate and that I don't think the @apply rule can handle (unless I'm misunderstanding part of its functionality).

You said:

Pseudo-elements expose an element for arbitrary styling, so that's useful. The @apply rule also does that [...]

Can you go into more detail here? How would the @apply rule handle the styling of an input element only in the :focus, :enabled, and :out-of-range state? As far as I can tell, the only way is for the element author to hard-code that selector in the element's shadow styles. Something like:

input {
  /* normal input styles */
  @apply --date-range-selector-inputs;
}
input:focus:enabled:out-of-range {
  /* input styles for the focus, enabled, and out-of-range states */
  @apply --date-range-selector-inputs-focused-enabled-out-of-range;
}

It seems unreasonable for component authors to have to anticipate and hard-code every element state their users may ever want to style. Exposing the element itself is much simpler and still maintains the privacy of other element in the component (unlike ::shadow/>>>).

Member

philipwalton commented Sep 2, 2015

@tabatkins I'm aware of the @apply rule (it was discussed in the issue that prompted this proposal), and I think in general using @apply with custom properties is the best way to style third-party elements.

However, I brought up several use-cases in the proposal that I think are legitimate and that I don't think the @apply rule can handle (unless I'm misunderstanding part of its functionality).

You said:

Pseudo-elements expose an element for arbitrary styling, so that's useful. The @apply rule also does that [...]

Can you go into more detail here? How would the @apply rule handle the styling of an input element only in the :focus, :enabled, and :out-of-range state? As far as I can tell, the only way is for the element author to hard-code that selector in the element's shadow styles. Something like:

input {
  /* normal input styles */
  @apply --date-range-selector-inputs;
}
input:focus:enabled:out-of-range {
  /* input styles for the focus, enabled, and out-of-range states */
  @apply --date-range-selector-inputs-focused-enabled-out-of-range;
}

It seems unreasonable for component authors to have to anticipate and hard-code every element state their users may ever want to style. Exposing the element itself is much simpler and still maintains the privacy of other element in the component (unlike ::shadow/>>>).

@tabatkins

This comment has been minimized.

Show comment
Hide comment
@tabatkins

tabatkins Sep 2, 2015

Member

You're right, this doesn't handle pseudo-classes well. I'll have to give that some thought.

Member

tabatkins commented Sep 2, 2015

You're right, this doesn't handle pseudo-classes well. I'll have to give that some thought.

@morewry

This comment has been minimized.

Show comment
Hide comment
@morewry

morewry Sep 28, 2015

I'm just a user, but, in my opinion, there is a need for pseudo-element functionality from a perspective of allowing a component author to provide a stable public interface for styling a component. As a component author, I should be able to change the internal implementation details of my web component without any disruption to a component user. The only way I can feasibly do that is to have an explicit mechanism for defining my publicly style-able surface. Custom pseudo elements do this. While @apply appears to actually do so as well, custom pseudo elements have benefits beyond lack of difficulty with pseudo classes.

Vendor elements that utilize obscured internals have long allowed styling of certain portions via pseudo elements. And, correct me if I'm wrong, isn't there some work on standardizing these for common replaced elements, like form controls? Therefore, with custom pseudo elements, a user authored component would be style-able like a vendor component. Having user authored elements feel like native elements is a good thing--and not only because having the standard explain the existing platform is nice.

A component user does not have to learn any new language constructs to style a custom pseudo element, and yet can still enhance their use of a custom pseudo element with new CSS features as they develop. While I am enthusiastic about bringing popular pre-processor features (and implied enhancements, like @apply) to CSS, I am also of the opinion that, in respect to web component styles, we should lean toward allowing CSS authors to simply...write familiar CSS. Since several of the CSS standards are brand spanking new, surely they would have kinks needing to be worked out? As a result, it seems to me that it would be putting the cart before the horse to hang the style story for Shadow DOM on them.

morewry commented Sep 28, 2015

I'm just a user, but, in my opinion, there is a need for pseudo-element functionality from a perspective of allowing a component author to provide a stable public interface for styling a component. As a component author, I should be able to change the internal implementation details of my web component without any disruption to a component user. The only way I can feasibly do that is to have an explicit mechanism for defining my publicly style-able surface. Custom pseudo elements do this. While @apply appears to actually do so as well, custom pseudo elements have benefits beyond lack of difficulty with pseudo classes.

Vendor elements that utilize obscured internals have long allowed styling of certain portions via pseudo elements. And, correct me if I'm wrong, isn't there some work on standardizing these for common replaced elements, like form controls? Therefore, with custom pseudo elements, a user authored component would be style-able like a vendor component. Having user authored elements feel like native elements is a good thing--and not only because having the standard explain the existing platform is nice.

A component user does not have to learn any new language constructs to style a custom pseudo element, and yet can still enhance their use of a custom pseudo element with new CSS features as they develop. While I am enthusiastic about bringing popular pre-processor features (and implied enhancements, like @apply) to CSS, I am also of the opinion that, in respect to web component styles, we should lean toward allowing CSS authors to simply...write familiar CSS. Since several of the CSS standards are brand spanking new, surely they would have kinks needing to be worked out? As a result, it seems to me that it would be putting the cart before the horse to hang the style story for Shadow DOM on them.

@tabatkins

This comment has been minimized.

Show comment
Hide comment
@tabatkins

tabatkins Sep 28, 2015

Member

Efforts to standardize the internal pseudo-elements aren't very successful, unfortunately. I keep trying to get something done with that, but it's very difficult to do in a reasonable way. I'm not confident that it will ever be achieved.

Mixins and Nesting are battle-tested concepts with long histories from CSS preprocessors; their mixture, especially, has a long history of use in Sass (and maybe more?). These aren't new concepts, they're just new to vanilla CSS. And they lean on existing useful and well-tested concepts from CSS, like inheritance and properties.

Custom pseudo-elements, on the other hand, don't. We've had pseudo-elements for a long time, but only a small number, and they don't nest. We still need to figure out how to solve a number of issues before it's usable:

  1. Are shadow-pseudos more like classes or IDs? That is, do they need to be unique, or is it ok to have multiple selected by the same name? If you can have multiple, how do we select a single one to style? Does the component author have to allow this? Can a single element expose multiple pseudo names for itself?
  2. How do we handle both a parent and child exposing themselves as pseudo-elements? Is the nesting visible or not? If yes, can you write ::foo::bar?
  3. (This one's important and hard.) If a component contains other components, and wants to expose some of its sub-components parts as pseudo-elements, how does it surface them? Are they selectable by default somehow (if so, how do you handle namespacing)? If not, how do you expose them? Is it apparent that they come from a sub-component, or can the parent component make them look "native"?

All of these questions apply to vars/@apply/nesting too, but they all have definite, simple answers there, and those answers are imo reasonable behavior. We have no idea what the answers would be for pseudo-elements, tho.

This is why it's backwards to characterize vars/@apply/nesting as "new and untested" and pseudo-elements as "old and well-known". It's almost exactly the opposite when you dig into details.

Member

tabatkins commented Sep 28, 2015

Efforts to standardize the internal pseudo-elements aren't very successful, unfortunately. I keep trying to get something done with that, but it's very difficult to do in a reasonable way. I'm not confident that it will ever be achieved.

Mixins and Nesting are battle-tested concepts with long histories from CSS preprocessors; their mixture, especially, has a long history of use in Sass (and maybe more?). These aren't new concepts, they're just new to vanilla CSS. And they lean on existing useful and well-tested concepts from CSS, like inheritance and properties.

Custom pseudo-elements, on the other hand, don't. We've had pseudo-elements for a long time, but only a small number, and they don't nest. We still need to figure out how to solve a number of issues before it's usable:

  1. Are shadow-pseudos more like classes or IDs? That is, do they need to be unique, or is it ok to have multiple selected by the same name? If you can have multiple, how do we select a single one to style? Does the component author have to allow this? Can a single element expose multiple pseudo names for itself?
  2. How do we handle both a parent and child exposing themselves as pseudo-elements? Is the nesting visible or not? If yes, can you write ::foo::bar?
  3. (This one's important and hard.) If a component contains other components, and wants to expose some of its sub-components parts as pseudo-elements, how does it surface them? Are they selectable by default somehow (if so, how do you handle namespacing)? If not, how do you expose them? Is it apparent that they come from a sub-component, or can the parent component make them look "native"?

All of these questions apply to vars/@apply/nesting too, but they all have definite, simple answers there, and those answers are imo reasonable behavior. We have no idea what the answers would be for pseudo-elements, tho.

This is why it's backwards to characterize vars/@apply/nesting as "new and untested" and pseudo-elements as "old and well-known". It's almost exactly the opposite when you dig into details.

@philipwalton

This comment has been minimized.

Show comment
Hide comment
@philipwalton

philipwalton Sep 29, 2015

Member

Are shadow-pseudos more like classes or IDs? That is, do they need to be unique, or is it ok to have multiple selected by the same name? If you can have multiple, how do we select a single one to style? Does the component author have to allow this? Can a single element expose multiple pseudo names for itself?

I imagined them as being non-unique, like classes. It's extremely likely that component authors will create elements with many children of the same type, like a <ul> element does today.

Though that raises the question (at least in my mind) of whether or not to allow a single HTML element to have more than one pseudo element name. To continue with the <date-range-selector> element example, the start and end date inputs are both inputs, and will probably want to be styled similarly, but they also might want to be styled individually. Should it be possible to select the start date field via both ::start-date and ::field (or whatever). Perhaps by allowing space-separated pseudo attribute name definitions: e.g. <input pseudo="field start-date">.

How do we handle both a parent and child exposing themselves as pseudo-elements? Is the nesting visible or not? If yes, can you write ::foo::bar?

(This one's important and hard.) If a component contains other components, and wants to expose some of its sub-components parts as pseudo-elements, how does it surface them? Are they selectable by default somehow (if so, how do you handle namespacing)? If not, how do you expose them? Is it apparent that they come from a sub-component, or can the parent component make them look "native"?

I think this paradigm can be simplified to just components and their sub-components, rather than thinking of it in terms of a possibly infinitely nested tree of shadow components (ala /deep/). As long as the component author exposes the sub-component via a custom pseudo-element, it shouldn't matter how many levels deep it's nested. For example:

<x-foo>
  <!-- #shadow-root -->
    <x-bar pseudo="x-bar-item">
  <!-- /shadow-root -->
</x-foo>

If <x-bar> exposes its own ::x-baz-item pseudo-element, then consumers of <x-foo> could theoretically style ::x-baz-item pseudo-elements from a main document stylesheet via something like:

x-foo::x-bar-item::x-baz-item {
  color: red;
}

This paradigm allows for /deep/-like styling without the performance implications or the exposing of component internals.

Member

philipwalton commented Sep 29, 2015

Are shadow-pseudos more like classes or IDs? That is, do they need to be unique, or is it ok to have multiple selected by the same name? If you can have multiple, how do we select a single one to style? Does the component author have to allow this? Can a single element expose multiple pseudo names for itself?

I imagined them as being non-unique, like classes. It's extremely likely that component authors will create elements with many children of the same type, like a <ul> element does today.

Though that raises the question (at least in my mind) of whether or not to allow a single HTML element to have more than one pseudo element name. To continue with the <date-range-selector> element example, the start and end date inputs are both inputs, and will probably want to be styled similarly, but they also might want to be styled individually. Should it be possible to select the start date field via both ::start-date and ::field (or whatever). Perhaps by allowing space-separated pseudo attribute name definitions: e.g. <input pseudo="field start-date">.

How do we handle both a parent and child exposing themselves as pseudo-elements? Is the nesting visible or not? If yes, can you write ::foo::bar?

(This one's important and hard.) If a component contains other components, and wants to expose some of its sub-components parts as pseudo-elements, how does it surface them? Are they selectable by default somehow (if so, how do you handle namespacing)? If not, how do you expose them? Is it apparent that they come from a sub-component, or can the parent component make them look "native"?

I think this paradigm can be simplified to just components and their sub-components, rather than thinking of it in terms of a possibly infinitely nested tree of shadow components (ala /deep/). As long as the component author exposes the sub-component via a custom pseudo-element, it shouldn't matter how many levels deep it's nested. For example:

<x-foo>
  <!-- #shadow-root -->
    <x-bar pseudo="x-bar-item">
  <!-- /shadow-root -->
</x-foo>

If <x-bar> exposes its own ::x-baz-item pseudo-element, then consumers of <x-foo> could theoretically style ::x-baz-item pseudo-elements from a main document stylesheet via something like:

x-foo::x-bar-item::x-baz-item {
  color: red;
}

This paradigm allows for /deep/-like styling without the performance implications or the exposing of component internals.

@philipwalton

This comment has been minimized.

Show comment
Hide comment
@philipwalton

philipwalton Sep 29, 2015

Member

As I was writing that last post, I thought of a possible solution to the pseudo-class/@apply issue. Borrowing from Sass, again, pseudo-element states could be defined in the custom property definition via the &:pseudo-class nesting syntax.

Here's what it could look like:

:root {
  --x-foo-styles: {
    color: blue;

    &:hover {
      background-color: yellow;
    }
    &:active {
      outline: 1px dotted;
    }
  }
}

This, if @apply-ed in a shadow <style> declaration, would only be applied when the pseudo-class states matched.

There are definitely some specificity/cascade issues to iron out, but if this could work, then I believe all my concerns about custom property shortcomings would be alleviated.

Member

philipwalton commented Sep 29, 2015

As I was writing that last post, I thought of a possible solution to the pseudo-class/@apply issue. Borrowing from Sass, again, pseudo-element states could be defined in the custom property definition via the &:pseudo-class nesting syntax.

Here's what it could look like:

:root {
  --x-foo-styles: {
    color: blue;

    &:hover {
      background-color: yellow;
    }
    &:active {
      outline: 1px dotted;
    }
  }
}

This, if @apply-ed in a shadow <style> declaration, would only be applied when the pseudo-class states matched.

There are definitely some specificity/cascade issues to iron out, but if this could work, then I believe all my concerns about custom property shortcomings would be alleviated.

@rniwa

This comment has been minimized.

Show comment
Hide comment
@rniwa

rniwa Sep 29, 2015

Contributor

Like we agreed during the last F2F, custom properties, @apply, and custom pseudo elements aren't conflicting ideas. They address slightly different use cases and both can be supported.

In particular, in an isolated web component case where neither component nor its container document trusts each other, we can't expose all mixins defined in the container document into the shadow tree.

Also, there is an added benefit of developer familiarity with custom pseudo elements. While the technical problems faced by nested rules and @apply might be similar (or even simpler as you claim) to those faced by custom pseudo elements, relying solely on three brand new CSS syntax seems like a lot of cognitive load.

Contributor

rniwa commented Sep 29, 2015

Like we agreed during the last F2F, custom properties, @apply, and custom pseudo elements aren't conflicting ideas. They address slightly different use cases and both can be supported.

In particular, in an isolated web component case where neither component nor its container document trusts each other, we can't expose all mixins defined in the container document into the shadow tree.

Also, there is an added benefit of developer familiarity with custom pseudo elements. While the technical problems faced by nested rules and @apply might be similar (or even simpler as you claim) to those faced by custom pseudo elements, relying solely on three brand new CSS syntax seems like a lot of cognitive load.

@rniwa

This comment has been minimized.

Show comment
Hide comment
@rniwa

rniwa Sep 29, 2015

Contributor

Also, where is the proposal for nesting syntax? Is it https://lists.w3.org/Archives/Public/www-style/2011Jun/0022.html ?

Contributor

rniwa commented Sep 29, 2015

Also, where is the proposal for nesting syntax? Is it https://lists.w3.org/Archives/Public/www-style/2011Jun/0022.html ?

@othermaciej

This comment has been minimized.

Show comment
Hide comment
@othermaciej

othermaciej Sep 29, 2015

Member

I think custom pseudos are a good direction. Comments on some specific issues:

(1) It's critical for component authors to be able to whitelist the set of style properties that can be applied to an exposed named part.
(2) I'm somewhat partial to :part syntax because then we never have to worry about namespace collisions with future CSS built-in pseudos.
(3) I don't think prefixing is necessary. If pseudo names are reused between different custom elements, that's not a problem, because you can specify the element and part.
(4) I don't think @apply plus the increasing series of other features that need to come along with it is a replacement. The syntax is pretty inscrutable. I immediately understand what Philip's named part examples are doing, but I don't understand what his @apply + nesting proposal does.
(5) Having a nice and easy to understand syntax for consumers of a component is important. Unlike many other aspects of web components, this syntax will be used by many garden-variety web developers, not just authors of JavaScript frameworks.

Member

othermaciej commented Sep 29, 2015

I think custom pseudos are a good direction. Comments on some specific issues:

(1) It's critical for component authors to be able to whitelist the set of style properties that can be applied to an exposed named part.
(2) I'm somewhat partial to :part syntax because then we never have to worry about namespace collisions with future CSS built-in pseudos.
(3) I don't think prefixing is necessary. If pseudo names are reused between different custom elements, that's not a problem, because you can specify the element and part.
(4) I don't think @apply plus the increasing series of other features that need to come along with it is a replacement. The syntax is pretty inscrutable. I immediately understand what Philip's named part examples are doing, but I don't understand what his @apply + nesting proposal does.
(5) Having a nice and easy to understand syntax for consumers of a component is important. Unlike many other aspects of web components, this syntax will be used by many garden-variety web developers, not just authors of JavaScript frameworks.

@tabatkins

This comment has been minimized.

Show comment
Hide comment
@tabatkins

tabatkins Sep 30, 2015

Member

@philipwalton (re: nesting) Yes, that's precisely what I was getting at when I was talking about @apply/nesting. ^_^ There's no specificity issues to iron out - It Just Works™.

@rniwa

Like we agreed during the last F2F, custom properties, @apply, and custom pseudo elements aren't conflicting ideas. They address slightly different use cases and both can be supported.

No, they don't address slightly different use-cases. They address the exact same use-cases.

In particular, in an isolated web component case where neither component nor its container document trusts each other, we can't expose all mixins defined in the container document into the shadow tree.

Sure we can. And when you do want to block, that's what all: initial is for. (Alternately, an equivalent of all for just custom properties has been proposed; it would be called --, as in --: initial.) You can then let particular custom properties thru with --foo: inherit.

Also, there is an added benefit of developer familiarity with custom pseudo elements. While the technical problems faced by nested rules and @apply might be similar (or even simpler as you claim) to those faced by custom pseudo elements, relying solely on three brand new CSS syntax seems like a lot of cognitive load.

I already addressed this in my earlier reply to Rachel.

@othermaciej

(1) It's critical for component authors to be able to whitelist the set of style properties that can be applied to an exposed named part.

Why? CSS doesn't do this today. ::before/after take all properties. ::first-line and friends don't, but that's because they're not actually elements, they're bizarre collections of fragments (to use CSS terms), and that limits what you can reasonably do with them. That's not relevant to this topic, tho; we're only planning to expose actual elements here.

If you want to whitelist properties, that's best done with simple CSS variables - expose variables for each property you want to allow. If you want to expose arbitrary styling but blacklist some properties for sanity, that's trivially done with @apply - just list the blacklist properties with their desired values after the @apply rule. With custom pseudos, this is yet another new thing you'll have to define and introduce, presumably in JS.

(2) I'm somewhat partial to :part syntax because then we never have to worry about namespace collisions with future CSS built-in pseudos.

Yes, ::part() is definitely a preferable syntax for pseudo-elements here.

(4) I don't think @apply plus the increasing series of other features that need to come along with it is a replacement. The syntax is pretty inscrutable. I immediately understand what Philip's named part examples are doing, but I don't understand what his @apply + nesting proposal does.

It's not "increasing", it's complete. Variables/@apply/nesting are a complete styling feature.

The syntax is relatively new, but a lack of immediately familiarity doesn't automatically imply "inscrutable". It's a declaration block stuffed into a custom property. Nesting is familiar to users of every single CSS preprocessor in existence, and is consistently one of the most popular and highly used features. The learning curve here is miniscule based on practical evidence.

Member

tabatkins commented Sep 30, 2015

@philipwalton (re: nesting) Yes, that's precisely what I was getting at when I was talking about @apply/nesting. ^_^ There's no specificity issues to iron out - It Just Works™.

@rniwa

Like we agreed during the last F2F, custom properties, @apply, and custom pseudo elements aren't conflicting ideas. They address slightly different use cases and both can be supported.

No, they don't address slightly different use-cases. They address the exact same use-cases.

In particular, in an isolated web component case where neither component nor its container document trusts each other, we can't expose all mixins defined in the container document into the shadow tree.

Sure we can. And when you do want to block, that's what all: initial is for. (Alternately, an equivalent of all for just custom properties has been proposed; it would be called --, as in --: initial.) You can then let particular custom properties thru with --foo: inherit.

Also, there is an added benefit of developer familiarity with custom pseudo elements. While the technical problems faced by nested rules and @apply might be similar (or even simpler as you claim) to those faced by custom pseudo elements, relying solely on three brand new CSS syntax seems like a lot of cognitive load.

I already addressed this in my earlier reply to Rachel.

@othermaciej

(1) It's critical for component authors to be able to whitelist the set of style properties that can be applied to an exposed named part.

Why? CSS doesn't do this today. ::before/after take all properties. ::first-line and friends don't, but that's because they're not actually elements, they're bizarre collections of fragments (to use CSS terms), and that limits what you can reasonably do with them. That's not relevant to this topic, tho; we're only planning to expose actual elements here.

If you want to whitelist properties, that's best done with simple CSS variables - expose variables for each property you want to allow. If you want to expose arbitrary styling but blacklist some properties for sanity, that's trivially done with @apply - just list the blacklist properties with their desired values after the @apply rule. With custom pseudos, this is yet another new thing you'll have to define and introduce, presumably in JS.

(2) I'm somewhat partial to :part syntax because then we never have to worry about namespace collisions with future CSS built-in pseudos.

Yes, ::part() is definitely a preferable syntax for pseudo-elements here.

(4) I don't think @apply plus the increasing series of other features that need to come along with it is a replacement. The syntax is pretty inscrutable. I immediately understand what Philip's named part examples are doing, but I don't understand what his @apply + nesting proposal does.

It's not "increasing", it's complete. Variables/@apply/nesting are a complete styling feature.

The syntax is relatively new, but a lack of immediately familiarity doesn't automatically imply "inscrutable". It's a declaration block stuffed into a custom property. Nesting is familiar to users of every single CSS preprocessor in existence, and is consistently one of the most popular and highly used features. The learning curve here is miniscule based on practical evidence.

@rniwa

This comment has been minimized.

Show comment
Hide comment
@rniwa

rniwa Sep 30, 2015

Contributor

Sure we can. And when you do want to block, that's what all: initial is for. (Alternately, an equivalent of all for just custom properties has been proposed; it would be called --, as in --: initial.) You can then let particular custom properties thru with --foo: inherit.

That's such a cumbersome API. So if an author wanted to use a component and didn't want to expose any custom properties to it, then he/she has to do --: initial and selectively expose custom property. That's too much complexity just to style a part of component.

Nesting is familiar to users of every single CSS preprocessor in existence, and is consistently one of the most popular and highly used features. The learning curve here is miniscule based on practical evidence.

I disagree. Nesting declarations of pseudo-class selectors inside custom mixins is extremely confusing.

Contributor

rniwa commented Sep 30, 2015

Sure we can. And when you do want to block, that's what all: initial is for. (Alternately, an equivalent of all for just custom properties has been proposed; it would be called --, as in --: initial.) You can then let particular custom properties thru with --foo: inherit.

That's such a cumbersome API. So if an author wanted to use a component and didn't want to expose any custom properties to it, then he/she has to do --: initial and selectively expose custom property. That's too much complexity just to style a part of component.

Nesting is familiar to users of every single CSS preprocessor in existence, and is consistently one of the most popular and highly used features. The learning curve here is miniscule based on practical evidence.

I disagree. Nesting declarations of pseudo-class selectors inside custom mixins is extremely confusing.

@philipwalton

This comment has been minimized.

Show comment
Hide comment
@philipwalton

philipwalton Sep 30, 2015

Member

That's such a cumbersome API. So if an author wanted to use a component and didn't want to expose any custom properties to it, then he/she has to do --: initial and selectively expose custom property. That's too much complexity just to style a part of component.

@rniwa, what would the custom pseudo-element equivalent be for whitelisting the styleable properties? As the proposal is now (and obviously it can change), exposing an element as a custom pseudo element exposes all properties. Presumably component authors could use !important to blacklist, but that wouldn't work to whitelist.

Member

philipwalton commented Sep 30, 2015

That's such a cumbersome API. So if an author wanted to use a component and didn't want to expose any custom properties to it, then he/she has to do --: initial and selectively expose custom property. That's too much complexity just to style a part of component.

@rniwa, what would the custom pseudo-element equivalent be for whitelisting the styleable properties? As the proposal is now (and obviously it can change), exposing an element as a custom pseudo element exposes all properties. Presumably component authors could use !important to blacklist, but that wouldn't work to whitelist.

@philipwalton

This comment has been minimized.

Show comment
Hide comment
@philipwalton

philipwalton Sep 30, 2015

Member

There's no specificity issues to iron out - It Just Works™.

@tabatkins, yeah I think you're right. I'd originally imagined a situation where element-author-defined pseudo-class rules would trump @apply-ed rules (inside a selector without the pseudo-classes), but that issue could easily be solved by moving the @apply declaration to after all rules with pseudo-classes.

Member

philipwalton commented Sep 30, 2015

There's no specificity issues to iron out - It Just Works™.

@tabatkins, yeah I think you're right. I'd originally imagined a situation where element-author-defined pseudo-class rules would trump @apply-ed rules (inside a selector without the pseudo-classes), but that issue could easily be solved by moving the @apply declaration to after all rules with pseudo-classes.

@rniwa

This comment has been minimized.

Show comment
Hide comment
@rniwa

rniwa Sep 30, 2015

Contributor

@philipwalton : My point in #300 (comment) is nothing to do with whitelisting properties but more to do with exposing mixins and properties across component boundaries.

In the case of custom pseudo elements, the authors of components are explicitly opting in the contract that a part of its component is stylable by its user, and the users of components are similarly opting in to style those parts without exposing any other mixin or custom property defined in the document.

This is a crucial property for the isolated components where neither component author nor component user trust each other (e.g. for cross-origin widgets).

Contributor

rniwa commented Sep 30, 2015

@philipwalton : My point in #300 (comment) is nothing to do with whitelisting properties but more to do with exposing mixins and properties across component boundaries.

In the case of custom pseudo elements, the authors of components are explicitly opting in the contract that a part of its component is stylable by its user, and the users of components are similarly opting in to style those parts without exposing any other mixin or custom property defined in the document.

This is a crucial property for the isolated components where neither component author nor component user trust each other (e.g. for cross-origin widgets).

@tabatkins

This comment has been minimized.

Show comment
Hide comment
@tabatkins

tabatkins Sep 30, 2015

Member

That's such a cumbersome API. So if an author wanted to use a component and didn't want to expose any custom properties to it, then he/she has to do --: initial and selectively expose custom property. That's too much complexity just to style a part of component.

I'm not seeing the complexity. Did you actually write it out and see what it would look like, compared to whatever else you'd want to do?

/* set one variable for a sub-component,
   letting the outer page set the rest if they want */
sub-component { 
  --heading: { color: blue; text-decoration: underline; };
}

/* set one variable for a sub-component,
   and force the rest to be default value,
   blocking the outer page from setting anything */
sub-component {
  --: initial;
  --heading: { color: blue; text-decoration: underline; };
}

I'm not seeing the "too much complexity".

And note, we're comparing this to a nonexistent, unknown syntax for exposing or hiding the ::part()s of sub-components. We have no idea if such a syntax will be blacklist or whitelist based, how it will be invoked, or what language we'll use for it. (HTML attributes? JS api? CSS syntax?) We have no clue what its complexity will be, because it doesn't exist yet. This is one of many already-mentioned unanswered-so-far questions about the ::part syntax.

I disagree. Nesting declarations of pseudo-class selectors inside custom mixins is extremely confusing.

You're free to disagree, but heavy usage of nesting in Sass and others goes against your feelings. I'm not sure what the usage numbers are for the nearest direct analogue (Sass nesting within @mixin rules), but this doesn't look confusing in the slightest to me:

sub-component {
  --heading: {
    color: blue;
    text-decoration: underline;
    &:hover { color: red; font-weight: bold; }
  };
}

Compare this to an assumed ::part-based syntax:

sub-component::part(heading) {
  color: blue;
  text-decoration: underline;
}
sub-component::part(heading):hover {
  color: red; font-weight: bold;
}

Those look basically identical. I won't fault ::part() for the extra selector verbosity there; if Nesting exists, it can be simplified to a basically identical form:

sub-component::part(heading) {
  color: blue;
  text-decoration: underline;
  &:hover { color: red; font-weight: bold; }
}

I just want to emphasize, again, that ::part() has a number of currently-unanswered questions about its functionality and syntax. I outlined several of them up above in a previous comment. They certainly can be addressed, but haven't been so far, and when they are, several of them will imply further syntax and complexity for the feature. Custom properties and @apply/Nesting, on the other hand, have answers to all those questions right now; they fall out of the definitions of the feature.

For more complex cases, until we actually answer the relevant questions for ::part (such as how sub-component ::parts are exposed or hidden), we won't have the ability to do good comparisons with anything else.

Member

tabatkins commented Sep 30, 2015

That's such a cumbersome API. So if an author wanted to use a component and didn't want to expose any custom properties to it, then he/she has to do --: initial and selectively expose custom property. That's too much complexity just to style a part of component.

I'm not seeing the complexity. Did you actually write it out and see what it would look like, compared to whatever else you'd want to do?

/* set one variable for a sub-component,
   letting the outer page set the rest if they want */
sub-component { 
  --heading: { color: blue; text-decoration: underline; };
}

/* set one variable for a sub-component,
   and force the rest to be default value,
   blocking the outer page from setting anything */
sub-component {
  --: initial;
  --heading: { color: blue; text-decoration: underline; };
}

I'm not seeing the "too much complexity".

And note, we're comparing this to a nonexistent, unknown syntax for exposing or hiding the ::part()s of sub-components. We have no idea if such a syntax will be blacklist or whitelist based, how it will be invoked, or what language we'll use for it. (HTML attributes? JS api? CSS syntax?) We have no clue what its complexity will be, because it doesn't exist yet. This is one of many already-mentioned unanswered-so-far questions about the ::part syntax.

I disagree. Nesting declarations of pseudo-class selectors inside custom mixins is extremely confusing.

You're free to disagree, but heavy usage of nesting in Sass and others goes against your feelings. I'm not sure what the usage numbers are for the nearest direct analogue (Sass nesting within @mixin rules), but this doesn't look confusing in the slightest to me:

sub-component {
  --heading: {
    color: blue;
    text-decoration: underline;
    &:hover { color: red; font-weight: bold; }
  };
}

Compare this to an assumed ::part-based syntax:

sub-component::part(heading) {
  color: blue;
  text-decoration: underline;
}
sub-component::part(heading):hover {
  color: red; font-weight: bold;
}

Those look basically identical. I won't fault ::part() for the extra selector verbosity there; if Nesting exists, it can be simplified to a basically identical form:

sub-component::part(heading) {
  color: blue;
  text-decoration: underline;
  &:hover { color: red; font-weight: bold; }
}

I just want to emphasize, again, that ::part() has a number of currently-unanswered questions about its functionality and syntax. I outlined several of them up above in a previous comment. They certainly can be addressed, but haven't been so far, and when they are, several of them will imply further syntax and complexity for the feature. Custom properties and @apply/Nesting, on the other hand, have answers to all those questions right now; they fall out of the definitions of the feature.

For more complex cases, until we actually answer the relevant questions for ::part (such as how sub-component ::parts are exposed or hidden), we won't have the ability to do good comparisons with anything else.

@tabatkins

This comment has been minimized.

Show comment
Hide comment
@tabatkins

tabatkins Sep 30, 2015

Member

This is a crucial property for the isolated components where neither component author nor component user trust each other (e.g. for cross-origin widgets).

Fully isolated components are a trivial case in the vars/apply/nesting feature set; you just don't use any undefined variables. If you only use variables you define yourself, there's no way for outside-world inheritance to interfere. I presume this is the same as in ::part - just don't expose any parts, and nobody can style you.

If you further want to block sub-components from receiving styling, this is trivial in vars/apply/nesting, as I demonstrated above (or if you want to block everything immediately, for all your sub-components, just apply a :host { --: initial; }, or maybe just rely on the "no inheritance by default/ever" concept that I assume isolated components will have).

Since we have no idea how ::part will expose/hide the parts of sub-components, we can't make a proper comparison yet, but we can at least see that the v/a/n option is trivial.

Member

tabatkins commented Sep 30, 2015

This is a crucial property for the isolated components where neither component author nor component user trust each other (e.g. for cross-origin widgets).

Fully isolated components are a trivial case in the vars/apply/nesting feature set; you just don't use any undefined variables. If you only use variables you define yourself, there's no way for outside-world inheritance to interfere. I presume this is the same as in ::part - just don't expose any parts, and nobody can style you.

If you further want to block sub-components from receiving styling, this is trivial in vars/apply/nesting, as I demonstrated above (or if you want to block everything immediately, for all your sub-components, just apply a :host { --: initial; }, or maybe just rely on the "no inheritance by default/ever" concept that I assume isolated components will have).

Since we have no idea how ::part will expose/hide the parts of sub-components, we can't make a proper comparison yet, but we can at least see that the v/a/n option is trivial.

@rniwa

This comment has been minimized.

Show comment
Hide comment
@rniwa

rniwa Sep 30, 2015

Contributor

Well, the problem is that we do need the fidelity of being able to expose some parts of the isolated component. This is the exact use case addressed by various builtin pseudo elements in WebKit. Since that is a feature that has been shipping in WebKit/Blink for years and proven to be popular amongst developers, I don't see why we need to re-invent the wheel to replace that solution.

Contributor

rniwa commented Sep 30, 2015

Well, the problem is that we do need the fidelity of being able to expose some parts of the isolated component. This is the exact use case addressed by various builtin pseudo elements in WebKit. Since that is a feature that has been shipping in WebKit/Blink for years and proven to be popular amongst developers, I don't see why we need to re-invent the wheel to replace that solution.

@tabatkins

This comment has been minimized.

Show comment
Hide comment
@tabatkins

tabatkins Oct 1, 2015

Member

Yes, I understand that we need to expose particular parts for styling; that's addressed equally well with v/a/n or ::part. (With v/a/n, you document several variables, and use each within an appropriate selector, like h1 { @apply --heading; }. With ::part(), you expose the relevant element as a visible part, I presume with something like <h1 part=heading>.)

What, specifically, do you think is hard with one or the other solution?

I don't see why we need to re-invent the wheel to replace that solution.

No matter what we do, we're going to be reinventing things. We will not produce a solution that allows for a 100% fidelity recreation of the current form pseudo-element stuff, because that immediately runs into namespace issues. Current form controls also only touch on a small subset of the possibility space, while ::part() needs to address a bunch more cases (I outlined some in a previous comment).

So lots of invention have to happen no matter what.

Member

tabatkins commented Oct 1, 2015

Yes, I understand that we need to expose particular parts for styling; that's addressed equally well with v/a/n or ::part. (With v/a/n, you document several variables, and use each within an appropriate selector, like h1 { @apply --heading; }. With ::part(), you expose the relevant element as a visible part, I presume with something like <h1 part=heading>.)

What, specifically, do you think is hard with one or the other solution?

I don't see why we need to re-invent the wheel to replace that solution.

No matter what we do, we're going to be reinventing things. We will not produce a solution that allows for a 100% fidelity recreation of the current form pseudo-element stuff, because that immediately runs into namespace issues. Current form controls also only touch on a small subset of the possibility space, while ::part() needs to address a bunch more cases (I outlined some in a previous comment).

So lots of invention have to happen no matter what.

@Zambonifofex

This comment has been minimized.

Show comment
Hide comment
@Zambonifofex

Zambonifofex Jun 9, 2016

From my point of view, custom elements are here to give authors the same power as user-agents have of defining elements, or at least as close to that as possible.

The elements defined by the spec are not necessarily implemented with JavaScript and CSS, but they could be. Even the most convoluted ones like input with all its types and shapes.

Giving authors the ability to create custom elements is putting them at nearly the same level as the user-agent, allowing them to define elements in the same way the user-agent does.

Not without giving authors the power to create custom pseudo-elements feels incomplete to me, as it’s yet another thing the user-agent can do that the author cannot. By giving authors the ability to create custom pseudo-elements you are bringing their power closer to the user-agent’s.

@tabatkins effectively, what I’m saying is that I prefer custom pseudo-elements to your suggestion because it is closer to what is done by user-agents to regular elements.

I suggest the following syntax:

x-foo..custom-element
{
    /* styles */
}

As it’s comparable to what we have today:

/* user-agent-defined class */
button:enabled
{}

/* author-defined class */
button.loading
{}

/* user-agent-defined pseudo-element */
x-foo::first-line
{}

/* author-defined pseudo-element */
x-foo..last-letter
{}

Zambonifofex commented Jun 9, 2016

From my point of view, custom elements are here to give authors the same power as user-agents have of defining elements, or at least as close to that as possible.

The elements defined by the spec are not necessarily implemented with JavaScript and CSS, but they could be. Even the most convoluted ones like input with all its types and shapes.

Giving authors the ability to create custom elements is putting them at nearly the same level as the user-agent, allowing them to define elements in the same way the user-agent does.

Not without giving authors the power to create custom pseudo-elements feels incomplete to me, as it’s yet another thing the user-agent can do that the author cannot. By giving authors the ability to create custom pseudo-elements you are bringing their power closer to the user-agent’s.

@tabatkins effectively, what I’m saying is that I prefer custom pseudo-elements to your suggestion because it is closer to what is done by user-agents to regular elements.

I suggest the following syntax:

x-foo..custom-element
{
    /* styles */
}

As it’s comparable to what we have today:

/* user-agent-defined class */
button:enabled
{}

/* author-defined class */
button.loading
{}

/* user-agent-defined pseudo-element */
x-foo::first-line
{}

/* author-defined pseudo-element */
x-foo..last-letter
{}
@andyearnshaw

This comment has been minimized.

Show comment
Hide comment
@andyearnshaw

andyearnshaw Jun 10, 2016

As it’s comparable to what we have today:

That comparison doesn't really hold true for web components. "User-agent-defined [classes]" (pseudo-classes) represent the target when it is in a particular state without modifying the attributes of the element. Component authors have a requirement similar to this, as opposed to page authors who are using the component. Page authors expect that, when the internal state of an element changes (e.g. focus), its attributes do not (though its properties may). A component author, therefore, wouldn't modify the class attribute of the custom element hosting its shadow tree.

In a similar vein, a component author might wish to define a "pseudo-element" to represent an internal part of the component, which they would do without modifying the host element in any observable manner. A page author would just add a child element to the host.

/* user-agent-defined state */
button:enabled

/* page author-defined state */
button.loading

/* component author-defined state */
x-button<???>

/* user-agent-defined pseudo-element */
foo::first-line

/* page author-defined element */
foo bar

/* component author-defined shady-element */
x-foo<???>

If you want to propose new syntax, you should rethink your premise for choosing .. (which I don't really like, to be honest).

andyearnshaw commented Jun 10, 2016

As it’s comparable to what we have today:

That comparison doesn't really hold true for web components. "User-agent-defined [classes]" (pseudo-classes) represent the target when it is in a particular state without modifying the attributes of the element. Component authors have a requirement similar to this, as opposed to page authors who are using the component. Page authors expect that, when the internal state of an element changes (e.g. focus), its attributes do not (though its properties may). A component author, therefore, wouldn't modify the class attribute of the custom element hosting its shadow tree.

In a similar vein, a component author might wish to define a "pseudo-element" to represent an internal part of the component, which they would do without modifying the host element in any observable manner. A page author would just add a child element to the host.

/* user-agent-defined state */
button:enabled

/* page author-defined state */
button.loading

/* component author-defined state */
x-button<???>

/* user-agent-defined pseudo-element */
foo::first-line

/* page author-defined element */
foo bar

/* component author-defined shady-element */
x-foo<???>

If you want to propose new syntax, you should rethink your premise for choosing .. (which I don't really like, to be honest).

@Zambonifofex

This comment has been minimized.

Show comment
Hide comment
@Zambonifofex

Zambonifofex Jun 10, 2016

@andyearnshaw

A component author, therefore, wouldn't modify the class attribute of the custom element hosting its shadow tree.

Hrn, you’re right. Either way, my point is: I really dislike the ::part() syntax. I think a different token could be used to represent custom pseudo-elements.


But does anyone disagree with what I said about custom pseudo-elements?

Effectively, what I’m saying is that I prefer custom pseudo-elements to [@tabatkins’] suggestion because it is closer to what is done by user-agents to regular elements.


Addressing #300 (comment):

(1) Are shadow-pseudos more like classes or IDs?

I think they should act exactly like ids.

invalid:

<div pseudo="foo bar"></div>

invalid:

<div pseudo="foo"></div>
<div pseudo="foo"></div>

(2) How do we handle both a parent and child exposing themselves as pseudo-elements?

<div pseudo="foo">
    <div pseudo="bar"></div>
</div>
/* matches nothing */
x-baz::foo > div
{}

/* matches nothing */
div > x-baz::bar
{}

/* matches nothing */
x-bar::foo::bar
{}

/* matches <div pseudo="bar"> */
x-baz::foo > x-baz::bar
{}

(3) If a component contains other components, and wants to expose some of its sub-components parts as pseudo-elements, how does it surface them?

You can access them by nesting ::. The following would show a “Hello, world!” button.

<!doctype html>
<html>
    <head>
        <style>
            x-foo::bar::baz::before
            {
                content: "Hello, world!";
            }
        </style>
    </head>
    <body>
        <x-foo>
            #shadow
                <x-bar pseudo="bar">
                    #shadow
                        <button pseudo="baz"></button>
                    /#shadow
                </x-bar>
            /#shadow
        </x-foo>
    </body>
</html>

Zambonifofex commented Jun 10, 2016

@andyearnshaw

A component author, therefore, wouldn't modify the class attribute of the custom element hosting its shadow tree.

Hrn, you’re right. Either way, my point is: I really dislike the ::part() syntax. I think a different token could be used to represent custom pseudo-elements.


But does anyone disagree with what I said about custom pseudo-elements?

Effectively, what I’m saying is that I prefer custom pseudo-elements to [@tabatkins’] suggestion because it is closer to what is done by user-agents to regular elements.


Addressing #300 (comment):

(1) Are shadow-pseudos more like classes or IDs?

I think they should act exactly like ids.

invalid:

<div pseudo="foo bar"></div>

invalid:

<div pseudo="foo"></div>
<div pseudo="foo"></div>

(2) How do we handle both a parent and child exposing themselves as pseudo-elements?

<div pseudo="foo">
    <div pseudo="bar"></div>
</div>
/* matches nothing */
x-baz::foo > div
{}

/* matches nothing */
div > x-baz::bar
{}

/* matches nothing */
x-bar::foo::bar
{}

/* matches <div pseudo="bar"> */
x-baz::foo > x-baz::bar
{}

(3) If a component contains other components, and wants to expose some of its sub-components parts as pseudo-elements, how does it surface them?

You can access them by nesting ::. The following would show a “Hello, world!” button.

<!doctype html>
<html>
    <head>
        <style>
            x-foo::bar::baz::before
            {
                content: "Hello, world!";
            }
        </style>
    </head>
    <body>
        <x-foo>
            #shadow
                <x-bar pseudo="bar">
                    #shadow
                        <button pseudo="baz"></button>
                    /#shadow
                </x-bar>
            /#shadow
        </x-foo>
    </body>
</html>
@Zambonifofex

This comment has been minimized.

Show comment
Hide comment
@Zambonifofex

Zambonifofex Jul 13, 2016

By the way, I’d like to state that my opinions changed about this: I’m starting to like @apply.

I think that it’d be really neat if browsers allowed us to style their vendor‐specific “pseudo‐elements” using a similar mechanism:

input
{
    -webkit-inner-spin-button:
    {
        /* ... */
    };
}

Please note how using a single dash avoids collision with user‐defined variables.

Zambonifofex commented Jul 13, 2016

By the way, I’d like to state that my opinions changed about this: I’m starting to like @apply.

I think that it’d be really neat if browsers allowed us to style their vendor‐specific “pseudo‐elements” using a similar mechanism:

input
{
    -webkit-inner-spin-button:
    {
        /* ... */
    };
}

Please note how using a single dash avoids collision with user‐defined variables.

@trusktr

This comment has been minimized.

Show comment
Hide comment
@trusktr

trusktr Jul 13, 2016

@tabatkins Can you give (or link to) an example of @apply being used to expose stylable parts of a shadow tree?

trusktr commented Jul 13, 2016

@tabatkins Can you give (or link to) an example of @apply being used to expose stylable parts of a shadow tree?

@hayatoito

This comment has been minimized.

Show comment
Hide comment
@hayatoito

hayatoito Sep 2, 2016

Member

cc: @dominiccooney , @shans
This might be an issue you are interested in.

I think this issue is a worth triaging for v2. If someone has an updated opinion or idea, please let us know that.

Member

hayatoito commented Sep 2, 2016

cc: @dominiccooney , @shans
This might be an issue you are interested in.

I think this issue is a worth triaging for v2. If someone has an updated opinion or idea, please let us know that.

@seaneking

This comment has been minimized.

Show comment
Hide comment
@seaneking

seaneking Oct 13, 2016

Just some quick input as a user - another case where I've found @apply currently falls apart is with composition. Eg: I have an element that exposes a mixin, I apply a 'base theme' to that element in my app, I want to add extra styles on top of that base theme in a specific instance of the element. Right now if I @apply another mixin on the element it clobbers the base theme.

A workaround for explicit inheritance for @apply was discussed at tabatkins/specs#49, though the proposed syntax is perhaps a little unintuitive for users, whereas composition with parts (should?) 'just work'.

seaneking commented Oct 13, 2016

Just some quick input as a user - another case where I've found @apply currently falls apart is with composition. Eg: I have an element that exposes a mixin, I apply a 'base theme' to that element in my app, I want to add extra styles on top of that base theme in a specific instance of the element. Right now if I @apply another mixin on the element it clobbers the base theme.

A workaround for explicit inheritance for @apply was discussed at tabatkins/specs#49, though the proposed syntax is perhaps a little unintuitive for users, whereas composition with parts (should?) 'just work'.

@tabatkins

This comment has been minimized.

Show comment
Hide comment
@tabatkins

tabatkins Oct 13, 2016

Member

@seaneking Can you elaborate on the failure case you're talking about more, perhaps with a code example? I don't understand how you're getting a clobbering.

Member

tabatkins commented Oct 13, 2016

@seaneking Can you elaborate on the failure case you're talking about more, perhaps with a code example? I don't understand how you're getting a clobbering.

@seaneking

This comment has been minimized.

Show comment
Hide comment
@seaneking

seaneking Oct 14, 2016

@tabatkins perhaps I've misunderstood the spec (re: cascading in https://tabatkins.github.io/specs/css-apply-rule/#defining), but wouldn't the following result in overwritten styles?

Assuming an element x-foo with local DOM

<template>
  <style>
    .inner {
      @apply --my-mixin;
    }
  </style>
  <div class="inner"></div>
</template>

And I use it on my page

<x-foo class="styled"></x-foo>

Then style all x-foo's on my page with some base styles, and some additional styles for this particular one

x-foo {
  --my-mixin: {
    background: blue;
  };
}

/* Can't inherit background blue */
x-foo.styled {
  --my-mixin: {
    border: 1px solid red;
  };
}

--my-mixin doesn't cascade, so I can't compose. I think this is the correct behaviour, it just makes them hard to reason about in custom elements.

Specific eg: I'm putting together a collection of UI elements (SimpleElements), and applying base styles for them throughout our app (using mixins). But if I touch one of them again the base is overwritten and the whole notion becomes quite redundant.

TL;DR I think (again, as a user) that mixins are extremely useful for doing what they say on the tin - giving you bags of styles to mixin to multiple elements. But feels like the function of exposing parts of custom elements would be better served by custom pseudo elements or similar.

seaneking commented Oct 14, 2016

@tabatkins perhaps I've misunderstood the spec (re: cascading in https://tabatkins.github.io/specs/css-apply-rule/#defining), but wouldn't the following result in overwritten styles?

Assuming an element x-foo with local DOM

<template>
  <style>
    .inner {
      @apply --my-mixin;
    }
  </style>
  <div class="inner"></div>
</template>

And I use it on my page

<x-foo class="styled"></x-foo>

Then style all x-foo's on my page with some base styles, and some additional styles for this particular one

x-foo {
  --my-mixin: {
    background: blue;
  };
}

/* Can't inherit background blue */
x-foo.styled {
  --my-mixin: {
    border: 1px solid red;
  };
}

--my-mixin doesn't cascade, so I can't compose. I think this is the correct behaviour, it just makes them hard to reason about in custom elements.

Specific eg: I'm putting together a collection of UI elements (SimpleElements), and applying base styles for them throughout our app (using mixins). But if I touch one of them again the base is overwritten and the whole notion becomes quite redundant.

TL;DR I think (again, as a user) that mixins are extremely useful for doing what they say on the tin - giving you bags of styles to mixin to multiple elements. But feels like the function of exposing parts of custom elements would be better served by custom pseudo elements or similar.

@tabatkins

This comment has been minimized.

Show comment
Hide comment
@tabatkins

tabatkins Nov 22, 2016

Member

Ah, yes, custom properties cascade like every other property does, with one value winning per element; the fact that one of them might contain a declaration block intended to be sent to an @apply doesn't change things.

I have a proposal for some cascade controls that would let a property "merge" with its earlier-in-the-cascade variants; if the custom property was appropriately typed as carrying a declaration list (we don't have a type for that in Custom Properties & Values yet, but we'll add it when @apply is officially accepted), then --my-mixin+: {...}; would work. (It would be considered a list-valued property, I guess, just with a different delimiter than normal.)

Member

tabatkins commented Nov 22, 2016

Ah, yes, custom properties cascade like every other property does, with one value winning per element; the fact that one of them might contain a declaration block intended to be sent to an @apply doesn't change things.

I have a proposal for some cascade controls that would let a property "merge" with its earlier-in-the-cascade variants; if the custom property was appropriately typed as carrying a declaration list (we don't have a type for that in Custom Properties & Values yet, but we'll add it when @apply is officially accepted), then --my-mixin+: {...}; would work. (It would be considered a list-valued property, I guess, just with a different delimiter than normal.)

@seaneking

This comment has been minimized.

Show comment
Hide comment
@seaneking

seaneking Nov 22, 2016

Ah okay, I guess that would solve that issue. Still, you'd now have nesting and ampersand operators to handle pseudo classes, and 'merge' combinators to handle cascading, when neither really seem in the scope of a mixin's primary purpose (mixing in a bag of styles to multiple elements), and the alternative is just to expose a paradigm that developers are already familiar with (pseudo elements) which 'just works' for all of these cases. And FWIW, I personally find ::part a less radical introduction than nesting in CSS, especially since it's become a bit of an antipattern in preprocessors.

I can't really comment on future-of-the-platform stuff, but even from a developer ergonomics standpoint, just seems we're overloading mixins with lots of new things for the sake of not introducing a new thing.

seaneking commented Nov 22, 2016

Ah okay, I guess that would solve that issue. Still, you'd now have nesting and ampersand operators to handle pseudo classes, and 'merge' combinators to handle cascading, when neither really seem in the scope of a mixin's primary purpose (mixing in a bag of styles to multiple elements), and the alternative is just to expose a paradigm that developers are already familiar with (pseudo elements) which 'just works' for all of these cases. And FWIW, I personally find ::part a less radical introduction than nesting in CSS, especially since it's become a bit of an antipattern in preprocessors.

I can't really comment on future-of-the-platform stuff, but even from a developer ergonomics standpoint, just seems we're overloading mixins with lots of new things for the sake of not introducing a new thing.

@jouni

This comment has been minimized.

Show comment
Hide comment
@jouni

jouni Jan 8, 2017

@seaneking I’m not entirely sure, but I think you can solve your use case in the following way (nesting @apply):

x-foo {
  --my-mixin: {
    background: blue;
    @apply --my-mixin-extras;
  };
}

x-foo.styled {
  --my-mixin-extras: {
    border: 1px solid red;
  };
}

It definitely feels like a workaround (and is it even allowed by the proposed spec?), as you need to introduce another name for the mixin after each “level”.

jouni commented Jan 8, 2017

@seaneking I’m not entirely sure, but I think you can solve your use case in the following way (nesting @apply):

x-foo {
  --my-mixin: {
    background: blue;
    @apply --my-mixin-extras;
  };
}

x-foo.styled {
  --my-mixin-extras: {
    border: 1px solid red;
  };
}

It definitely feels like a workaround (and is it even allowed by the proposed spec?), as you need to introduce another name for the mixin after each “level”.

@seaneking

This comment has been minimized.

Show comment
Hide comment
@seaneking

seaneking Jan 8, 2017

@jouni then you're tightly coupling the two sets of styles, which isn't really tenable in a lot of use cases, and in others is just asking for unmaintainable code.

I think the 'cascade controls' proposed by @tabatkins would be a better solution for, well, controlling cascade. My big concern is adding all these band-aids to mixins for a job they (I'm assuming) weren't originally designed to do, instead of using a paradigm that has already been proven to work for all of these use cases (pseudo elements).

seaneking commented Jan 8, 2017

@jouni then you're tightly coupling the two sets of styles, which isn't really tenable in a lot of use cases, and in others is just asking for unmaintainable code.

I think the 'cascade controls' proposed by @tabatkins would be a better solution for, well, controlling cascade. My big concern is adding all these band-aids to mixins for a job they (I'm assuming) weren't originally designed to do, instead of using a paradigm that has already been proven to work for all of these use cases (pseudo elements).

@othermaciej

This comment has been minimized.

Show comment
Hide comment
@othermaciej

othermaciej Jan 18, 2017

Member

Perhaps there is value in styling both parts and states, and thus if we want to be sure CSS-ishly correct, there may need to be both shadow pseudo-elements and shadow pseudo-classes (for states). I would suggest they could be named ::part() and :state() (different number of colons intentional). You'd target the active image with ::part(image):state(active). Having shadow pseudo-classes would also allow defining styling for custom states of the whole custom element, if pre-defined pseudo-classes like :active and :hover are not enough.

Member

othermaciej commented Jan 18, 2017

Perhaps there is value in styling both parts and states, and thus if we want to be sure CSS-ishly correct, there may need to be both shadow pseudo-elements and shadow pseudo-classes (for states). I would suggest they could be named ::part() and :state() (different number of colons intentional). You'd target the active image with ::part(image):state(active). Having shadow pseudo-classes would also allow defining styling for custom states of the whole custom element, if pre-defined pseudo-classes like :active and :hover are not enough.

@morewry

This comment has been minimized.

Show comment
Hide comment
@morewry

morewry Jan 30, 2017

This makes sense to me:

  • Named parts are similar to "custom named pseudo-element selectors."
  • Named parts should act analogously to type and pseudo-element selectors. Except that named parts allow one part to have multiple aliases.
  • Named parts names therefore can be repeated and selectors can match multiple elements.
  • The named parts of a sub-component are not automatically exposed.
  • The named parts of a sub-component can be specifically exposed ("forwarded" in other comments) to the component user by the consuming component author(/sub-component user) if the component author gives them an alias.
  • Named parts need a mechanism for being aliased for exposure when they are in sub-components used by a consuming component author.
  • All feasible standardized combinators and pseudo-classes can be used with a named part, but only apply to elements exposed for selection, i.e. other named parts, for example:
    • :first-of-type() would only be eligible to match part names, or not work at all. (I'd prefer this to the ability to match actual type of a named part.)
    • * selects only exposed named parts, not all elements.
    • :first-child selects the first child that is an exposed as a named part, if any. Should this ignore an unnamed first child to select the first named child or only select if the named child is the first child?
    • + * selects the adjacent named part, if any. Should this ignore any intervening unnamed siblings or only select if the adjacent sibling is named?
    • Similarly :nth-child(3)--should it only match if a named part is the third child, or should it match the third named child, if any?
  • Named parts are enhanced by named states.
  • Named states are custom named pseudo-classes.
  • Named states should act analogously to class and pseudo-class selectors.
  • Named state names therefore can be repeated and selectors can match multiple elements.
  • Named states can only be used on elements exposed for selection, i.e. named parts.
  • Named states need a mechanism for setting and unsetting the state internally.

morewry commented Jan 30, 2017

This makes sense to me:

  • Named parts are similar to "custom named pseudo-element selectors."
  • Named parts should act analogously to type and pseudo-element selectors. Except that named parts allow one part to have multiple aliases.
  • Named parts names therefore can be repeated and selectors can match multiple elements.
  • The named parts of a sub-component are not automatically exposed.
  • The named parts of a sub-component can be specifically exposed ("forwarded" in other comments) to the component user by the consuming component author(/sub-component user) if the component author gives them an alias.
  • Named parts need a mechanism for being aliased for exposure when they are in sub-components used by a consuming component author.
  • All feasible standardized combinators and pseudo-classes can be used with a named part, but only apply to elements exposed for selection, i.e. other named parts, for example:
    • :first-of-type() would only be eligible to match part names, or not work at all. (I'd prefer this to the ability to match actual type of a named part.)
    • * selects only exposed named parts, not all elements.
    • :first-child selects the first child that is an exposed as a named part, if any. Should this ignore an unnamed first child to select the first named child or only select if the named child is the first child?
    • + * selects the adjacent named part, if any. Should this ignore any intervening unnamed siblings or only select if the adjacent sibling is named?
    • Similarly :nth-child(3)--should it only match if a named part is the third child, or should it match the third named child, if any?
  • Named parts are enhanced by named states.
  • Named states are custom named pseudo-classes.
  • Named states should act analogously to class and pseudo-class selectors.
  • Named state names therefore can be repeated and selectors can match multiple elements.
  • Named states can only be used on elements exposed for selection, i.e. named parts.
  • Named states need a mechanism for setting and unsetting the state internally.
@tabatkins

This comment has been minimized.

Show comment
Hide comment
@tabatkins

tabatkins Jan 30, 2017

Member

So I, uh, put together a proposal for ::part() week before last. Shane and I are behind this, and Polymer team has given it a very basic go-over and seem okay with it.

CSS Shadow Parts

Member

tabatkins commented Jan 30, 2017

So I, uh, put together a proposal for ::part() week before last. Shane and I are behind this, and Polymer team has given it a very basic go-over and seem okay with it.

CSS Shadow Parts

@othermaciej

This comment has been minimized.

Show comment
Hide comment
@othermaciej

othermaciej Jan 31, 2017

Member

Thanks for writing up a draft!

Early comments:
(1) Style nit: the motivation section goes on too much about past proposed solutions for this problem but that weren't adopted. It's probably better to have the motivation just state the use case, perhaps mentioning the analogous case of styling for built-in controls, for those browser that have it.

(2) It seems like ::theme() is only useful for open mode shadow DOMs that have not done appropriate forwarding for further shadow DOMs that they contain. Is that really a big enough use case? The forwarding feature seems like a cleaner way to handle this, and one that also works with closed-mode shadow DOMs.

(3) It should be possible for components to restrict the set of CSS properties that can be applied to a part, ideally via a whitelist. (a) Built-in elements that expose pieces as pseudo-elements all do this; and (b) it seems likely that some CSS properties could break components in unexpected ways.

Member

othermaciej commented Jan 31, 2017

Thanks for writing up a draft!

Early comments:
(1) Style nit: the motivation section goes on too much about past proposed solutions for this problem but that weren't adopted. It's probably better to have the motivation just state the use case, perhaps mentioning the analogous case of styling for built-in controls, for those browser that have it.

(2) It seems like ::theme() is only useful for open mode shadow DOMs that have not done appropriate forwarding for further shadow DOMs that they contain. Is that really a big enough use case? The forwarding feature seems like a cleaner way to handle this, and one that also works with closed-mode shadow DOMs.

(3) It should be possible for components to restrict the set of CSS properties that can be applied to a part, ideally via a whitelist. (a) Built-in elements that expose pieces as pseudo-elements all do this; and (b) it seems likely that some CSS properties could break components in unexpected ways.

@tabatkins

This comment has been minimized.

Show comment
Hide comment
@tabatkins

tabatkins Feb 6, 2017

Member

(2) It seems like ::theme() is only useful for open mode shadow DOMs that have not done appropriate forwarding for further shadow DOMs that they contain. Is that really a big enough use case? The forwarding feature seems like a cleaner way to handle this, and one that also works with closed-mode shadow DOMs.

Yes, I believe it is important enough, for two reasons.

  1. One of the objections I heard from Polymer folk about dropping @apply for ::part() was that is made it more difficult to style all the buttons on the page, or similar. This was easy with @apply - just set --button-styles: {...}; and you were good - unless someone in the chain explicitly blocks that property, every button in the subtree will receive the styling.

This seems like it's trading off convenience of authoring components (save the need to forward explicitly) for complexity using components (you have to understand the difference between ::part and ::theme). That seems like the wrong tradeoff. In addition, the component author is better placed than the page author to know what should be forwarded to deeper layers.

I think it is reasonable and appropriate to ask more of component authors, than authors of code that uses them.

  1. Related to the above, one of the justifications for this whole feature area (@apply, ::part(), etc) is that you can already arbitrarily style things with custom properties, it's just awkward and dumb. If we restrict to just explicit forwarding (via ::part()), then people who want to allow arbitrary styling of their components can still get around the restriction by using the tons-of-properties approach. ::theme() captures this use-case in a much better way. (We might, of course, judge that this use-case is bad and people shouldn't do it, and so making it less terrible to use isn't worthwhile. Per the first reason, I don't think this is the case.)

I don't follow. Can you try explaining this again? Not sure what this is saying that's different from reason 1.

(3) It should be possible for components to restrict the set of CSS properties that can be applied to a part, ideally via a whitelist. (a) Built-in elements that expose pieces as pseudo-elements all do this; and (b) it seems likely that some CSS properties could break components in unexpected ways.

Maybe! This is definitely something we can explore; it seems easily separable, meaning we don't necessarily have to have it in the v1 proposal. I'm definitely okay with it, tho. Maybe something like part-properties="<comma-separated list>", where if it's missing or set to "*" it allows all of CSS.

I think it should be in v1. The fact that pretty much all built-in elements with styleable parts have this makes me believe this is fundamental, not just a nice add-on. And the custom properties approach allows this.

The authlist should only apply to normal CSS properties, I think - all custom props should be implicitly allowed. You can disallow them by setting them from within the component with an !important, I think.

Tentatively, I don't see a problem with that. Alternately, having a token to represent allowing all custom properties (maybe "--*" to be cute?) would give both control and convenience.

Member

tabatkins commented Feb 6, 2017

(2) It seems like ::theme() is only useful for open mode shadow DOMs that have not done appropriate forwarding for further shadow DOMs that they contain. Is that really a big enough use case? The forwarding feature seems like a cleaner way to handle this, and one that also works with closed-mode shadow DOMs.

Yes, I believe it is important enough, for two reasons.

  1. One of the objections I heard from Polymer folk about dropping @apply for ::part() was that is made it more difficult to style all the buttons on the page, or similar. This was easy with @apply - just set --button-styles: {...}; and you were good - unless someone in the chain explicitly blocks that property, every button in the subtree will receive the styling.

This seems like it's trading off convenience of authoring components (save the need to forward explicitly) for complexity using components (you have to understand the difference between ::part and ::theme). That seems like the wrong tradeoff. In addition, the component author is better placed than the page author to know what should be forwarded to deeper layers.

I think it is reasonable and appropriate to ask more of component authors, than authors of code that uses them.

  1. Related to the above, one of the justifications for this whole feature area (@apply, ::part(), etc) is that you can already arbitrarily style things with custom properties, it's just awkward and dumb. If we restrict to just explicit forwarding (via ::part()), then people who want to allow arbitrary styling of their components can still get around the restriction by using the tons-of-properties approach. ::theme() captures this use-case in a much better way. (We might, of course, judge that this use-case is bad and people shouldn't do it, and so making it less terrible to use isn't worthwhile. Per the first reason, I don't think this is the case.)

I don't follow. Can you try explaining this again? Not sure what this is saying that's different from reason 1.

(3) It should be possible for components to restrict the set of CSS properties that can be applied to a part, ideally via a whitelist. (a) Built-in elements that expose pieces as pseudo-elements all do this; and (b) it seems likely that some CSS properties could break components in unexpected ways.

Maybe! This is definitely something we can explore; it seems easily separable, meaning we don't necessarily have to have it in the v1 proposal. I'm definitely okay with it, tho. Maybe something like part-properties="<comma-separated list>", where if it's missing or set to "*" it allows all of CSS.

I think it should be in v1. The fact that pretty much all built-in elements with styleable parts have this makes me believe this is fundamental, not just a nice add-on. And the custom properties approach allows this.

The authlist should only apply to normal CSS properties, I think - all custom props should be implicitly allowed. You can disallow them by setting them from within the component with an !important, I think.

Tentatively, I don't see a problem with that. Alternately, having a token to represent allowing all custom properties (maybe "--*" to be cute?) would give both control and convenience.

@rniwa

This comment has been minimized.

Show comment
Hide comment
@rniwa

rniwa Feb 7, 2017

Contributor

I think we should have an allowed/blocked list of properties to be applied for each part.

Given we have custom properties, I think we should focus on the use cases that aren't served by custom properties, namely ::part, first especially since forward declaring a part would allow nested styling as well.

Also, some component may want to override the theme coming from the outside if the component requires a sub-component to always use a specific theme. e.g. a button component inside a social share button may always need to use its social network's theme color, and not page's theme color, but may still want to have a comment form to match the theme of the page.

Contributor

rniwa commented Feb 7, 2017

I think we should have an allowed/blocked list of properties to be applied for each part.

Given we have custom properties, I think we should focus on the use cases that aren't served by custom properties, namely ::part, first especially since forward declaring a part would allow nested styling as well.

Also, some component may want to override the theme coming from the outside if the component requires a sub-component to always use a specific theme. e.g. a button component inside a social share button may always need to use its social network's theme color, and not page's theme color, but may still want to have a comment form to match the theme of the page.

@morewry

This comment has been minimized.

Show comment
Hide comment
@morewry

morewry Feb 9, 2017

(2) It seems like ::theme() is only useful for open mode shadow DOMs that have not done appropriate forwarding for further shadow DOMs that they contain. Is that really a big enough use case? The forwarding feature seems like a cleaner way to handle this, and one that also works with closed-mode shadow DOMs.

I thought the same thing reading the initial draft.

One of the objections I heard from Polymer folk about dropping @apply for ::part() was that is made it more difficult to style all the buttons on the page, or similar. This was easy with @apply - just set --button-styles: {...}; and you were good - unless someone in the chain explicitly blocks that property, every button in the subtree will receive the styling.

I wouldn't want the outcome of dropping @apply for ::part(). My understanding is that both are candidates for standardization still and that neither supersedes the other--is that right? I think each style option: custom properties, @apply with custom property sets, and named ::parts are differently useful and each could be individually selected for a component based on what's most appropriate or combined together facilitate different levels of customization.

morewry commented Feb 9, 2017

(2) It seems like ::theme() is only useful for open mode shadow DOMs that have not done appropriate forwarding for further shadow DOMs that they contain. Is that really a big enough use case? The forwarding feature seems like a cleaner way to handle this, and one that also works with closed-mode shadow DOMs.

I thought the same thing reading the initial draft.

One of the objections I heard from Polymer folk about dropping @apply for ::part() was that is made it more difficult to style all the buttons on the page, or similar. This was easy with @apply - just set --button-styles: {...}; and you were good - unless someone in the chain explicitly blocks that property, every button in the subtree will receive the styling.

I wouldn't want the outcome of dropping @apply for ::part(). My understanding is that both are candidates for standardization still and that neither supersedes the other--is that right? I think each style option: custom properties, @apply with custom property sets, and named ::parts are differently useful and each could be individually selected for a component based on what's most appropriate or combined together facilitate different levels of customization.

@seaneking

This comment has been minimized.

Show comment
Hide comment
@seaneking

seaneking Feb 9, 2017

Echoing @morewry, wouldn't want to lose mixins. They're very powerful for doing what authors have been doing with preprocessor mixins for years (mixing in a bag of styles in many places), and don't see why they couldn't work hand in hand with parts should the need arise.

seaneking commented Feb 9, 2017

Echoing @morewry, wouldn't want to lose mixins. They're very powerful for doing what authors have been doing with preprocessor mixins for years (mixing in a bag of styles in many places), and don't see why they couldn't work hand in hand with parts should the need arise.

@tabatkins

This comment has been minimized.

Show comment
Hide comment
@tabatkins

tabatkins Feb 10, 2017

Member

I thought the same thing reading the initial draft.

I anticipated this reaction, and I probably should have been more explicit about what it's doing: ::theme() is exactly the same, mechanically, as @apply. Literally, modulo some unimportant corners of the functionality, the two are doing precisely the same thing - letting you target a component arbitrarily far down in the shadow hierarchy.

It's ::part() that's new and restrictive, compared to @apply - it's also the more common case, and better for avoiding shooting yourself in the foot. Foot-shooting is much easier with ::theme(), but it's also more obvious; if you didn't realize that @apply was precisely the same amount of foot-shooty, it's just because it was better at hiding its problems from plain sight. ^_^

I wouldn't want the outcome of dropping @apply for ::part(). My understanding is that both are candidates for standardization still and that neither supersedes the other--is that right? I think each style option: custom properties, @apply with custom property sets, and named ::parts are differently useful and each could be individually selected for a component based on what's most appropriate or combined together facilitate different levels of customization.

No, not really; they're attacking the exact same problem space, and don't even have differing ergonomics to fall back on (like, say, @mixin and @extend do in Sass).

Ultimately, @apply is a bit of a dirty hack. It shifts CSS machinery into other parts of CSS, and in so doing loses a lot of functionality:

  • By using property names instead of selectors, you lose the ability to use pseudo-classes and pseudo-elements. You instead have to reinvent them yourself, manually, by creating additional variant property names and manually applying them to the selectors in question.
  • By using property values instead of declaration blocks, you lose the cascade. There can be precisely one winner who gets to set the whole block of properties (whoever wins the cascade for the custom property itself); you can't have a more specific selector set/override some of the properties without wiping out the whole thing and starting fresh.

@apply happened to be a relatively easy hack to define, is all, building directly on existing machinery. (Tho there were some really annoying niggling problems with transitions and animations that we had to work out, which complicates the implementation a decent bit.) I invented it and promoted it because the earlier attempts to define ::part() were never nailed down by anyone, and there were a number of important mechanics questions that hadn't been answered (which @apply answered automatically, by virtue of piggybacking on the existing custom property machinery). Now that I think I've worked out all those issues with ::part(), tho, I'm happy to support it as a substantially better approach to solving the styling problem.

The only loss is that @apply can also be used to style elements in the same light dom, while ::part() is explicitly only for elements in a shadow. But this usage suffers from the same problems I cited above; it's much better to just expose selector hooks for the elements you want to target and use CSS like normal. (Which is precisely what ::part() is doing - giving shadows the ability to expose selector hooks for their elements when they want it.)

Member

tabatkins commented Feb 10, 2017

I thought the same thing reading the initial draft.

I anticipated this reaction, and I probably should have been more explicit about what it's doing: ::theme() is exactly the same, mechanically, as @apply. Literally, modulo some unimportant corners of the functionality, the two are doing precisely the same thing - letting you target a component arbitrarily far down in the shadow hierarchy.

It's ::part() that's new and restrictive, compared to @apply - it's also the more common case, and better for avoiding shooting yourself in the foot. Foot-shooting is much easier with ::theme(), but it's also more obvious; if you didn't realize that @apply was precisely the same amount of foot-shooty, it's just because it was better at hiding its problems from plain sight. ^_^

I wouldn't want the outcome of dropping @apply for ::part(). My understanding is that both are candidates for standardization still and that neither supersedes the other--is that right? I think each style option: custom properties, @apply with custom property sets, and named ::parts are differently useful and each could be individually selected for a component based on what's most appropriate or combined together facilitate different levels of customization.

No, not really; they're attacking the exact same problem space, and don't even have differing ergonomics to fall back on (like, say, @mixin and @extend do in Sass).

Ultimately, @apply is a bit of a dirty hack. It shifts CSS machinery into other parts of CSS, and in so doing loses a lot of functionality:

  • By using property names instead of selectors, you lose the ability to use pseudo-classes and pseudo-elements. You instead have to reinvent them yourself, manually, by creating additional variant property names and manually applying them to the selectors in question.
  • By using property values instead of declaration blocks, you lose the cascade. There can be precisely one winner who gets to set the whole block of properties (whoever wins the cascade for the custom property itself); you can't have a more specific selector set/override some of the properties without wiping out the whole thing and starting fresh.

@apply happened to be a relatively easy hack to define, is all, building directly on existing machinery. (Tho there were some really annoying niggling problems with transitions and animations that we had to work out, which complicates the implementation a decent bit.) I invented it and promoted it because the earlier attempts to define ::part() were never nailed down by anyone, and there were a number of important mechanics questions that hadn't been answered (which @apply answered automatically, by virtue of piggybacking on the existing custom property machinery). Now that I think I've worked out all those issues with ::part(), tho, I'm happy to support it as a substantially better approach to solving the styling problem.

The only loss is that @apply can also be used to style elements in the same light dom, while ::part() is explicitly only for elements in a shadow. But this usage suffers from the same problems I cited above; it's much better to just expose selector hooks for the elements you want to target and use CSS like normal. (Which is precisely what ::part() is doing - giving shadows the ability to expose selector hooks for their elements when they want it.)

@Zambonifofex

This comment has been minimized.

Show comment
Hide comment
@Zambonifofex

Zambonifofex Feb 10, 2017

I find it really sad that @apply is going away. Not only I got accustomed to it (like I said), but I was also planning to use it for non-custom elements-related stuff too once @nested became a thing.

I guess I’ll have to think about another design for my stylesheets, then.

Zambonifofex commented Feb 10, 2017

I find it really sad that @apply is going away. Not only I got accustomed to it (like I said), but I was also planning to use it for non-custom elements-related stuff too once @nested became a thing.

I guess I’ll have to think about another design for my stylesheets, then.

@tabatkins

This comment has been minimized.

Show comment
Hide comment
@tabatkins

tabatkins Feb 10, 2017

Member

Echoing @morewry, wouldn't want to lose mixins. They're very powerful for doing what authors have been doing with preprocessor mixins for years (mixing in a bag of styles in many places), and don't see why they couldn't work hand in hand with parts should the need arise.

@apply really isn't very good at this, tho - it's roughly equivalent in power to a zero-arg @mixin from Sass (with the added benefit that you can set different values in different subtrees). Zero-arg mixins are convenient, but all the real useful mixins, in my experience, use arguments; you can only do pretty simple things with zero args.

@apply can't realistically be expanded into becoming an arg-full mixin syntax, either; at least not conveniently/readably. (If/when we invent "late-binding var()" you can get something approaching it, but it's not very convenient.)

Partial solutions, tho, have a tendency to suck the air out of the room for "real" solutions, unless you have an evolutionary path to the "full" solution. There isn't one for @apply to become a full mixin solution, tho; we'll just have to invent something new, and the existence of @apply already partially solving the problem will make creating a new thing much less attractive.

I'd rather just invent the full thing; getting it together will already be a huge fight. ^_^

Member

tabatkins commented Feb 10, 2017

Echoing @morewry, wouldn't want to lose mixins. They're very powerful for doing what authors have been doing with preprocessor mixins for years (mixing in a bag of styles in many places), and don't see why they couldn't work hand in hand with parts should the need arise.

@apply really isn't very good at this, tho - it's roughly equivalent in power to a zero-arg @mixin from Sass (with the added benefit that you can set different values in different subtrees). Zero-arg mixins are convenient, but all the real useful mixins, in my experience, use arguments; you can only do pretty simple things with zero args.

@apply can't realistically be expanded into becoming an arg-full mixin syntax, either; at least not conveniently/readably. (If/when we invent "late-binding var()" you can get something approaching it, but it's not very convenient.)

Partial solutions, tho, have a tendency to suck the air out of the room for "real" solutions, unless you have an evolutionary path to the "full" solution. There isn't one for @apply to become a full mixin solution, tho; we'll just have to invent something new, and the existence of @apply already partially solving the problem will make creating a new thing much less attractive.

I'd rather just invent the full thing; getting it together will already be a huge fight. ^_^

@matthewp

This comment has been minimized.

Show comment
Hide comment
@matthewp

matthewp Feb 10, 2017

How would you "forward" a part, I don't quite understand that.

matthewp commented Feb 10, 2017

How would you "forward" a part, I don't quite understand that.

@tabatkins

This comment has been minimized.

Show comment
Hide comment
@tabatkins

tabatkins Feb 10, 2017

Member

Say you're using an <x-button> component in your own shadow tree, and it exposes a "label" part that you'd like to expose as part of your own API. You can do <x-button part="label => button-label">, and now ::part(button-label) will work on you, and target the <x-button>s ::part(label).

Member

tabatkins commented Feb 10, 2017

Say you're using an <x-button> component in your own shadow tree, and it exposes a "label" part that you'd like to expose as part of your own API. You can do <x-button part="label => button-label">, and now ::part(button-label) will work on you, and target the <x-button>s ::part(label).

@matthewp

This comment has been minimized.

Show comment
Hide comment
@matthewp

matthewp Feb 10, 2017

Can you only forward a single part, then?

matthewp commented Feb 10, 2017

Can you only forward a single part, then?

@tabatkins

This comment has been minimized.

Show comment
Hide comment
@tabatkins

tabatkins Feb 10, 2017

Member

No, the part attribute takes a comma-separated list of commands. https://tabatkins.github.io/specs/css-shadow-parts/#part-attr

Member

tabatkins commented Feb 10, 2017

No, the part attribute takes a comma-separated list of commands. https://tabatkins.github.io/specs/css-shadow-parts/#part-attr

@seaneking

This comment has been minimized.

Show comment
Hide comment
@seaneking

seaneking Feb 11, 2017

Zero-arg mixins are convenient, but all the real useful mixins, in my experience, use arguments; you can only do pretty simple things with zero args

True, but I still think there's an argument to be made for theming with @apply since it works across both components/shadow roots and light DOM (vs. using classes for light DOM and ::theme for components).

Without @apply I imagine a lot of people (myself included) would fall back on mixins from their pre/post-processor of choice to achieve that kind of thing, so I guess the question is whether that should be baked into the platform or left to tooling?

seaneking commented Feb 11, 2017

Zero-arg mixins are convenient, but all the real useful mixins, in my experience, use arguments; you can only do pretty simple things with zero args

True, but I still think there's an argument to be made for theming with @apply since it works across both components/shadow roots and light DOM (vs. using classes for light DOM and ::theme for components).

Without @apply I imagine a lot of people (myself included) would fall back on mixins from their pre/post-processor of choice to achieve that kind of thing, so I guess the question is whether that should be baked into the platform or left to tooling?

@othermaciej

This comment has been minimized.

Show comment
Hide comment
@othermaciej

othermaciej Feb 11, 2017

Member

I anticipated this reaction, and I probably should have been more explicit about what it's doing: ::theme() is exactly the same, mechanically, as @apply. Literally, modulo some unimportant corners of the functionality, the two are doing precisely the same thing - letting you target a component arbitrarily far down in the shadow hierarchy.

I may not understand @apply very well. But it seems like @apply puts control in the hands of component authors for where stuff gets applied, and the expected API contract is that the client of the component provides some custom properties with the intended styles. But @theme applies style to all parts with a given name everywhere. And the component client has to decide whether to use @part or @theme. So the component's API contract is not just a specific name of the styling hook, but also the requirement to know whether they should use ::part or ::theme with it.

Meanwhile, forwarding provides a completely component-controlled way of handling it where the client should always just say ::part.

Components authored with a closed shadow DOM will have to be done with forwarding, and in that case ::theme is useless. So different kinds of components will have different API contracts for how to style them that are dependent on an authoring choice that shouldn't be relevant to this.

In brief, it seems like telling your users to use ::theme (or leaving the choice to them) is poor authoring practice, and explicitly forwarding part names is good practice. The fact that ::theme theoretically is similar in power in some sense to @apply is not a good reason to have it. Depth of styling should be controlled by the component, not the client of the component, and leaving it to client code adds a needless confusing decision.

Member

othermaciej commented Feb 11, 2017

I anticipated this reaction, and I probably should have been more explicit about what it's doing: ::theme() is exactly the same, mechanically, as @apply. Literally, modulo some unimportant corners of the functionality, the two are doing precisely the same thing - letting you target a component arbitrarily far down in the shadow hierarchy.

I may not understand @apply very well. But it seems like @apply puts control in the hands of component authors for where stuff gets applied, and the expected API contract is that the client of the component provides some custom properties with the intended styles. But @theme applies style to all parts with a given name everywhere. And the component client has to decide whether to use @part or @theme. So the component's API contract is not just a specific name of the styling hook, but also the requirement to know whether they should use ::part or ::theme with it.

Meanwhile, forwarding provides a completely component-controlled way of handling it where the client should always just say ::part.

Components authored with a closed shadow DOM will have to be done with forwarding, and in that case ::theme is useless. So different kinds of components will have different API contracts for how to style them that are dependent on an authoring choice that shouldn't be relevant to this.

In brief, it seems like telling your users to use ::theme (or leaving the choice to them) is poor authoring practice, and explicitly forwarding part names is good practice. The fact that ::theme theoretically is similar in power in some sense to @apply is not a good reason to have it. Depth of styling should be controlled by the component, not the client of the component, and leaving it to client code adds a needless confusing decision.

@tabatkins

This comment has been minimized.

Show comment
Hide comment
@tabatkins

tabatkins Feb 13, 2017

Member

I may not understand @apply very well. But it seems like @apply puts control in the hands of component authors for where stuff gets applied, and the expected API contract is that the client of the component provides some custom properties with the intended styles. But @theme applies style to all parts with a given name everywhere. And the component client has to decide whether to use @part or @theme. So the component's API contract is not just a specific name of the styling hook, but also the requirement to know whether they should use ::part or ::theme with it.

Not really. Like I said above, ::theme() is actually the almost-direct translation of @apply's functionality back into the selector space (where this functionality belongs). Every bad implication you can imagine ::theme() having, @apply has, because inheritance lets you set the custom property arbitrarily far up in the flat tree, and it'll work it's way down to the component unless explicitly blocked (by the custom property getting set by an element somewhere between the component and the first element to set it).

::part(), on the other hand, is the better-designed API that you actually want. It, plus part-forwarding, gives you access to precisely the parts that your component chooses to give to you, and it works consistently between open and closed shadows.

If we all collectively decided that we only wanted ::part(), that's okay with me. It's the good part of the proposal anyway. I included ::theme() because it avoids throwing away any power; this power is precisely what some Polymer people talked about as useful in their early feedback to me. And if we don't allow ::theme(), we don't actually shut anything down, we just make similar usage vastly less convenient (the "big bag of custom properties" approach I talk about in the spec).

Ultimately, if you can pass a single value arbitrarily far down the flat tree without the intervening elements having to do anything (besides just fail to stop you), it seems weird to not allow sets of values; disallowing it doesn't actually add any security whatsoever, just makes it less convenient for component authors and users.

The fact that ::theme theoretically is similar in power in some sense to @apply is not a good reason to have it.

Just to reiterate, it's not similar "in some sense" - it's exactly as powerful in a direct-translation sense - @apply and ::theme() are mirrors of each other, just living in different parts of the CSS syntax space. And @apply is only very slightly more powerful var(), just way more convenient for the use-case of allowing arbitrary styling on an element (only "more power" is that it allows setting arbitrary custom properties, which can't have their names predicted ahead of time).

Member

tabatkins commented Feb 13, 2017

I may not understand @apply very well. But it seems like @apply puts control in the hands of component authors for where stuff gets applied, and the expected API contract is that the client of the component provides some custom properties with the intended styles. But @theme applies style to all parts with a given name everywhere. And the component client has to decide whether to use @part or @theme. So the component's API contract is not just a specific name of the styling hook, but also the requirement to know whether they should use ::part or ::theme with it.

Not really. Like I said above, ::theme() is actually the almost-direct translation of @apply's functionality back into the selector space (where this functionality belongs). Every bad implication you can imagine ::theme() having, @apply has, because inheritance lets you set the custom property arbitrarily far up in the flat tree, and it'll work it's way down to the component unless explicitly blocked (by the custom property getting set by an element somewhere between the component and the first element to set it).

::part(), on the other hand, is the better-designed API that you actually want. It, plus part-forwarding, gives you access to precisely the parts that your component chooses to give to you, and it works consistently between open and closed shadows.

If we all collectively decided that we only wanted ::part(), that's okay with me. It's the good part of the proposal anyway. I included ::theme() because it avoids throwing away any power; this power is precisely what some Polymer people talked about as useful in their early feedback to me. And if we don't allow ::theme(), we don't actually shut anything down, we just make similar usage vastly less convenient (the "big bag of custom properties" approach I talk about in the spec).

Ultimately, if you can pass a single value arbitrarily far down the flat tree without the intervening elements having to do anything (besides just fail to stop you), it seems weird to not allow sets of values; disallowing it doesn't actually add any security whatsoever, just makes it less convenient for component authors and users.

The fact that ::theme theoretically is similar in power in some sense to @apply is not a good reason to have it.

Just to reiterate, it's not similar "in some sense" - it's exactly as powerful in a direct-translation sense - @apply and ::theme() are mirrors of each other, just living in different parts of the CSS syntax space. And @apply is only very slightly more powerful var(), just way more convenient for the use-case of allowing arbitrary styling on an element (only "more power" is that it allows setting arbitrary custom properties, which can't have their names predicted ahead of time).

@rniwa

This comment has been minimized.

Show comment
Hide comment
@rniwa

rniwa Feb 13, 2017

Contributor

Just to reiterate, it's not similar "in some sense" - it's exactly as powerful in a direct-translation sense - @apply and ::theme() are mirrors of each other, just living in different parts of the CSS syntax space. And @apply is only very slightly more powerful var(), just way more convenient for the use-case of allowing arbitrary styling on an element (only "more power" is that it allows setting arbitrary custom properties, which can't have their names predicted ahead of time).

"exactly as powerful" is a misleading statement given @apply can be used without any shadow trees even if there was an equivalent expressibility when it comes to styling parts of components across shadow boundaries. I can see some people may want to be using mix-ins outside the context of shadow trees, so for them @apply is a lot more useful than ::theme which seems to only work on parts defined inside a shadow tree.

Having said that, we're all for focusing on ::part first regardless of what happens to @apply vs ::theme since ::part is the feature what we always wanted.

Contributor

rniwa commented Feb 13, 2017

Just to reiterate, it's not similar "in some sense" - it's exactly as powerful in a direct-translation sense - @apply and ::theme() are mirrors of each other, just living in different parts of the CSS syntax space. And @apply is only very slightly more powerful var(), just way more convenient for the use-case of allowing arbitrary styling on an element (only "more power" is that it allows setting arbitrary custom properties, which can't have their names predicted ahead of time).

"exactly as powerful" is a misleading statement given @apply can be used without any shadow trees even if there was an equivalent expressibility when it comes to styling parts of components across shadow boundaries. I can see some people may want to be using mix-ins outside the context of shadow trees, so for them @apply is a lot more useful than ::theme which seems to only work on parts defined inside a shadow tree.

Having said that, we're all for focusing on ::part first regardless of what happens to @apply vs ::theme since ::part is the feature what we always wanted.

@matthewp

This comment has been minimized.

Show comment
Hide comment
@matthewp

matthewp Feb 13, 2017

I disagree that ::theme is unimportant. Having very deep shadow trees is going to be very common. If you have to manually forward each part at every level it is going to get quite verbose. I can imagine CSS frameworks like Bootstrap having widgets that contain 3 or 4 parts. With deep trees and each level adding new parts I can see the outer components might have an absurd number of things to forward.

::theme gives you an escape hatch for the cases where you have global-esque styles, such as when you use a CSS framework. Being able to use ::theme(bootstrap-form) and define the styles only once will be a big advantage.

matthewp commented Feb 13, 2017

I disagree that ::theme is unimportant. Having very deep shadow trees is going to be very common. If you have to manually forward each part at every level it is going to get quite verbose. I can imagine CSS frameworks like Bootstrap having widgets that contain 3 or 4 parts. With deep trees and each level adding new parts I can see the outer components might have an absurd number of things to forward.

::theme gives you an escape hatch for the cases where you have global-esque styles, such as when you use a CSS framework. Being able to use ::theme(bootstrap-form) and define the styles only once will be a big advantage.

@rniwa

This comment has been minimized.

Show comment
Hide comment
@rniwa

rniwa Feb 13, 2017

Contributor

I'm not discounting or denying the importance of use cases for @apply and ::theme here but I'd like to keep this thread's discussion focused on ::part. I think we need a separate thread for discussing the merits of @apply and ::theme.

Contributor

rniwa commented Feb 13, 2017

I'm not discounting or denying the importance of use cases for @apply and ::theme here but I'd like to keep this thread's discussion focused on ::part. I think we need a separate thread for discussing the merits of @apply and ::theme.

@jouni

This comment has been minimized.

Show comment
Hide comment
@jouni

jouni Mar 10, 2017

One thing I’ve been wondering with the new “custom shadow parts” proposal is how do we expose the host element for theming by default? Can we add a part attribute for the host as well (by the element itself, automatically)? Or is it always necessary to forward the host element as a new shadow part?

As a simple use case, if I have a button component I want to provide themes for, which are not bundled with the component, and I’m not the author of the button component. The component has only the host element, no extra elements/parts in shadow DOM.

So, I would like to offer users an additional stylesheet they can load, which would give a new default look for the button, but also offer some extra styles/variations for it, like “small”, “large” and “primary”.

<link rel="stylesheet" href="theme-for-nice-button.html">

<!-- This would now look different than the <nice-button> component looks without the theme -->
<nice-button>Button</button>

<!-- I would like to offer these kind of additional styles as well -->
<nice-button class="small primary">Small Primary Button</nice-button>

Could the <nice-button> component automatically add a new part value for itself, so the generated DOM would look like this:

<nice-button part="nice-button">Button</button>
<nice-button part="nice-button" class="small primary">Small Primary Button</nice-button>

And I could then write the following CSS in the theme I provide:

html::theme(nice-button) {
  /* My new default styles for nice-button */
}

html::theme(nice-button).primary {
  /* Additional styles for the primary button */
}

Am I completely off? Has this use case been considered in the “custom shadow parts” proposal?

jouni commented Mar 10, 2017

One thing I’ve been wondering with the new “custom shadow parts” proposal is how do we expose the host element for theming by default? Can we add a part attribute for the host as well (by the element itself, automatically)? Or is it always necessary to forward the host element as a new shadow part?

As a simple use case, if I have a button component I want to provide themes for, which are not bundled with the component, and I’m not the author of the button component. The component has only the host element, no extra elements/parts in shadow DOM.

So, I would like to offer users an additional stylesheet they can load, which would give a new default look for the button, but also offer some extra styles/variations for it, like “small”, “large” and “primary”.

<link rel="stylesheet" href="theme-for-nice-button.html">

<!-- This would now look different than the <nice-button> component looks without the theme -->
<nice-button>Button</button>

<!-- I would like to offer these kind of additional styles as well -->
<nice-button class="small primary">Small Primary Button</nice-button>

Could the <nice-button> component automatically add a new part value for itself, so the generated DOM would look like this:

<nice-button part="nice-button">Button</button>
<nice-button part="nice-button" class="small primary">Small Primary Button</nice-button>

And I could then write the following CSS in the theme I provide:

html::theme(nice-button) {
  /* My new default styles for nice-button */
}

html::theme(nice-button).primary {
  /* Additional styles for the primary button */
}

Am I completely off? Has this use case been considered in the “custom shadow parts” proposal?

@rniwa

This comment has been minimized.

Show comment
Hide comment
@rniwa

rniwa Mar 14, 2017

Contributor

No, that's a use case for @apply and ::theme, which should really be discussed in a separate thread.

Contributor

rniwa commented Mar 14, 2017

No, that's a use case for @apply and ::theme, which should really be discussed in a separate thread.

@jouni jouni referenced this issue Mar 14, 2017

Closed

vaadin-grid Polymer 2.0 support #710

5 of 9 tasks complete
@jouni

This comment has been minimized.

Show comment
Hide comment
@jouni

jouni Mar 14, 2017

Alright. Do we have that thread already somewhere?

jouni commented Mar 14, 2017

Alright. Do we have that thread already somewhere?

@tomalec

This comment has been minimized.

Show comment
Hide comment
@tomalec

tomalec Nov 28, 2017

Contributor

It was discussed at TPAC2017, the minutes are at https://www.w3.org/2017/11/10-webplat-minutes.html#item03

Contributor

tomalec commented Nov 28, 2017

It was discussed at TPAC2017, the minutes are at https://www.w3.org/2017/11/10-webplat-minutes.html#item03

@annevk

This comment has been minimized.

Show comment
Hide comment
@annevk

annevk Feb 19, 2018

Member

It seems this specification is still hosted in @tabatkins's private GitHub, despite there being feedback tracked here as well: https://github.com/w3c/csswg-drafts/issues?q=is%3Aissue+is%3Aopen+css-shadow-parts.

Will the CSS WG adopt this soonish?

Can we close this issue as this is now mostly a CSS WG matter?

Member

annevk commented Feb 19, 2018

It seems this specification is still hosted in @tabatkins's private GitHub, despite there being feedback tracked here as well: https://github.com/w3c/csswg-drafts/issues?q=is%3Aissue+is%3Aopen+css-shadow-parts.

Will the CSS WG adopt this soonish?

Can we close this issue as this is now mostly a CSS WG matter?

@rniwa

This comment has been minimized.

Show comment
Hide comment
@rniwa

rniwa Feb 19, 2018

Contributor

Again, we're strongly in favor of having this feature. We have a strong interest in implementing this feature in WebKit as well.

Contributor

rniwa commented Feb 19, 2018

Again, we're strongly in favor of having this feature. We have a strong interest in implementing this feature in WebKit as well.

@trusktr

This comment has been minimized.

Show comment
Hide comment
@trusktr

trusktr Feb 19, 2018

Not sure if this is the right place, but as a custom elements author, I'd like a way to define toggle :hover for custom elements.

EDIT: made a new issue for it: #738

trusktr commented Feb 19, 2018

Not sure if this is the right place, but as a custom elements author, I'd like a way to define toggle :hover for custom elements.

EDIT: made a new issue for it: #738

@tabatkins

This comment has been minimized.

Show comment
Hide comment
@tabatkins

tabatkins Feb 20, 2018

Member

It seems this specification is still hosted in @tabatkins's private GitHub, despite there being feedback tracked here as well: https://github.com/w3c/csswg-drafts/issues?q=is%3Aissue+is%3Aopen+css-shadow-parts.

No, I just haven't marked my personal copy as being obsolete. The spec is at https://drafts.csswg.org/css-shadow-parts/ and is officially tracked by the CSSWG.

Member

tabatkins commented Feb 20, 2018

It seems this specification is still hosted in @tabatkins's private GitHub, despite there being feedback tracked here as well: https://github.com/w3c/csswg-drafts/issues?q=is%3Aissue+is%3Aopen+css-shadow-parts.

No, I just haven't marked my personal copy as being obsolete. The spec is at https://drafts.csswg.org/css-shadow-parts/ and is officially tracked by the CSSWG.

@annevk

This comment has been minimized.

Show comment
Hide comment
@annevk

annevk Feb 21, 2018

Member

Let's close this then. I have updated https://github.com/w3c/webcomponents/blob/gh-pages/README.md to point out that proposal so we still have a centralized place to look at for where the various web component bits ended up.

Member

annevk commented Feb 21, 2018

Let's close this then. I have updated https://github.com/w3c/webcomponents/blob/gh-pages/README.md to point out that proposal so we still have a centralized place to look at for where the various web component bits ended up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment