DEV Community

Serhii Kulykov
Serhii Kulykov

Posted on • Updated on

CSS Shadow Parts are coming!

Firefox 72 has been released with some new APIs, and two of those are good news for the Web Components users. The first one is FormDataEvent and event-based form participation, previously only available in Chromium. But today I would like to focus on another feature, implemented behind a flag since Firefox 69: part HTML attribute and ::part pseudo-element.

CSS Shadow Parts proposal is among the most waited additions to the standard for developers who create customizable components and design systems. It is a new API for styling individual elements in Shadow DOM from outside. This lets to make a single instance of certain UI widget look slightly differently – an ability that we often need in the real world apps.

Unlike CSS Custom Properties, which only allow to configure individual values like --primary-color, a new ::part pseudo-element give us a true flexibility we have been longed for. At Vaadin, we adopted the idea of CSS Shadow Parts already in 2017 as explained in this talk on theming. And now we are happy to see that ::part finally gets cross-browser support.

History

The idea of custom pseudo-elements, also known as CSS Shadow Parts, has been discussed at least since 2015 but the agreement was reached in early 2017 when it was decided to abandon CSS mixins and @apply proposal (never released in Chrome), and to implement ::part instead. Three years after – not too long for web standards – and we are getting there in 2020.

One note for those who remember a fancy explainer by Monica Dinculescu: the spec has changed since then. There is now an updated version by Fergal Daly. Also, today I'm only talking about ::part because ::theme, another pseudo-element from the original proposal, has been postponed. There are ongoing discussions related to ::theme and its future is rather unclear.

But the status of ::part looks very promising. It is supported in Chrome since version 73 and now also in Firefox 72. Safari has already implemented CSS Shadow Parts in Technology Preview 94, and it remains to wait for a stable version to be released – same as for Chromium-based Edge. So it's already time to experiment with ::part and learn how to use it in practice.

Usage

Basic example

According to the comment by Tab Atkins, one of the spec editors, the point of ::part is to hide the internal details of the web component, and to only expose exactly the parts the component author explicitly wants to.

Let's go directly to the example. The embedded CodePen below provides an example of styling <vaadin-details> custom element using ::part. This is a simple component which exposes a few elements using part attribute.

Looks like good old vanilla CSS, doesn't it? Even if you open this demo in a browser that doesn't support ::part, nothing will explode: the component will remain fully functional with the styles that it provides out of the box.

Using CSS Shadow Parts in simple cases like this one it's cheap. In fact, you only need to add part HTML attribute on the elements you wish to expose. This leaves a component consumer to choose whether to use this API or not.

Using ::part with pseudo-elements

As you may noticed, the above example demonstrates how the details toggle button can he styled using the ::part(toggle)::after CSS selector. We can use ::part this way in combination with any other pseudo elements, too.

As demonstrated in the CodePen above, all the browsers that implement ::part already support this feature. However, please don't expect it to work with non-standard pseudo-elements like ::-webkit-scrollbar.

Using ::part with pseudo-classes

Another feature that is often needed for elements inside shadow tree is an ability to style certain states like :hover. We can achieve it with ::part – and that's how it naturally fits into the way of writing CSS we are used to:

While other user-action pseudo-classes like :focus are also allowed to be used with ::part, the spec clearly states that structural pseudo-classes (e. g. :first-child, :nth-of-type(), :empty and so on) are not supported.

Path forwarding with exportparts

In certain cases, the parts we might need to expose for styling are located in a nested component, which has its own shadow root. That isn't possible with ::part by default, as it does not recursively traverse shadow trees.

However, there is now exportparts attribute designed to handle this case. When an element in shadow tree has this attribute, its value defines what parts are "exported" from that element and can be styled with ::part:

As of today, this feature only works as a "whitelist". There was a proposal to also support forwarding with -* to have kind of "wildcard". However, it is not currently standardized and therefore not implemented in browsers.

Feature detection

In some cases you might want to detect whether a browser supports CSS Shadow Parts or not – for example, to load a different chunk or .css file with fallback styles. This can be done in JS using the following code:

'part' in HTMLElement.prototype; // true if "part" is supported

There is no cross-browser way to detect support for ::part using CSS only because @supports selector() API is currently only available in Firefox.

Limitations

While CSS Shadow Parts definitely bring us a lot of flexibility, they still has certain limitations that exist by design. The restrictions related to certain pseudo-classes that are disallowed after ::part belong to them.

Same as ::slotted() pseudo-element that is already familiar to anyone who uses Shadow DOM, ::part is disallowed to use in complex selectors so you can't use it to style a sibling or child node – only the part itself.

Another important limitation: it's not possible to use classes or attribute selectors after ::part – these are considered an implementation detail. One possible workaround for it could be to use multiple parts approach.

The :state() pseudo-class is designed to work with ::part and it should cover some use cases by allowing CSS like ::part(video):state(playing) for styling custom elements that are exposed using part attribute.

Documenting

CSS Shadow Parts can be considered public CSS API exposed by a web component. This means that they should follow semantic versioning – so removing or renaming any part attribute requires a major version bump.

The web-component-analyzer tool supports @csspart JSDoc annotation that can be used to document CSS Shadow Parts exposed by a custom element, and outputs them as part of experimental JSON format.

And with the help of the <api-viewer> element (my pet project), you can generate a web component API docs from JSON, including CSS Shadow Parts. By the way, <api-viewer> itself can be styled with ::part.

CSS Shadow Parts API documentation

Learn more

As long as CSS Shadow Parts are relatively new API, there aren't many examples yet. Here are few places where you can see ::part in action:

  • Elix, a collection of web components that implement common UI patterns by Jan Miksovsky. Elix recommends styling element parts among other approaches for customizing elements appearance.

  • Chromium Web UI elements. These web components are in progress of being updated to using CSS Shadow Parts instead of CSS mixins. You can see them already used in several places like Chrome Settings.

  • Firefox UI. As mentioned in the recent blog post by Brian Grinstead, the process of re-building Firefox UI with Web Components resulted in prioritizing CSS Shadow Parts support among other related features.

  • The web-platform-tests suite. This project is very useful to understand how certain features are supposed to work. You can check the results to see how CSS Shadow Parts work in the latest browser builds.

  • Mozilla Developer Network already (MDN) provides two articles about ::part pseudo-element and part global attribute. They also have a dedicated example in the web-components-examples project.

Finally, if you want to look into questions that are still under discussion, check out the CSSWG issue tracker with the corresponding label.

Summary

CSS Shadow Parts are new API that can be already used in some projects, especially in those cases when graceful degradation approach is acceptable. So I highly recommend evaluating part and exportparts to anyone who works on a web components library, UI kit or design system.

Top comments (4)

Collapse
 
mrgrigri profile image
Michael Richins • Edited

Your link for the @supports selector() is not correct. Here is the documentation on MDN

Collapse
 
webpadawan profile image
Serhii Kulykov

Thanks! Fixed.

Collapse
 
mrgrigri profile image
Michael Richins

Thanks for this article. I've been anticipating this feature for quite some time now and it's awesome to hear that it's being implemented.

Collapse
 
larsdenbakker profile image
Lars den Bakker

Excellent article.