Skip to content

[css-shadow-parts] ::part(foo)::part(bar) instead of forwarding? #3841

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

Open
emilio opened this issue Apr 18, 2019 · 9 comments
Open

[css-shadow-parts] ::part(foo)::part(bar) instead of forwarding? #3841

emilio opened this issue Apr 18, 2019 · 9 comments

Comments

@emilio
Copy link
Collaborator

emilio commented Apr 18, 2019

I was going through the shadow parts spec, and it seems to me like a better alternative to complex part-forwarding micro-syntaxes (albeit maybe a bit more simplistic?) could be just to allow multiple ::part() selectors.

So you would do ::part(filepicker)::part(button) rather than ::part(filepicker-button) or what not. Whether (or which) parts or a sub-component are exposed could still be controlled the same way exportparts works.

Seems to me the model would be much more consistent. Also, this is much easier (seems to me, at least, while I've been poking at this) implementation-wise, since you don't need to concatenate part names around to figure out if a part matches from a given tree. Stuff would just work.

Has this approach been considered?

// cc @fergald @rniwa @tabatkins

@emilio
Copy link
Collaborator Author

emilio commented Apr 18, 2019

Oh, also cc @lilles

@fergald
Copy link
Contributor

fergald commented Apr 18, 2019

I think the main objection t this is that it would expose the implementation details in the API. E.g. if you expose something as ::part(a)::part(b)::part(c) then you must continue to maintain a custom element b wrapping c forever (or make a breaking API change).

It also prevents a component from hiding its inner parts from styling (unless we add some kind of do-not-expose-parts="...". Preventing users from styling certain things is less of a concern but preventing users from depending on the existence of certain implementation details is a real goal.

In both cases, this is about who has control. I can see that this gives a bit of an escape hatch when the component author hasn't exposed enough but it's a step back toward the deep combinator.

It would be useful to know how you have been using part and what pain-point inspired this suggestion. Part hasn't had much attention since shipping, so knowing more about the practical problems with using it could help get it some attention.

@tabatkins
Copy link
Member

Yes, allowing a component to choose how it exposes a component/sub-component is an intentional and important point here; whether a part is implemented directly by the component or as part of a nested sub-component isn't a detail that the outer page should care about or even be aware of.

@rniwa
Copy link

rniwa commented Apr 19, 2019

Indeed automatically exposing inner parts is problematic because a component may not want to let its users style a component used as an implementation detail.

@emilio
Copy link
Collaborator Author

emilio commented Apr 19, 2019

I think the main objection t this is that it would expose the implementation details in the API. E.g. if you expose something as ::part(a)::part(b)::part(c) then you must continue to maintain a custom element b wrapping c forever (or make a breaking API change).

Assuming you had to explicitly export the nested parts in the first place that doesn't seem like a huge concern to me, fwiw.

It also prevents a component from hiding its inner parts from styling (unless we add some kind of do-not-expose-parts="...". Preventing users from styling certain things is less of a concern but preventing users from depending on the existence of certain implementation details is a real goal.

Yes, to be clear the proposal is not automatically exporting all nested parts or such. I understand that hiding implementation details is a goal worth the effort. This isn't about getting rid of that functionality, but about how to address nested parts using selectors. Sorry if that caused misunderstanding.

Basically, you would have <my-component exportparts="foo bar"> and you would only match ::part on the outer trees if that part is actually exported. Not sure if that is clear enough, I'm happy to make a more formal proposal if it's not clear enough.

It would be useful to know how you have been using part and what pain-point inspired this suggestion. Part hasn't had much attention since shipping, so knowing more about the practical problems with using it could help get it some attention.

I'm mostly been playing with it a bit, and mostly with an implementer hat on, FWIW. Some of the Firefox front-end engineers are interested in shadow parts to style part of the UI, and I thought a bit about how would I go about implementing it.

Implementing parts without part forwarding is quite straight-forward. Implementing forwarding is a bit more of a headache, since you need to track changes to a bunch of different attributes in all your ancestor chain, and makes a single ::part() jump across potentially many nested shadow trees.

The model I'm proposing for nested parts would be quite simpler (this is both with an author hat on and an implementer hat on), and the only thing you miss is the "you can't lie and say that a nested component's part is actually your part".

Yes, allowing a component to choose how it exposes a component/sub-component is an intentional and important point here; whether a part is implemented directly by the component or as part of a nested sub-component isn't a detail that the outer page should care about or even be aware of.

I'm not sure I agree with that. Allowing to rename parts brings quite a bit of implementation complexity unnecessarily, IMO. I think the whole model would be a bit easier to understand if you could just say which nested parts of a component you export, and match them using selectors.

Indeed automatically exposing inner parts is problematic because a component may not want to let its users style a component used as an implementation detail.

Yes, I get that. as I said I'm not talking about automatically exposing inner parts, that's not incompatible with this model. You could still require having exportparts="foo bar" on the shadow host in order to expose it to outer trees. This is exclusively about how to address them with selectors. Sorry for not making that clearer.

@tabatkins
Copy link
Member

Okay, so going with "only the subparts you explicitly choose to export show up, but you have to access them with nested ::part() selectors", that still exposes implementation details of the component in an unfortunate way.

When styling a component with custom properties, you have no way of knowing whether the things consuming the custom properties are going to be in the component itself, or in a sub-component that the component passes the property on along to (possibly with a rename (--foo2: var(--foo1);) to match the custom property that that subcomponent expects.

This is good: the component can implement some bit of functionality itself, or with a nested component; it can swap out components that expect different theming variables; etc. And all of this is invisible to the outermost user of the component, who only needs to care about the custom-property contract that the outer component lists and can keep stable between updates.

We'd like the same property to be true of ::part(); exposing the precise nesting structure (and names of sub-parts, which may be out of the control of the main component author) seems like an unfortunate regression in encapsulation of components.

@emilio
Copy link
Collaborator Author

emilio commented Apr 19, 2019

Okay, so going with "only the subparts you explicitly choose to export show up, but you have to access them with nested ::part() selectors", that still exposes implementation details of the component in an unfortunate way.

It does complicate the model quite a lot, IMO, but I can take your word on it being an important feature I guess...

@bgrins
Copy link
Member

bgrins commented Apr 25, 2019

It would be useful to know how you have been using part and what pain-point inspired this suggestion. Part hasn't had much attention since shipping, so knowing more about the practical problems with using it could help get it some attention.

I'm mostly been playing with it a bit, and mostly with an implementer hat on, FWIW. Some of the Firefox front-end engineers are interested in shadow parts to style part of the UI, and I thought a bit about how would I go about implementing it.

For some more context here, we are planning to start testing this out in the Firefox UI after https://bugzilla.mozilla.org/show_bug.cgi?id=1505489. We have a few elements in mind to start, so should have some usage feedback after that.

moz-v2v-gh pushed a commit to mozilla/gecko-dev that referenced this issue May 1, 2019
Also fairly straight-forward. This may get more complicated when we do part
forwarding, if any.

I've opened w3c/csswg-drafts#3841 in what I think
would be a cleaner model for forwarding.

Differential Revision: https://phabricator.services.mozilla.com/D28063

--HG--
extra : moz-landing-system : lando
@ExE-Boss
Copy link
Contributor

ExE-Boss commented May 2, 2019

I’m against this because it would unnecessarily expose implementation details, which goes against the intent of Shadow DOM.

mykmelez pushed a commit to mykmelez/gecko that referenced this issue May 2, 2019
Also fairly straight-forward. This may get more complicated when we do part
forwarding, if any.

I've opened w3c/csswg-drafts#3841 in what I think
would be a cleaner model for forwarding.

Differential Revision: https://phabricator.services.mozilla.com/D28063
emilio added a commit to emilio/servo that referenced this issue May 6, 2019
Also fairly straight-forward. This may get more complicated when we do part
forwarding, if any.

I've opened w3c/csswg-drafts#3841 in what I think
would be a cleaner model for forwarding.

Differential Revision: https://phabricator.services.mozilla.com/D28063
emilio added a commit to emilio/servo that referenced this issue May 7, 2019
Also fairly straight-forward. This may get more complicated when we do part
forwarding, if any.

I've opened w3c/csswg-drafts#3841 in what I think
would be a cleaner model for forwarding.

Differential Revision: https://phabricator.services.mozilla.com/D28063
gecko-dev-updater pushed a commit to marco-c/gecko-dev-comments-removed that referenced this issue Oct 4, 2019
Also fairly straight-forward. This may get more complicated when we do part
forwarding, if any.

I've opened w3c/csswg-drafts#3841 in what I think
would be a cleaner model for forwarding.

Differential Revision: https://phabricator.services.mozilla.com/D28063

UltraBlame original commit: 5cac03a223e560c730e9e1e58ad4a6f93ebba4da
gecko-dev-updater pushed a commit to marco-c/gecko-dev-wordified that referenced this issue Oct 4, 2019
Also fairly straight-forward. This may get more complicated when we do part
forwarding, if any.

I've opened w3c/csswg-drafts#3841 in what I think
would be a cleaner model for forwarding.

Differential Revision: https://phabricator.services.mozilla.com/D28063

UltraBlame original commit: 5cac03a223e560c730e9e1e58ad4a6f93ebba4da
gecko-dev-updater pushed a commit to marco-c/gecko-dev-wordified-and-comments-removed that referenced this issue Oct 4, 2019
Also fairly straight-forward. This may get more complicated when we do part
forwarding, if any.

I've opened w3c/csswg-drafts#3841 in what I think
would be a cleaner model for forwarding.

Differential Revision: https://phabricator.services.mozilla.com/D28063

UltraBlame original commit: 5cac03a223e560c730e9e1e58ad4a6f93ebba4da
bhearsum pushed a commit to mozilla-releng/staging-firefox that referenced this issue May 1, 2025
Also fairly straight-forward. This may get more complicated when we do part
forwarding, if any.

I've opened w3c/csswg-drafts#3841 in what I think
would be a cleaner model for forwarding.

Differential Revision: https://phabricator.services.mozilla.com/D28063
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

6 participants