Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"::slotted" pseudo elements #331

Closed
hayatoito opened this issue Sep 28, 2015 · 47 comments
Closed

"::slotted" pseudo elements #331

hayatoito opened this issue Sep 28, 2015 · 47 comments

Comments

@hayatoito
Copy link
Contributor

Let's define the behavior of ::slotted (a tentative name).
I am aware that CSS Scoping should define it, but let's have a rough consensus here.

I remember that I had discussion, maybe in www-style?, that we should make ::content take only a simple selector, instead of a selector. Polymer guys told us that styling a direct child of an insertion point is the only use case of ::content in Polymer.

e.g.
::content(div)

That should be interpreted as ::content > div in the previous syntax.

We no longer support a selector as its argument, like ::content(div p)

We might want to make ::slotted have this restriction because this makes the implementation simpler.

See also #308 (comment) because the definition of ::slotted will depend on those incoming changes.

@JanMiksovsky
Copy link

I recently did a search through the Basic Web Components project looking for where we currently use ::content. We concur with Polymer: the only cases where we currently use ::content are trying to style the direct children of an insertion point.

Regarding the name of this selector, it seems that the term "slotted" here is being used as a synonym for "assigned". For consistency with the use of "assigned" in the spec, and with existing API members such as getAssignedNodes(), perhaps this selector could be called ::assigned. That would reduce the number of terms and concepts a developer needs to learn here.

@hayatoito
Copy link
Contributor Author

@JanMiksovsky . Thank you! That's exactly the information I'd like to see.

Regarding the name of this selector, I'm afraid that ::assigned might be a confusing name.

In this example,
https://w3c.github.io/webcomponents/spec/shadow/#composition-example

  • T::assigned(*) should match H and P.
  • However T.getAssignedNodes() is O and P.

@rniwa
Copy link
Collaborator

rniwa commented Sep 29, 2015

Yeah, if we're making this pseudo element flatten slot elements' assigned node lists, then we should probably refrain from using the term "assigned". Also, someone pointed out that "assigned" might sound too generic while "slotted" sounds a lot more related to slot elements.

@hayatoito
Copy link
Contributor Author

We don't need to wait for #308 (comment). The following (tentative) definition could be enough:

A::slotted(simple_selector) matches a node B if and only if:

  1. B is a member of the distributed nodes of A
  2. and, simple_selector matches B

@TakayoshiKochi
Copy link
Member

Let me confirm:

For Shadow DOM V0, the following pattern is often used:
::content > div { color: blue; }

The color: blue is applied to <div>, not the parenting <content>.
(and in the selector matching, ::content matches <content>, of course)

In the proposed ::slotted() for V1, an author would expect the same as above by:
::slotted(div) { color: blue; }

If @hayatoito's comment above is adopted, the color: blue is applied to <div>
(which is node B in the comment), not the parenting <slot> (which is node A).

Obviously if an author wants to style <slot> element itself,
it can write slot { color: blue; } and no fancy pseudo class/element should be required,
so the matching logic in the above comment should be correct, but let me confirm.

(I was a bit confused by the name, btw)

@hayatoito
Copy link
Contributor Author

Let me clarify the definition:

A::slotted(simple_selector) matches a node B if and only if:

  1. Selector A matches a slot, called SLOT
  2. B is a member of the distributed nodes of SLOT
  3. and, simple_selector matches B

Does it make sense?

@TakayoshiKochi
Copy link
Member

TakayoshiKochi commented Dec 18, 2015

Thanks, it is clearer now.

Another question: Do we allow ::slotted without function-style parameter?
In that case, any element slotted under <slot> would match the rule?
In the default slot case, text node could match the rule, then?

E.g. ::slotted would be almost equivalent to ::slotted(*), but if a text node is slotted under a
<slot>, it might be different.

In shadow's style:

::slotted { color: green; }
::slotted(*) { color: blue; }

where in this HTML:

