CSS Shadow Parts are coming!
Tags: webcomponents, customelements, css
Published: 2020-01-07 18:23:50

---
title: CSS Shadow Parts are coming!
published: true
description: An overview of the CSS Shadow Parts API, now implemented in Firefox as well as Chrome and Safari Technology Preview
tags: webcomponents, customelements, css
---

Firefox 72 has been released with some [new APIs](https://wiki.developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/72), and two of those are good news for the Web Components users. The first one is [`FormDataEvent`](https://wiki.developer.mozilla.org/en-US/docs/Web/API/FormDataEvent) and [event-based form participation](https://web.dev/more-capable-form-controls/#event-based-api), 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](https://drafts.csswg.org/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](https://youtu.be/xSnG-mNWhqI). 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](https://github.com/w3c/webcomponents/issues/300#issuecomment-136716945) but the agreement was reached in early 2017 when it was decided to abandon [CSS mixins and `@apply`](https://www.xanthir.com/b4o00) 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](https://meowni.ca/posts/part-theme-explainer/) by Monica Dinculescu: the spec has changed since then. There is now an [updated version](https://github.com/fergald/docs/blob/master/explainers/css-shadow-parts-1.md) 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](https://www.chromestatus.com/feature/5763933658939392) since version 73 and now also in Firefox 72. Safari has already implemented CSS Shadow Parts in [Technology Preview 94](https://webkit.org/blog/9609/release-notes-for-safari-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](https://github.com/w3c/csswg-drafts/issues/3431#issuecomment-446401804) 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>`](https://github.com/vaadin/vaadin-details) custom element using `::part`. This is a simple component which exposes a few elements using `part` attribute.
{% codepen https://codepen.io/web-padawan/pen/yLyPKJP default-tab=result,css %}
Looks like good old vanilla CSS, doesn't it? Even if you open this demo in a browser that doesn't [support](https://caniuse.com/#feat=mdn-css_selectors_part) `::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.
{% codepen https://codepen.io/web-padawan/pen/PowOjYq default-tab=result,css %}
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:
{% codepen https://codepen.io/web-padawan/pen/RwNjNdJ default-tab=result,css %}
While other user-action pseudo-classes like `:focus` are also allowed to be used with `::part`, the spec clearly states that [structural pseudo-classes](https://drafts.csswg.org/selectors-4/#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`](https://drafts.csswg.org/css-shadow-parts/#exportparts-attr) 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`:
{% codepen https://codepen.io/web-padawan/pen/mdypNmR default-tab=result,css %}
As of today, this feature only works as a "whitelist". There was a proposal to also support [forwarding with `-*`](https://github.com/fergald/docs/blob/master/explainers/css-shadow-parts-1.md#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:

```js
'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()`](https://developer.mozilla.org/en-US/docs/Web/CSS/@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](https://github.com/w3c/csswg-drafts/issues/3431#issuecomment-446401804). One possible workaround for it could be to use [multiple parts](https://github.com/w3c/csswg-drafts/issues/4412) approach.

The [`:state()`](https://github.com/w3c/webcomponents/blob/gh-pages/proposals/custom-states-and-state-pseudo-class.md) 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](https://github.com/mdn/web-components-examples/tree/master/shadow-part) 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>`](https://github.com/web-padawan/api-viewer-element) 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`](https://api-viewer-element.netlify.com/).

![CSS Shadow Parts API documentation](https://thepracticaldev.s3.amazonaws.com/i/gcf9lnmeghmvh4srufd0.png)

## 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](https://github.com/elix/elix/search?q=part), a collection of web components that implement common UI patterns by Jan Miksovsky. Elix recommends [styling element parts](https://component.kitchen/elix/customizing#styling-element-parts) among other approaches for customizing elements appearance.

- [Chromium Web UI elements](https://source.chromium.org/chromium/chromium/src/+/master:ui/webui/resources/cr_elements/). These web components are [in progress](https://bugs.chromium.org/p/chromium/issues/detail?id=973674) of being updated to using CSS Shadow Parts instead of CSS mixins. You can see them already used in [several places](https://source.chromium.org/search?q=::part&ss=chromium%2Fchromium%2Fsrc:chrome%2Fbrowser%2Fresources%2F) like Chrome Settings.

- [Firefox UI](https://searchfox.org/mozilla-central/search?q=%3A%3Apart&path=browser). As mentioned in the recent [blog post](https://briangrinstead.com/blog/firefox-webcomponents/) 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](https://github.com/web-platform-tests/wpt/tree/master/css/css-shadow-parts). This project is very useful to understand how certain features are supposed to work. You can check the [results](https://wpt.fyi/results/css/css-shadow-parts) to see how CSS Shadow Parts work in the latest browser builds.

- Mozilla Developer Network already (MDN) provides two articles about [`::part` pseudo-element](https://developer.mozilla.org/en-US/docs/Web/CSS/::part) and [`part` global attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/part). They also have a dedicated [example](https://github.com/mdn/web-components-examples/tree/master/shadow-part) 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](https://github.com/w3c/csswg-drafts/labels/css-shadow-parts-1) 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.