w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.44k stars 657 forks source link

[css-shadow-parts][css-scoping] Allow ::part after ::slotted #3896

Open lilles opened 5 years ago

lilles commented 5 years ago

::slotted pseudo elements allow tree-abiding pseudo elements to follow according to [1], but ::part is not specified as a tree-abiding pseudo in [2] or [3]. Still, it would make sense to allow ::part after ::slotted. Also, ::placeholder, which essentially is a ::part is defined as tree-abiding in [2].

[1] https://drafts.csswg.org/css-scoping-1/#slotted-pseudo [2] https://drafts.csswg.org/css-pseudo-4/#treelike [3] https://drafts.csswg.org/css-shadow-parts-1/#part

tabatkins commented 5 years ago

We should spec ::part() as being tree-abiding, since it is.

lilles commented 5 years ago

Apart from the fact that it doesn't inherit from its originating element as the definition of tree-abiding says.

tabatkins commented 5 years ago

Ah, hm. That's not an important aspect for what we're discussing here tho. We should define the concept of an "aliasing pseudo-element" (as opposed to, hm, some-term-i-haven't-come-up-with-yet for pseudos that are created de novo), define that it's tree-abiding (by definition), and then split out the inheritance discussion to just define inheritance for the created pseudos, not aliased pseudos.

OnurGumus commented 4 years ago

I really think this will help web component styling to be more robust considering complex selectors are "slow" at least if we could style the parted parts of the slotted elements that would be great.

bennypowers commented 2 years ago

Case: Slide decks. Author wants to compose private notes for each slide, and display them onscreen only when the parent deck element has a presenter attribute.

<slide-deck>
  #shadow-root
    <style>
      /* ...etc */

      ::host([presenter]) ::slotted([active])::part(notes) {
        display: block;
        /* show notes for the active slide outside the presentation area */
        transform: translateY(30%);
      }
    </style>
    <!-- slides -->
    <slot></slot>

  <slide-slide>
    #shadow-root
      <style>#notes { display: none; }</style>
      <slot></slot>
      <div part="notes"><slot name="notes"></slot></div>

    <p>Slide Content!</p>
    <p slot="notes">Be exuberant when presenting this slide</p>
  </slide-slide>
</slide-deck>
carsonpowers commented 2 years ago

Adding to the point @bennypowers made (no relation): the inability to access the ::part of slotted web components feels like a significant flaw for web components in general.

Ironically, it makes web components a poor choice for building a component library since it leaves parent components with limited styling control over slotted child components.

clshortfuse commented 1 year ago

Note that the alternative of using CSS properties will cascade down the entire tree, instead of being short-circuited at the shadow DOM level when using ::part(). Not only is that a performance issue, but a scoping one as well.

The "Custom Elements way" is to use an attribute, but slotted elements shouldn't have to expose an attribute to its parent nor should a slotted element expect to have its attributes changed by a parent when it comes slotted. It would nice for custom elements to select stylings based on the type of child passed. For example, a custom element shouldn't have to have its tagname renamed or attribute added for no purpose other than because of its expected parent:

<nav-bar>
  <nav-item>link 1</nav-item>
  <nav-item>link 2</nav-item>
</nav-bar>

<nav-drawer>
  <nav-item>link 1</nav-item>
  <nav-item>link 2</nav-item>
</nav-drawer>

#nav-drawer-slot::slotted(nav-item)::part(icon) { font-size: 24px } would be very useful. CSS properties is one solution, but you still have to create those CSS properties in nav-item as well as worry about children of the immediate child being affected.

The alternative setup is either duplicate and rename the Custom Element and assign them, or tag them with an attribute:

<nav-bar>
  <nav-bar-item>link 1</nav-bar-item>
  <nav-bar-item>link 2</nav-bar-item>
</nav-bar>

<nav-drawer>
  <nav-item context=drawer>link 1</nav-item>
  <nav-item context=drawer>link 2</nav-item>
</nav-drawer>

Either way, styles would have to be scripted at the base (nav-item/nav-drawer-item) level, because the parent is unable to style it fully. That doesn't really make nav-item very extendable or customizable, despite having added a ::part(icon) in it's shadow DOM. Everytime you want to add a new type of container for nav-item, (eg: nav-rail), you'd have to create new styles within nav-item, the child, instead of the parent.

jlukic commented 5 months ago

Just adding my 2️⃣ 🪙 -- for SSR rendering of web components being able to use ::slotted::part would allow you handle correct styling of nested components from the shadow dom styles of the parent component without client side JS.

Currently any of the existing solutions like passing props or styles down to child components will cause page shift for the component when the nested css rules are applied when the component mounts.

p-bakker commented 2 weeks ago

Another use case:

Technical: targeting elements exposed as parts in slotted custom elements to apply a z-index with an 'outside'/the proper stacking context, so when those targeted elements also have position: sticky (and top: 0px), they stack on top of eachother when scrolling, instead of the current stuck element being pushed up (and out of view) when the next targeted element gets towards the top

User facing: a scrolling container with multiple slotted custom elements that have headers and a (varying length) content and when scrolling down you want the headers to stick to the top of the container (when scrolling down) and to be rendered on top of eachother (instead of the next header pushing the previous one upwards)

Right now the only way to achieve this (afaict) is to add the selector to target the parts to the global css