<div>
   :shadow-root
       <slot name="blue"></slot>
       <slot></slot>
   <div slot="blue">This text should be in blue.</div>
   <div>(1)Is this color blue?</div>
   (2)Is this color green?
</div>

@TakayoshiKochi
Copy link
Member

In my opinion, ::slotted is allowed, but it must be equivalent to *::slotted(*) and it never matches
slotted text node.

@hayatoito
Copy link
Contributor Author

In a general rule, you cannot select text nodes with CSS selector, even with *. :)

@hayatoito
Copy link
Contributor Author

"::slotted" (without parameter) is disallowed. It is invalid as if "div >" is invalid as a selector.

@hayatoito
Copy link
Contributor Author

A::slotted(simple_selector) matches a node B if and only if:

  1. Selector A matches a slot, called SLOT
  2. B is a member of the distributed nodes of SLOT
  3. and, simple_selector matches B

Ops. I meant a "compound selector" here, instead of a "simple selector". The motivation is to disallow "combinator" being used.

Thus, the definition should be:

A::slotted(compound_selector) matches a node B if and only if:

  1. Selector A matches a slot, called SLOT
  2. B is a member of the distributed nodes of SLOT
  3. and, compound_selector matches B

e.g. slot[name="slot1"]::slotted(div.foo)

I expect that this does not increase the difficulty of the implementation, however, this gives us much flexibility. :)

@ghost
Copy link

ghost commented Jan 11, 2016

How do you target pseudo elements of the slotted element?

::slotted(div.foo::before) or ::slotted(div.foo)::before ?

@hayatoito
Copy link
Contributor Author

Good question. I've never thought this case. :)

::slotted(div.foo::before)

This did not work, as intended, IMO. We might not want to support this because a pseudo element, which "::before" matches, is not a member of a distributed nodes of SLOT. This should not match.

or ::slotted(div.foo)::before ?

How to support this kind of consecutive pseudo elements is up to each pseudo element. In this case, it's up to "::slotted" pseudo element.

In a general rule, selectors have a limitation about what can be followed after a pseudo element.
I'm +1 not to support this unless we have a strong use case.

I'd like "::slotted" be valid only when it is the last item in a selector, as of now.

@ghost
Copy link

ghost commented Jan 12, 2016

On Tue, Jan 12, 2016 at 2:53 AM, Hayato Ito [email protected]
wrote:

Good question. I've never thought this case. :)

::slotted(div.foo::before)

This should not behave as intended, IMO. We might not want to support
this because a pseudo element, which "::before" matches, is not a
member of a distributed nodes of SLOT. This should not match.

or ::slotted(div.foo)::before ?

How to support this kind of consecutive pseudo elements is up to each
pseudo element. In this case, it's up to "::slotted" pseudo element.

In a general rule, selectors have a limitation about what can be followed
by a pseudo element.
I'm +1 not to support this until we have a strong use case of this.

Not a strong use case perhaps, but:

Rune

where the shadow tree for is:

list:shadow

<style> ::slotted(item)::before { content: attr(label) ":" } </style> /list:shadow

Fwiw, this currently works with ::content in Blink using "::content
item::before".

Also, this is quite similar to :host and :host-context. They happen to be
pseudo classes, not pseudo elements, but they select real dom elements in a
different (shadow) tree than the selector's origin, just like ::slotted.
:host()/:host-context()::before currently works in Blink.

In a general rule, the support is poor

Note: Note that, unless otherwise specified in a future specification,
pseudo-classes other than the user action pseudo-classes are not valid when
compounded to a pseudo-element; so, for example, ::before:first-child is an
invalid selector.


Reply to this email directly or view it on GitHub
#331 (comment).

Rune Lillesveen

@hayatoito
Copy link
Contributor Author

Yeah, I do not have a strong opinion on this as long as the support cost do not get increased.

AFAIR, the selector spec said that a selector can not have multiple pseudo elements being used at the same time, but it looks this limitation was removed from the editor's draft.

Regarding with ::before and ::after, I think it's okay to support if we can support that easily.

