Skip to main content

CSS Shadow Parts

CSS Shadow Parts allow you to style specific parts of components that use the Shadow DOM. This is important in WebJET components where the internal DOM is intentionally encapsulated and classic selectors on the outside do not see the internal nodes.

Benefits of Shadow parts

Shadow Parts allow you to expose only those internal parts that the author of the component deems safe to modify. This preserves encapsulation while giving you control over the appearance of specific parts without rewriting the entire implementation.

Using Shadow parts

When using web components with Shadow DOM, it is not possible to target internal parts with the normal selector. For example, wje-button renders its internal native element inside the shadow root.

<wje-button>
#shadow-root
<button class="button-native" part="native"></button>
</wje-button>

This internal <button> is encapsulated, so the following selector will not work:

/* Non-functional selector */
wje-button .button-native {
color: blue;
}

This problem is solved by ::part(). The component exposes an internal node with the part="native" attribute, so you can target it as follows:

wje-button::part(native) {
color: blue;
}

Practical example

Some components expose multiple parts. For example, wje-select uses parts such as native, input, popup or clear, among others.

wje-select::part(input) {
color: #0f172a;
background: #fff;
}

How ::part works

The ::part() pseudo-element only works on parts that are explicitly exposed by the component. This is so that you have control over the design, but at the same time are not tied to the entire internal DOM layout of the component.

Styling via ::part() allows you to change those CSS properties that the internal element supports.

WebJET Elements parts

You can find a list of all exposed parts on the API page of the specific component in the CSS Shadow Parts section.

Limitations

Compatibility with browsers

CSS Shadow Parts work in current versions of modern browsers. If you need to support legacy environments, check for compatibility and consider whether CSS Variables will suffice.

Support for browser-prefixed pseudoelements

Vendor-prefixed

pseudo-elements are not currently supported. Therefore, for example, the ::-webkit-scrollbar pseudoelement in the example below will not work.

/* Not supported */
my-component::part(scroll)::-webkit-scrollbar {
background: green;
}

See the GitHub discussion for more information.

Structural pseudo-classes

Most pseudo-classes are supported using parts, but structural pseudo-classes are not supported. An example of structural pseudo-classes that do not work is given below.

/* Not supported */
my-component::part(container):first-child {
background: green;
}

/* Not supported */
my-component::part(container):last-child {
background: green;
}

Chaining multiple Parts

Pseudo-element ::part() cannot be chained across multiple parts. If you need to stylize a specific part, use the part that exposes the component directly.

/* Not supported */
my-component::part(button)::part(label)