w3c / csswg-drafts

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

[css-shadow-parts-1] feedback from devs #3467

Open fergald opened 5 years ago

fergald commented 5 years ago

spec

I've created this issue as a place to log feedback from developers who have tried using shadow parts. Even if there are features missing, knowing that the basic shape of the API is right and e.g. already works well for some set of components is useful.

stefsullrew commented 5 years ago

::part feedback + thoughts

Below are thoughts, questions, and general feedback from the SLDS Framework team regarding the currently implemented in Chrome. We're experimenting a little further with slots and some more complex examples, so I expect we'll add a couple other questions with examples.

Feedback

Access control

It would be very useful if it was possible to limit access to CSS rules for a defined part. The purpose is to limit the styling API of a component by only allowing certain things to be overridden or to lock down certain things that should never be overridden.

In the example below “part='title'” is defined but only “color” and “background-color” can be overridden, all other rules would be ignored.

We envision allow all by default with the possibility to allow or deny specific lists of CSS rules.

<x-foo>
    <h2 part="title" part-allow="color, background-color">Access control</h2>
    <slot></slot>
</x-foo>
x-foo::part(title) {
    color: purple; // allowed
    bacgkround-color: grey; // allowed
    height: 100%; // denied
}

In the example below “part='title'” is defined but “color” and “background-color” are locked, all other rules would apply and override the component's default styles.

<x-foo>
    <h2 part="title" part-deny="color, background-color">Access control</h2>
    <slot></slot>
</x-foo>
x-foo::part(title) {
    color: purple; // denied
    background-color: grey; // denied
    height: 100%; // allowed
}

Limited ::theme selector

The current description of the ::theme selector is perhaps a bit too broad for real world usage. However the ability to access any ::part within the context of a component, without elaborate exportparts= chains would be much easier to work with in the context of a design system.

In the example below, a theme context is defined for **x-foo** with the prefix of “foo-. This would grant access to all child parts that match the name within the declared theme's context. This scoping limits the reach of ::theme while avoiding the potentially complex exportparts= chains. Particularly when there's deep composition of components not owned by a single team.

<x-foo theme="foo-">
    <x-bar></x-bar>
    <x-oof></x-oof>
</x-foo>

// x-bar definition
<x-bar>
    <p part="metadata">Posted on: 2019-01-10</p>
</x-bar>

// x-oof definition
<x-oof>
    <svg part="icon"></svg>
</x-oof>
x-foo::theme(foo-metadata) {
    color: grey;
    font-style: italic;
}

Web Component default exportparts=

When building a custom web component via composition not having to explicitly specify exportparts= would improve the experience.

The example below presents a possible solution where, at component creation, exportparts= is defined.

class BarElement extends HTMLElement {
  constructor() {
    super();

    const shadowRoot = this.attachShadow({mode: 'closed'});

    shadowRoot.innerHTML = `
        <h1 part="header">Headline</h1>
        <p part="metadata">Posted on: 2019-01-10</p>
        <div part="content">...</div>
    `;

    if (!this.hasAttribute('exportparts'))
        this.setAttribute('exportparts', 'header, metadata, content');
  }
}

customElements.define('bar-element', BarElement);
<x-foo>
    <!-- iteration of bar-elements -->
    <!-- since no manual "exportparts=" was specified on each instance
         the defaults are exported -->
    <bar-element></bar-element>
    <bar-element></bar-element>
    ...
</x-foot>
x-foo::part(header) {
    color: green;
}

Questions

Dynamic exportparts=

Would it be possible to dynamically set the value of exportparts=? The below example show some pseudo-code to illustrate the question.

<x-foo>
    <h1>Component headline</h1>

    <template for:each={items} for:item="item" for:index="idx">
        <x-item exportparts="item-header: foo-item-header-{idx}"></x-item>
    </template>
</x-foo>

::before and ::after pseudo elements

It appears the ::before and ::after pseudo elements/selectors do not function with ::part. Is this intentionally blocked, if so why? If not are there plans to add this functionality in the future?

// add an asterisk prefix to the title part
x-foo::part(title)::before {
    content: '*';
    color: red;
    margin-right: 0.25rem;
}
frivoal commented 5 years ago

This spec is not my main area of expertise, so I'll refrain from commenting on the substance until I've studied it further, but a comment on the form: usually things are much easier to process when there's one "issue" per github issue. If you want to have a master issue that links to all of them, that's fine, and we can cross link as needed, but splitting each topic into its own issue lets the CSSWG get to the bottom of each one by one, track their status independently, assign them, schedule them for discussion in teleconfs, etc.

I've reposted each point from @stefsullrew's comment (https://github.com/w3c/csswg-drafts/issues/3467#issuecomment-453678965) into separate issues:

stefsullrew commented 5 years ago

Thanks so much, Florian.

fergald commented 5 years ago

Thanks @frivoal I understand your point about having separate issues and thanks for analyzing the content and creating them. The reason I created this general issue is that I was trying to get as much feedback as possible, including feedback that was purely supportive of the existing spec (for purposes of launching). I think it's easier for people to leave feedback (especially positive feedback) in a more narrative and less precise form. A github issue isn't the ideal way to do this but I'm not sure what would be a better open forum.

klasjersevi commented 4 months ago

I've been struggling with shadow parts for a little while now in a project with many nested web components that we want to provide external styling for. The current definition requires a lot of bloated extra code for us to achieve external styling possibilities.

exportparts

For nested components, this is very messy way of making sure everything is exported all the way to the root. A general simpler (default) way would increase the developer experience a lot. As a workaround to avoid maintenance hell, we are using a mixin that finds parts and exportparts in the subtree and forwards them on the component if needed.

parts are not selectable within the shadow dom

When defining a part name, why not make it selectable from stylesheets within the shadow dom? To select a part you have to use something like :host::part(my-part) or an attribute selector [part~=my-part] instead of simply just using ::part(my-part). It would also be easier for our third-party component users (developers) if they could just see and use the same selector as they would find in the web inspector to modify a part. It would also be easier to document.

part names vs class names

For us, the part names and our class names have the same semantical meaning and we don't want to repeat the names in two attributes like <my-component class="my-name my-other-name" part="my-name my-other-name"></my-component> so we switched from class names to part names only instead (and use an attribute selector internally). For this case it would have made more sense to just be able to export the class names right away.

Use ShadowRoot mode settings

It would be great if the exportparts were aligned with the mode provided for the shadow root, so that part names were accessible the same way as the shadow dom nodes are accessible in javascript via closed or open.