@tabatkins,
Can we allow multiple pseudo elements in a row? e.g. ::slotted(div)::before
Is this a right direction?

@hayatoito
Copy link
Contributor Author

I think there is no significant contentious bits about ::slotted pseudo elements.

@tabatkins, could you update the CSS Scoping? Is the current rough definition good enough to spec it in the CSS Scoping?

@annevk
Copy link
Collaborator

annevk commented Feb 17, 2016

Well, weren't @rniwa and @hober saying that they would prefer styling <slot> directly? If you can do that, why would we have ::slotted?

@hayatoito
Copy link
Contributor Author

"::slotted" is used to style a distributed node directly, having a more power.

e.g.

slot::slotted(div.red) {
  color: red;
}
slot::slotted(div.green) {
  color: green;
}

The following might be roughly equivalent to style a <slot>.

slot::slotted(*) {
  color: green;
}

@annevk
Copy link
Collaborator

annevk commented Feb 17, 2016

Fair.

@rniwa
Copy link
Collaborator

rniwa commented Feb 26, 2016

Does a distributed node get all rules from each slot that it went through applied?

Let's say an element X is assigned to a slot A, and the slot A is assigned to another slot B. So the X's final slot is B. Now, let's say both A's shadow DOM and B's shadow DOM have ::slotted rules that match X.

Should the rule in A's shadow DOM be applied to X? Or would it be ignored and only the rule in B's shadow DOM is applied?

@hayatoito
Copy link
Contributor Author

Does a distributed node get all rules from each slot that it went through applied?

Yes, that's an intended behavior. A distributed node gets all rules from each slot.

::content, the former name of ::slotted, has a similar behavior. A distributed nodes gets all rules from all insertion points. This behavior has not changed in this sense.

I am no sure which is desired behavior, each slot or only final slot. I do not recall it being discussed.

kisg pushed a commit to paul99/webkit-mips that referenced this issue Feb 26, 2016
https://bugs.webkit.org/show_bug.cgi?id=149441
<rdar://problem/22731987>

Reviewed by Andreas Kling.

Source/WebCore:

Based on latest in WICG/webcomponents#331

* css/CSSGrammar.y.in:

    Parse ::slotted.

* css/CSSParser.cpp:
(WebCore::CSSParser::detectFunctionTypeToken):
* css/CSSParserValues.cpp:
(WebCore::CSSParserSelector::parsePseudoElementCueFunctionSelector):
(WebCore::CSSParserSelector::parsePseudoElementSlottedFunctionSelector):

    Tokenize ::slotted.

(WebCore::CSSParserSelector::parsePseudoClassAndCompatibilityElementSelector):
* css/CSSParserValues.h:
* css/CSSSelector.cpp:
(WebCore::CSSSelector::pseudoId):
* css/CSSSelector.h:
* css/ElementRuleCollector.cpp:
(WebCore::ElementRuleCollector::matchAuthorRules):
(WebCore::ElementRuleCollector::matchHostPseudoClassRules):
(WebCore::ElementRuleCollector::matchSlottedPseudoElementRules):

    Match ::slotted selector.

(WebCore::ElementRuleCollector::collectSlottedPseudoElementRulesForSlot):

    Collect ::slotted rules that may apply to an element in a slot.

(WebCore::ElementRuleCollector::matchUserRules):
(WebCore::ElementRuleCollector::matchUARules):
(WebCore::findSlottedPseudoElementSelector):
(WebCore::ElementRuleCollector::ruleMatches):
* css/ElementRuleCollector.h:
* css/RuleSet.cpp:
(WebCore::RuleSet::addRule):

    Collect ::slotted rules.

(WebCore::RuleSet::shrinkToFit):
* css/RuleSet.h:
(WebCore::RuleSet::hostPseudoClassRules):
(WebCore::RuleSet::slottedPseudoElementRules):
(WebCore::RuleSet::focusPseudoClassRules):
(WebCore::RuleSet::universalRules):
* css/SelectorChecker.cpp:
(WebCore::SelectorChecker::checkOne):
* style/StyleSharingResolver.cpp:
(WebCore::Style::SharingResolver::resolve):

    Disable style sharing for children of shadow host. They may be affected by the shadow tree style
    which is not considered in style sharing checks.

