Valid Names for CSS Parts
Tags: webcomponents, css, javascript
Published: 2022-02-20 20:46:00


[CSS Shadow Parts](https://www.w3.org/TR/css-shadow-parts-1/), colloquially known as _CSS Parts_, are used to expose elements inside a web component's shadow root so they can be styled by consumers with CSS. But what are we allowed to call these parts? What characters comprise a valid CSS part name? To find out, I had to [dive into the spec](https://www.w3.org/TR/css-shadow-parts-1/#element-attrdef-html-global-part)!

> The part attribute is parsed as a space-separated list of tokens representing the part names of this element.

OK, but what exactly is a "token?" It's not really clear from this page, but we get a hint if we look at the spec for the [`::part()` pseudo selector](https://developer.mozilla.org/en-US/docs/Web/CSS/::part):

```
::part() = ::part( <ident> )
```

It expects an `<ident>`, whatever that is. Fortunately, there's [a link to it](https://www.w3.org/TR/css-values-4/#typedef-ident) that takes us to a page that defines it.

> CSS identifiers, generically denoted by `<ident>`, consist of a sequence of characters conforming to the `<ident-token>` grammar.

Perfect! So part names must conform to the `<ident-token>` grammar, but what's that mean? Let's follow the link to [`<ident-token>`](https://www.w3.org/TR/css-syntax-3/#typedef-ident-token) in the spec.

From that page:

> `<ident-token>` [...] have a value composed of zero or more code points. Additionally, hash tokens have a type flag set to either "id" or "unrestricted". The type flag defaults to "unrestricted" if not otherwise set.

That's not, uh, super helpful. There's a link around "code points," so let's see if that makes it more clear. The link goes to [this page](https://infra.spec.whatwg.org/#code-point):

> A code point is a Unicode code point and is represented as "U+" followed by four-to-six ASCII upper hex digits, in the range U+0000 to U+10FFFF, inclusive. A code point’s value is its underlying number.

That seems to be a pretty wide range, though. [According to Wikipedia:](https://en.wikipedia.org/wiki/Plane_(Unicode))

> The last code point in Unicode is the last code point in plane 16, U+10FFFF.

So does this mean we can use any Unicode character as a CSS part name? I'm no Unicode expert, so it still isn't clear.

## Experimenting with Various Characters

Let's experiment and see what actually works in the browser. I created a sample custom element with the following parts, then tried styling them with `::part()` to see what actually worked.

```
✅ part="a"
✅ part="a-b"
✅ part="a_b"
❌ part="a,b"
❌ part="a.b"
❌ part="a$b"
❌ part="a+b"
❌ part="a@b"
❌ part="a?b"
```

Clearly we can't use _any_ character for part names. The browser doesn't like symbols, which feels intuitive to be honest.

Let's try some more characters.

```
✅ part="á"
✅ part="ñ"
✅ part="ę"
✅ part="Ф"
✅ part="平"
✅ part="λ"
✅ part="ה"
✅ part="字"
```

It seems to be OK with non-punctuation. What about emojis?!

```
✅ part="🥕"
✅ part="🐛"
✅ part="🐓"
✅ part="💩"
✅ part="🤨"
```

Totally fine. So it seems like any character except punctuation is good. Let's check out that lower Unicode range the spec referred to: U+0000

[Here's a document](https://www.unicode.org/charts/PDF/U0000.pdf) with the lower end character points. It lists a number of the symbols we tried earlier in this range. 🥲

Clearly this range doesn't reflect valid CSS part names. Maybe we overlooked something in the spec, or maybe the CSS spec defines the argument too generically. Surely [the HTML spec](https://html.spec.whatwg.org/#global-attributes) will explain it better.

Whoops...there's no mention of the `part` attribute at all. 😭

Let's see if [this MDN page](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#attr-part) can help us clear things up.

> A space-separated list of the part names of the element. Part names allows CSS to select and style specific elements in a shadow tree via the ::part pseudo-element.

OK, but _what_ is a valid part name?!

## MDN To The Rescue

I'm still not sure what's correct per the spec. Let's take a look at [the `::part()` docs](https://developer.mozilla.org/en-US/docs/Web/CSS/::part) in MDN now. I found this, which looks a lot like what we saw in the spec!

```
::part( `<ident>+` )
```

There's a link around `<ident>` that goes [to this page](https://developer.mozilla.org/en-US/docs/Web/CSS/ident), which says:

![Screenshot from the aforementioned page that shows the definition of <ident>](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3pcer0v4q85hp3g789f1.png)

Thus far, MDN has provided the most human-readable explanation of what a valid ident is, and therefore what a valid CSS part name is. Here's the rundown of valid characters:

- A-Z, a-z
- 0-9
- hyphens
- underscores
- escaped chars
- unicode chars in hex format

This explains why my punctuation examples didn't work. Here's something interesting, though:

> an escaped character (preceded by a backslash, \)

So _can_ we use any character? Let's try one of those parts with punctuation that didn't work again:

```html
<div part="a.b">
```

Now let's target it with CSS:

```css
::part(a\.b) {
  ...
}
```

It works! So the spec is technically correct in that any character can be used, it just wasn't clear in that some of them need to be escaped.

Check this out:

```html
<div part="a.b{c}|d">
```

And with CSS:

```css
::part(a\.b\{c\}\|d) {
  ...
}
```

This works too!

## Using Parts To Target State

Until now, I've primary used a single part for each element I wanted to expose. However, [per the spec](https://drafts.csswg.org/css-shadow-parts/#part), parts should be used more like classes:

> Part names act similarly to classes: multiple elements can have the same part name, and a single element can have multiple part names.

I guess I should change the way I use parts. I used to think of them as an identifier, but thinking of them more like _classes_ as the spec suggests can help fill a gap!

Now that I know underscores are valid, I may explore them as a convention for hover/focus/active states since we never got the `::state` selector. For example, we can give parts a synthetic focus state or even "hoist" a natural focus state to another element in the component.

```html
<div part="control control_focus">
```

With this convention, perhaps we don't need a `::state` selector after all!

## Just Because You Can, Doesn't Mean You Should

So there you have it. You can name CSS parts anything you want — just remember to escape special characters! Using the previous example, we can even remove all of the letters and make a part name with only symbols!

```html
<div part=".{}|">
```

The selector gets a bit crazy, though.

```css
::part(\.\{\}\|) {
  /* 😎 */
}
```

I've tested this in all modern browsers and it appears to work consistently. Of course, this isn't very practical. Out of respect for your users, I would advise against naming your CSS parts like this! 🙈

I'm torn on whether or not special characters are useful here. Perhaps for an internal pattern, but I don't think I'd expose it as part of a public API. Users will definitely forget to escape those characters in their stylesheets. For my parts, I'll probably continue using a-z and dashes most of the time.