LayoutTests:

* fast/shadow-dom/css-scoping-shadow-slotted-rule.html:

    Enable the test, fix it and update it to the current spec.

* fast/shadow-dom/slotted-pseudo-element-css-text-expected.txt: Added.
* fast/shadow-dom/slotted-pseudo-element-css-text.html: Added.

    Add parsing/cssText test based on a Blink test.
    There are a few failures due to * not roundtripping and the parser being too lenient with pseudo elements.

* platform/mac/TestExpectations:



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@197165 268f45cc-cd09-0410-ab3c-d52691b4dbfc
kisg pushed a commit to paul99/webkit-mips that referenced this issue Feb 29, 2016
https://bugs.webkit.org/show_bug.cgi?id=154765
<rdar://problem/24870995>

Reviewed by Ryosuke Niwa.

Source/WebCore:

See WICG/webcomponents#331 (comment)

Test: fast/shadow-dom/css-scoping-shadow-slotted-nested.html

* css/ElementRuleCollector.cpp:
(WebCore::ElementRuleCollector::matchSlottedPseudoElementRules):

Collect ::slotted rules from all the nested shadow trees instead of just the host's.

LayoutTests:

* fast/shadow-dom/css-scoping-shadow-slotted-nested-expected.html: Added.
* fast/shadow-dom/css-scoping-shadow-slotted-nested.html: Added.



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@197316 268f45cc-cd09-0410-ab3c-d52691b4dbfc
@TakayoshiKochi
Copy link
Member

Here's the example for @rniwa
http://jsbin.com/yequpu/edit?html,js,output

<div id="host">
  :shadow-root
    <style>::slotted(span) { color: green; background-color: yellow; }</style>
    <div id="host2">
      :shadow-root
        <style>::slotted(span) { color: red; }</style>
        <slot name="slot2"></slot>
      <slot name="slot1" slot="slot2"></slot>
    </div>
  <span slot="slot1">Hello, Shadow DOM</span>
</div>

In current Blink's implementation, first { color: green; background-color: yellow; } is applied,
then { color: red; } is applied on top of it -> result is { color: red, background-color: yellow; }.

@ghost
Copy link

ghost commented Mar 7, 2016

Just a note. We still use our broken cascade order from v0 in Blink, so we shouldn't trust the current order from Blink in general right?

@rniwa
Copy link
Collaborator

rniwa commented Mar 7, 2016

Yeah, we just implemented that behavior in http://trac.webkit.org/changeset/197316.

@hayatoito hayatoito changed the title Define the behavior of '::slotted' (the former name is '::content'). '::slotted' (the former name is '::content') pseudo elements Mar 17, 2016
@hayatoito hayatoito changed the title '::slotted' (the former name is '::content') pseudo elements "::slotted" pseudo elements Mar 17, 2016
@hayatoito
Copy link
Contributor Author

@TakayoshiKochi
Could you follow-up? It looks there is no reaction in www-style, as I expected. :)

@TakayoshiKochi
Copy link
Member

I'm contacting @tabatkins to get the spec updated.

@tabatkins
Copy link

All done, and reply sent to the list. https://lists.w3.org/Archives/Public/www-style/2016Apr/0239.html Please review!

@tabatkins
Copy link

Oh yeah, I haven't yet defined pseudo-element handling. Which do y'all prefer:

  1. ::slotted(.foo::before)
  2. ::slotted(.foo)::before

I don't have a strong opinion either way.

@ghost
Copy link

ghost commented Apr 14, 2016

I think I'd prefer ::slotted(.foo)::before because it's similar to how you add ::before/::after on hosts.

Blink currently supports ::before/::after on hosts using

:host(.foo)::before

not

:host(.foo::before)

:host-context(.foo::before) would be strange also.

@tabatkins
Copy link

Right, that's because :host() always represents the host element; it just uses the argument to query the tree to see if it should match or not. Putting ::before inside the argument wouldn't make any sense - you dont' want to check if an ancestor has a ::before pseudo-element.

::slotted, on the other hand, uses its argument to decide which of its assigned slotables it's supposed to represent. As such, it makes a little more sense to have ::before inside.

(At least, it makes as much sense as using doing something like :matches(.foo, .foo::before), which is allowed.)

I don't have a problem with ::slotted(.foo)::before, it just means dealing with chained pseudo-elements. Which we had before, so it's probably not a problem.


I'll also note that if you're drawing parallels between :host() and ::slotted(), authors probably will too - they select up and down outside the shadow tree. Having them work as similarly as possible (main difference being the number of colons) is probably good for comprehension. So that's an argument for ::slotted()::before.

@annevk
Copy link
Collaborator

annevk commented Apr 15, 2016

It seems weird though since it addresses a box inside .foo, not inside ::slotted.

@hayatoito
Copy link
Contributor Author

hayatoito commented Apr 15, 2016

Thank you, @tabatkins !

I reviewed quickly. Here are my comments:

  1. Shadow Encapsulation

The descendants of a shadow host (its light tree) must not generate boxes in the formatting tree. Instead, the contents of the shadow tree generate boxes as if they were the contents of the shadow host instead.
In several instances in shadow DOM, elements don’t have element parents (instead, they may have a shadow root as parent, or something else). An element without a parent, or whose parent is not an element, is called a top-level element.
While a shadow host’s light tree does not generate boxes normally, its light tree direct children can be explicitly pulled into a shadow tree and forced to render normally. This is done by marking the child as slotable and then, in the shadow tree, creating a slot set to receive the slotable, making the element assigned to the slot. (Anonymous text nodes that are direct children are always slotable.)

Since the actual logic, "Which elements generate a box?", should be explained by the concept of the flat tree ,
can we avoid to explain that in details here?

I'm wondering how to state it simply in css scoping. The current Shadow DOM spec has the followings in the section of Flat trees:

A document flat tree is a flat tree whose root node is a document
A node is in a document flat tree if it participates in a document flat tree.
Unless an element is in a document flat tree, the element must not create any CSS box.
In resolving CSS inheritance, an element must inherit from the parent node in the flat tree, if applicable.
User agents must use the document flat tree in the visual formatting model, instead of the document tree.

I think we should upstream these sentences to CSS scoping. I guess these sentences need to be refined somehow to match the css scoping world.

DOM Standard does not need a flat tree. I think only css scoping is a client of a flat tree. Thus, eventually, we need to upstream a flat tree to CSS.

3.1. Shadow DOM Selection Model

Elements in the DOM have zero or more shadow trees.

This can be:

Elements in the DOM have zero or one shadow tree.

Yeah, multiple shadow trees are removed.
Otherwise, we might want to omit this because it looks a duplication of the HTML Standard.

3.1.1. Shadow Hosts in a Shadow Tree

For the purpose of Selectors, a shadow host also appears in each of its shadow trees, with the contents of the shadow tree treated as its children. If an element has multiple shadow trees, it appears in each shadow tree’s context independently; each shadow tree sees itself as the contents of the shadow host, not the other shadow trees.

This can be:

For the purpose of Selectors, a shadow host also appears in its shadow tree, with the contents of the shadow tree treated as its children.

because multiple shadow trees are removed.

3.1.2. Slots and Slotted Elements in a Shadow Tree

If a slot is not assigned any of its shadow host’s children, that slot’s own children are assigned to it. Otherwise, a slot’s DOM children must not generate any boxes, as if they were display:none.

Slots can, themselves, be assigned to a slot in a deeper shadow tree. If this occurs, the elements assigned to the first slot are also treated as being assigned to the deeper slot. The deepest slot that an element is assigned to is its final assigned slot.

Elements assigned to a slot must generate boxes as if they were children of their final assigned slot (rather than children of their actual parent in a shadow host’s light tree).

I think we can remove these sentences. If we use the flat tree in the previous section, we do not need to mention a slot here as a special case.

3.2.2. Selecting Slot-Assigned Content: the ::slotted() pseudo-element

The ::slotted() pseudo-element represents the elements assigned to a slot. This pseudo-element only exists on slots.

"::slotted()" represents "distributed nodes", instead of "assigned nodes".
"assigned nodes" and "distributed nodes" are different.

I think you can refer this as the definition of "distributed".

@TakayoshiKochi
Copy link
Member

In 3.2.2,

::slotted( <compound-selector>? )

If the <compound-selector> is omitted, it defaults to the universal selector.

We haven't discussed this in this issue before, and when I implemented in Blink I made the <compound-selector> mandatory, not be omitted. As :host has 2 forms (:host and :host()) and the latter the parameter is not omissible, the difference may be confusing.
@tabatkins, can this <compound-selector> be mandatory parameter and author must specify * explicitly if it wants?

@tabatkins
Copy link

@annevk ::slotted does not represent anything on its own; it's an aliasing pseudo-element (the first of its kind, I guess) rather than a box-creating pseudo-element. In other words, it functions kinda like a combinator, but with limitations (one compound selector).

@TakayoshiKochi Yeah, I can make the argument mandatory.

@hayatoito Thanks for the review!

I hadn't caught that multiple shadow roots were removed; I'll fix.

For the rest of your comments, I will (a) rephrase the explanatory part to be clearer that I'm not trying to redefine anything from DOM, just provide some framing for everything else in the spec (so people don't have to go read and understand DOM just to understand what I'm talking about), and (b) pull in all the flattening/distributing stuff so you can remove it from Shadow DOM.

Between DOM and CSS Scoping, it looks like nearly all of Shadow DOM is getting eaten by other specs, so we should be able to remove most or all of it soon!

@tabatkins
Copy link

I've finished all the edits; they ended up necessitating a larger reorg/rewrite than I'd expected! Scoping now defines the flattened tree, too.

@rniwa
Copy link
Collaborator

rniwa commented May 5, 2016

Closing this issue because the behavior is largely defined in the CSS scoping spec. We can file a new issue to track the remaining issues if there are any.

@ghost
Copy link

ghost commented May 22, 2020

Stop closing this issue, people won't suddenly forget they need this.

@rniwa
Copy link
Collaborator

rniwa commented May 22, 2020

Stop closing this issue, people won't suddenly forget they need this.

?? ::slotted is already supported by all major browser engines as far as I know. If you have specific issues to discuss, file a new issue in CSS scooping spec. There is nothing left to discuss in the broader web components community at the moment.

@ghost
Copy link

ghost commented May 22, 2020

Yes, ::slotted is supported everywhere, but ::slotted still doesn't allow to styling child nodes, which make them completely useless for anyone who care about UI.. Perhaps there's a way i don't know? But i sure as hell search for it!! If not, what is the purpose of a container who can't style it's content?
""::slotted() should full support complex selector"" has came up multiple time and always get closed without any solution.

@rniwa
Copy link
Collaborator

rniwa commented May 22, 2020

""::slotted() should full support complex selector"" has came up multiple time and always get closed without any solution.

That should be tracked in a new issue, not in this old issue about ::slotted pseudo element. Also, supporting complex selector inside ::slotted would be about matching selector in the tree in which slotted node appears, not about selecting arbitrary descendant of slotted content. Regardless, you should file a new issue. We can't tack every new idea into an existing issue.

@Lonniebiz
Copy link

What a disappointing arbitrary limitation. Let the browser-makers solve performance issues and write the spec to be ideal. WEAK!

@bathos
Copy link

bathos commented Oct 7, 2020

@Lonniebiz I’m not sure this is as arbitrary as it seems at first. If I’ve understood right, the performance cost stems from a mathematical truth rather than browser implementation details. (Correct me if I’m mistaken.)

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

No branches or pull requests

8 participants